diff --git a/.gitignore b/.gitignore
index 3399b78..807d687 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,7 @@
# Devfiles
.devfiles/bin/**/*
+public/assets/**
+public/.vite
#### -- TEMPLATES BEGIN -- ####
@@ -109,7 +111,6 @@ dist
# vuepress v2.x temp and cache directory
.temp
-.cache
# Docusaurus cache and generated files
.docusaurus
@@ -145,4 +146,3 @@ dist
# Added by cargo
/target
-.env
diff --git a/.idea/QuickJinja_project.xml b/.idea/QuickJinja_project.xml
new file mode 100644
index 0000000..76760d4
--- /dev/null
+++ b/.idea/QuickJinja_project.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml
new file mode 100644
index 0000000..5f28118
--- /dev/null
+++ b/.idea/dictionaries/project.xml
@@ -0,0 +1,8 @@
+
+
+
+ endblock
+ htmx
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..fefa8f5
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Dev.xml b/.idea/runConfigurations/Dev.xml
new file mode 100644
index 0000000..a76dce5
--- /dev/null
+++ b/.idea/runConfigurations/Dev.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Run_with_vite.xml b/.idea/runConfigurations/Run_with_vite.xml
new file mode 100644
index 0000000..e695ac0
--- /dev/null
+++ b/.idea/runConfigurations/Run_with_vite.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/Vite_Dev_Server.xml b/.idea/runConfigurations/Vite_Dev_Server.xml
new file mode 100644
index 0000000..63cf301
--- /dev/null
+++ b/.idea/runConfigurations/Vite_Dev_Server.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations/dev_database_pg.xml b/.idea/runConfigurations/dev_database_pg.xml
new file mode 100644
index 0000000..970e8c8
--- /dev/null
+++ b/.idea/runConfigurations/dev_database_pg.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqlDataSources.xml b/.idea/sqlDataSources.xml
new file mode 100644
index 0000000..d33af23
--- /dev/null
+++ b/.idea/sqlDataSources.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml
new file mode 100644
index 0000000..77a261d
--- /dev/null
+++ b/.idea/sqldialects.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 9d03386..818ddae 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -262,12 +262,6 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-[[package]]
-name = "cfg_aliases"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
-
[[package]]
name = "chrono"
version = "0.4.39"
@@ -283,16 +277,6 @@ dependencies = [
"windows-targets 0.52.6",
]
-[[package]]
-name = "command-group"
-version = "5.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a68fa787550392a9d58f44c21a3022cfb3ea3e2458b7f85d3b399d0ceeccf409"
-dependencies = [
- "nix 0.27.1",
- "winapi",
-]
-
[[package]]
name = "compendium"
version = "0.1.0"
@@ -302,12 +286,11 @@ dependencies = [
"axum-sqlx-tx",
"chrono",
"figment",
- "mime_guess",
"minijinja",
"minijinja-autoreload",
"minijinja-embed",
- "notify",
"serde",
+ "serde_json",
"sqlx",
"thiserror 2.0.11",
"tokio",
@@ -315,7 +298,7 @@ dependencies = [
"tower-livereload",
"tracing",
"tracing-subscriber",
- "vite-rs",
+ "url",
]
[[package]]
@@ -388,16 +371,6 @@ dependencies = [
"typenum",
]
-[[package]]
-name = "ctrlc"
-version = "3.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "697b5419f348fd5ae2478e8018cb016c00a5881c7f46c717de98ffd135a5651c"
-dependencies = [
- "nix 0.29.0",
- "windows-sys 0.59.0",
-]
-
[[package]]
name = "der"
version = "0.7.9"
@@ -811,7 +784,6 @@ dependencies = [
"pin-project-lite",
"smallvec",
"tokio",
- "want",
]
[[package]]
@@ -821,16 +793,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4"
dependencies = [
"bytes",
- "futures-channel",
"futures-util",
"http",
"http-body",
"hyper",
"pin-project-lite",
- "socket2",
"tokio",
"tower-service",
- "tracing",
]
[[package]]
@@ -1031,12 +1000,6 @@ dependencies = [
"libc",
]
-[[package]]
-name = "ipnet"
-version = "2.11.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
-
[[package]]
name = "itoa"
version = "1.0.14"
@@ -1244,29 +1207,6 @@ dependencies = [
"windows-sys 0.52.0",
]
-[[package]]
-name = "nix"
-version = "0.27.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053"
-dependencies = [
- "bitflags 2.8.0",
- "cfg-if",
- "libc",
-]
-
-[[package]]
-name = "nix"
-version = "0.29.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46"
-dependencies = [
- "bitflags 2.8.0",
- "cfg-if",
- "cfg_aliases",
- "libc",
-]
-
[[package]]
name = "notify"
version = "8.0.0"
@@ -1599,43 +1539,6 @@ version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
-[[package]]
-name = "reqwest"
-version = "0.12.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d19c46a6fdd48bc4dab94b6103fccc55d34c67cc0ad04653aad4ea2a07cd7bbb"
-dependencies = [
- "base64",
- "bytes",
- "futures-channel",
- "futures-core",
- "futures-util",
- "http",
- "http-body",
- "http-body-util",
- "hyper",
- "hyper-util",
- "ipnet",
- "js-sys",
- "log",
- "mime",
- "once_cell",
- "percent-encoding",
- "pin-project-lite",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "sync_wrapper",
- "tokio",
- "tower",
- "tower-service",
- "url",
- "wasm-bindgen",
- "wasm-bindgen-futures",
- "web-sys",
- "windows-registry",
-]
-
[[package]]
name = "rsa"
version = "0.9.7"
@@ -2095,9 +1998,6 @@ name = "sync_wrapper"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
-dependencies = [
- "futures-core",
-]
[[package]]
name = "synstructure"
@@ -2413,12 +2313,6 @@ dependencies = [
"tracing-log",
]
-[[package]]
-name = "try-lock"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
-
[[package]]
name = "typenum"
version = "1.18.0"
@@ -2508,44 +2402,6 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-[[package]]
-name = "vite-rs"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bd2e8124bdc188f95a998b59277dcbbb3e539e9ac7ffaadac852f1a70b8f014d"
-dependencies = [
- "vite-rs-dev-server",
- "vite-rs-embed-macro",
-]
-
-[[package]]
-name = "vite-rs-dev-server"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c4971c20397ebb6d5d88e93bf02a90c50346f7b05e3b92bce72b65e11d4ecf0"
-dependencies = [
- "command-group",
- "ctrlc",
- "lazy_static",
- "reqwest",
-]
-
-[[package]]
-name = "vite-rs-embed-macro"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec68e0f28d50c25d2945eb2d393179a0b43defa168d7e8334847a4624434033f"
-dependencies = [
- "mime_guess",
- "proc-macro2",
- "quote",
- "serde",
- "serde_json",
- "syn",
- "vite-rs-dev-server",
- "walkdir",
-]
-
[[package]]
name = "walkdir"
version = "2.5.0"
@@ -2556,15 +2412,6 @@ dependencies = [
"winapi-util",
]
-[[package]]
-name = "want"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
-dependencies = [
- "try-lock",
-]
-
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
@@ -2612,19 +2459,6 @@ dependencies = [
"wasm-bindgen-shared",
]
-[[package]]
-name = "wasm-bindgen-futures"
-version = "0.4.50"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61"
-dependencies = [
- "cfg-if",
- "js-sys",
- "once_cell",
- "wasm-bindgen",
- "web-sys",
-]
-
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.100"
@@ -2657,16 +2491,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "web-sys"
-version = "0.3.77"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
[[package]]
name = "whoami"
version = "1.5.2"
@@ -2717,41 +2541,6 @@ dependencies = [
"windows-targets 0.52.6",
]
-[[package]]
-name = "windows-link"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38"
-
-[[package]]
-name = "windows-registry"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3"
-dependencies = [
- "windows-result",
- "windows-strings",
- "windows-targets 0.53.0",
-]
-
-[[package]]
-name = "windows-result"
-version = "0.3.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c64fd11a4fd95df68efcfee5f44a294fe71b8bc6a91993e2791938abcc712252"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "windows-strings"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
-dependencies = [
- "windows-link",
-]
-
[[package]]
name = "windows-sys"
version = "0.48.0"
@@ -2803,29 +2592,13 @@ dependencies = [
"windows_aarch64_gnullvm 0.52.6",
"windows_aarch64_msvc 0.52.6",
"windows_i686_gnu 0.52.6",
- "windows_i686_gnullvm 0.52.6",
+ "windows_i686_gnullvm",
"windows_i686_msvc 0.52.6",
"windows_x86_64_gnu 0.52.6",
"windows_x86_64_gnullvm 0.52.6",
"windows_x86_64_msvc 0.52.6",
]
-[[package]]
-name = "windows-targets"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b"
-dependencies = [
- "windows_aarch64_gnullvm 0.53.0",
- "windows_aarch64_msvc 0.53.0",
- "windows_i686_gnu 0.53.0",
- "windows_i686_gnullvm 0.53.0",
- "windows_i686_msvc 0.53.0",
- "windows_x86_64_gnu 0.53.0",
- "windows_x86_64_gnullvm 0.53.0",
- "windows_x86_64_msvc 0.53.0",
-]
-
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.48.5"
@@ -2838,12 +2611,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
-[[package]]
-name = "windows_aarch64_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764"
-
[[package]]
name = "windows_aarch64_msvc"
version = "0.48.5"
@@ -2856,12 +2623,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
-[[package]]
-name = "windows_aarch64_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c"
-
[[package]]
name = "windows_i686_gnu"
version = "0.48.5"
@@ -2874,24 +2635,12 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
-[[package]]
-name = "windows_i686_gnu"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3"
-
[[package]]
name = "windows_i686_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
-[[package]]
-name = "windows_i686_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11"
-
[[package]]
name = "windows_i686_msvc"
version = "0.48.5"
@@ -2904,12 +2653,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
-[[package]]
-name = "windows_i686_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d"
-
[[package]]
name = "windows_x86_64_gnu"
version = "0.48.5"
@@ -2922,12 +2665,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
-[[package]]
-name = "windows_x86_64_gnu"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba"
-
[[package]]
name = "windows_x86_64_gnullvm"
version = "0.48.5"
@@ -2940,12 +2677,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
-[[package]]
-name = "windows_x86_64_gnullvm"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57"
-
[[package]]
name = "windows_x86_64_msvc"
version = "0.48.5"
@@ -2958,12 +2689,6 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
-[[package]]
-name = "windows_x86_64_msvc"
-version = "0.53.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
-
[[package]]
name = "winnow"
version = "0.7.3"
diff --git a/Cargo.toml b/Cargo.toml
index 9819775..7014a68 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,10 +1,11 @@
[package]
name = "compendium"
version = "0.1.0"
-edition = "2021"
+edition = "2024"
[features]
-bundled = []
+embed = ["vite"]
+vite = []
[dependencies]
axum = { version = "0.8.1", features = ["macros"] }
@@ -12,11 +13,9 @@ axum-htmx = { version = "0.7.0", features = ["auto-vary", "serde", "guards"] }
axum-sqlx-tx = "0.10.0"
chrono = { version = "0.4.39", features = ["serde"] }
figment = { version = "0.10.19", features = ["env", "toml"] }
-mime_guess = "2.0.5"
minijinja = { version = "2.7.0", features = ["loader"] }
minijinja-embed = "2.7.0"
minijinja-autoreload = "2.9.0"
-notify = "8.0.0"
serde = { version = "1.0.217", features = ["derive"] }
sqlx = { version = "0.8.3", features = [
"chrono",
@@ -31,7 +30,8 @@ tower-http = { version = "0.6.2", features = ["fs", "trace"] }
tower-livereload = "0.9.6"
tracing = "0.1.41"
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
-vite-rs = "0.1.0"
+url = "2.5.4"
+serde_json = "1.0.138"
[build-dependencies]
minijinja-embed = "2.7.0"
diff --git a/build.rs b/build.rs
index 2b43c19..2b6833b 100644
--- a/build.rs
+++ b/build.rs
@@ -1,8 +1,27 @@
fn main() {
- // we only need to bundle the templates with the
- // feature is enabled.
- #[cfg(feature = "bundled")]
+ #[cfg(feature = "vite")]
{
- minijinja_embed::embed_templates!("src/templates");
+ let out_dir = if cfg!(feature = "embed") {
+ std::env::var("OUT_DIR").unwrap()
+ } else {
+ format!("../../{}", std::env::var("CPD_PUBLIC_DIR").unwrap_or(String::from("public")))
+ };
+
+ std::process::Command::new("bun")
+ .args(&["vite", "build", "--outDir", &out_dir])
+ .status()
+ .unwrap();
+
+ println!("cargo::rerun-if-changed=frontend/assets");
+ println!("cargo::rerun-if-changed=frontend/static");
+ println!("cargo::rerun-if-env-changed=CPD_PUBLIC_DIR");
+ println!("cargo::rerun-if-env-changed=CARGO_FEATURE_EMBED");
+ }
+ println!("cargo::rerun-if-env-changed=CARGO_FEATURE_VITE");
+
+ #[cfg(feature = "embed")]
+ {
+ minijinja_embed::embed_templates!("frontend/templates");
+ println!("cargo::rerun-if-changed=frontend/templates");
}
}
diff --git a/frontend/assets/js/index.ts b/frontend/assets/js/index.ts
index 881092f..f4fc1d0 100644
--- a/frontend/assets/js/index.ts
+++ b/frontend/assets/js/index.ts
@@ -1 +1,2 @@
+// add the beginning of your app entry
import "htmx.org";
diff --git a/frontend/static/.gitkeep b/frontend/static/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/frontend/templates/base.html b/frontend/templates/base.html
index 72ee0b1..4220c6f 100644
--- a/frontend/templates/base.html
+++ b/frontend/templates/base.html
@@ -5,10 +5,11 @@
-
-
-
-
+ {% if is_production == false %}
+
+ {% endif %}
+
+
{% block title %}Axum web service!{% endblock %}
diff --git a/package.json b/package.json
index b50eadc..bef9057 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,26 @@
{
- "name": "compendium",
- "module": "index.ts",
- "version": "0.1.0",
- "devDependencies": {
- "@alecodes/tmpl-build-and-load": "^0.1.3",
- "@biomejs/biome": "1.9.4",
- "@types/bun": "^1.2.10",
- "sass-embedded": "^1.86.3",
- "vite": "^6.3.1"
- },
- "peerDependencies": {
- "typescript": "^5.7.3"
- },
- "type": "module",
- "dependencies": {
- "@mini-strap/core": "^0.1.2",
- "@picocss/pico": "^2.1.1",
- "feather-icons": "^4.29.2",
- "htmx.org": "2.0.4"
- }
+ "name": "compendium",
+ "module": "index.ts",
+ "version": "0.1.0",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build"
+ },
+ "devDependencies": {
+ "@alecodes/tmpl-build-and-load": "^0.1.3",
+ "@biomejs/biome": "1.9.4",
+ "@types/bun": "^1.2.10",
+ "sass-embedded": "^1.86.3",
+ "vite": "^6.3.1"
+ },
+ "peerDependencies": {
+ "typescript": "^5.7.3"
+ },
+ "type": "module",
+ "dependencies": {
+ "@mini-strap/core": "^0.1.2",
+ "@picocss/pico": "^2.1.1",
+ "feather-icons": "^4.29.2",
+ "htmx.org": "2.0.4"
+ }
}
diff --git a/public/.gitkeep b/public/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/public/assets/.gitkeep b/public/assets/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/src/config.rs b/src/config.rs
index 0f37fdc..daff0d9 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -84,6 +84,7 @@ impl DBConfig {
pub struct Config {
pub db: DBConfig,
pub addr: SocketAddr,
+ pub public_dir: PathBuf,
}
impl Default for Config {
@@ -91,6 +92,7 @@ impl Default for Config {
Config {
db: DBConfig::default(),
addr: "0.0.0.0:3000".parse().unwrap(),
+ public_dir: PathBuf::from("public"),
}
}
}
diff --git a/src/error.rs b/src/error.rs
index 5bef9fb..a1c55bf 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -22,6 +22,9 @@ pub enum Error {
#[error(transparent)]
Env(#[from] std::env::VarError),
+ #[error(transparent)]
+ JsonParse(#[from] serde_json::Error),
+
#[error(transparent)]
Template(#[from] minijinja::Error),
diff --git a/src/main.rs b/src/main.rs
index 9ff00e5..3cc647e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -17,7 +17,7 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main]
async fn main() -> Result<()> {
let config = Config::new("./config.toml".into())?;
- let assets = Assets::new();
+ let assets = Assets::new(&config)?;
// Logs
tracing_subscriber::registry()
@@ -40,7 +40,7 @@ async fn main() -> Result<()> {
let (tx_state, tx_layer) = Tx::setup(pool);
- let app = router::new()
+ let app = router::new(&config)
.layer(TraceLayer::new_for_http().on_request(()))
.layer(tx_layer)
.layer(AutoVaryLayer)
diff --git a/src/router.rs b/src/router.rs
index 3b3a990..5f0938f 100644
--- a/src/router.rs
+++ b/src/router.rs
@@ -1,8 +1,8 @@
-use axum::http::StatusCode;
+use crate::config::Config;
+use crate::{AppState, ResultTemplate, Tx};
use axum::{
- extract::{Path, State},
- http::{header, HeaderMap, HeaderValue},
- response::Html,
+ extract::State
+ ,
routing::get,
Router,
};
@@ -11,57 +11,67 @@ use chrono::Utc;
use minijinja::context;
use serde::Serialize;
use sqlx::prelude::FromRow;
-use vite_rs::ViteFile;
+use tower_http::services::ServeDir;
-use crate::{AppState, Error, Result, ResultTemplate, Tx};
+pub fn new(config: &Config) -> Router {
+ let router = Router::new()
+ .route("/", get(handler_home))
+ .nest("/public", load_asset_router(config));
-pub fn new() -> Router {
- Router::new()
- .route("/", get(handler_home))
- .route("/assets/{*asset}", get(handle_assets))
+ router
}
-async fn handle_assets(
- State(state): State,
- Path(asset_path): Path,
-) -> Result<(HeaderMap, String)> {
- let full_path = format!("frontend/assets/{}", asset_path);
- let asset: ViteFile = state.assets.get_asset(&full_path).ok_or(Error::HTTP(StatusCode::NOT_FOUND))?;
- let mut headers = HeaderMap::new();
- headers.insert(header::CONTENT_TYPE, HeaderValue::from_str(&asset.content_type).unwrap());
+#[cfg(feature = "embed")]
+fn load_asset_router(config: &Config) -> Router {
+ Router::new()
+ .fallback_service(
+ ServeDir::new(&config.public_dir),
+ )
+ .nest_service(
+ "/assets",
+ ServeDir::new(concat!(env!("OUT_DIR"), "/assets")),
+ )
+}
- Ok((headers, String::from_utf8(asset.bytes.to_vec()).unwrap()))
+#[cfg(not(feature = "embed"))]
+fn load_asset_router(config: &Config) -> Router {
+ Router::new()
+ .fallback_service(
+ ServeDir::new(&config.public_dir),
+ )
+ .nest_service(
+ "/assets",
+ ServeDir::new(&config.public_dir.join("assets")),
+ )
}
#[derive(FromRow, Debug, Serialize)]
struct ExampleRow {
- pub database: String,
- pub version: String,
- pub current_database: String,
- pub current_user: String,
- pub current_timestamp: chrono::DateTime,
+ pub database: String,
+ pub version: String,
+ pub current_database: String,
+ pub current_user: String,
+ pub current_timestamp: chrono::DateTime,
}
async fn handler_home(
- State(state): State,
- HxRequest(hx_request): HxRequest,
- mut tx: Tx,
+ State(state): State,
+ HxRequest(hx_request): HxRequest,
+ mut tx: Tx,
) -> ResultTemplate {
- let template = state.assets.get_template("index.html").ok_or(Error::HTTP(StatusCode::NOT_FOUND))?;
+ let rows = sqlx::query_as::<_, ExampleRow>("select 'Postgres' as database, setting as version, current_database(), current_user, current_timestamp from pg_settings where name = 'server_version'")
+ .fetch_all(&mut tx)
+ .await?;
- let rows = sqlx::query_as::<_, ExampleRow>("select 'Postgres' as database, setting as version, current_database(), current_user, current_timestamp from pg_settings where name = 'server_version'")
- .fetch_all(&mut tx)
- .await?;
+ let content = if hx_request {
+ state
+ .assets
+ .render_template_block("index.html", "htmx", context!(rows => rows))?
+ } else {
+ state
+ .assets
+ .render_template("index.html", context!(rows => rows))?
+ };
- println!("{:?}", rows);
-
- let content = if hx_request {
- template
- .eval_to_state(context!(rows => rows))?
- .render_block("htmx")?
- } else {
- template.render(context!(rows => rows))?
- };
-
- Ok(Html(content))
+ Ok(content)
}
diff --git a/src/static_assets.rs b/src/static_assets.rs
index 48e5718..2deaaa4 100644
--- a/src/static_assets.rs
+++ b/src/static_assets.rs
@@ -1,14 +1,28 @@
-use minijinja::{path_loader, Environment, Template};
-use minijinja_autoreload::AutoReloader;
-use vite_rs::{ViteFile, ViteProcess};
+mod template_functions;
-#[derive(vite_rs::Embed)]
-#[root = "."]
-struct Static;
+use crate::config::Config;
+use crate::static_assets::template_functions::load_functions;
+use crate::{Result, ResultTemplate};
+use axum::response::Html;
+use minijinja::{path_loader, Environment};
+use minijinja_autoreload::AutoReloader;
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+#[derive(Deserialize, Debug)]
+#[serde(rename_all = "camelCase")]
+#[derive(Clone)]
+pub struct ViteAsset {
+ pub file: String,
+ pub src: String,
+ pub is_entry: bool,
+}
+
+pub type ViteManifest = HashMap;
pub struct Assets {
templates: Environment<'static>,
- _guard: Option,
+ manifest: ViteManifest,
_reloader: Option,
}
@@ -16,26 +30,30 @@ impl Clone for Assets {
fn clone(&self) -> Self {
Self {
templates: self.templates.clone(),
- _guard: None,
+ manifest: self.manifest.clone(),
_reloader: None,
}
}
}
impl Assets {
- pub fn new() -> Self {
- #[allow(unused_mut)] let mut templates = Environment::new();
- let mut _guard = None;
+ pub fn new(config: &Config) -> Result {
+ let (manifest, manifest_str) = Self::load_vite_manifest(config)?;
+
+ let mut templates = Environment::new();
+ templates.add_global("vite_manifest", manifest_str);
+
+ load_functions(&mut templates);
+ templates.set_loader(path_loader("frontend/templates"));
let mut _reloader = None;
// Load in dev mode
#[cfg(debug_assertions)]
{
- // templates.set_loader(minijinja::path_loader("frontend/templates"));
- _guard = Static::start_dev_server(true);
_reloader = Some(AutoReloader::new(|notifier| {
- let template_path = "path/to/templates";
+ let template_path = "frontend/templates";
let mut env = Environment::new();
+ load_functions(&mut env);
env.set_loader(path_loader(template_path));
notifier.watch_path(template_path, true);
Ok(env)
@@ -45,31 +63,59 @@ impl Assets {
// Load in prod mode
#[cfg(not(debug_assertions))]
{
- minijinja_embed::load_templates!(&mut env);
+ minijinja_embed::load_templates!(&mut templates);
}
-
- Self {
+ Ok(Self {
templates,
- _guard,
+ manifest,
_reloader,
- }
+ })
}
- pub fn get_asset(&self, path: &str) -> Option {
- let full_path = format!("frontend/assets/{}", path);
- Static::get(full_path.as_str())
+ #[cfg(feature = "embed")]
+ fn load_vite_manifest(_config: &Config) -> Result<(ViteManifest, String)> {
+ let file = include_str!(concat!(env!("OUT_DIR"), "/.vite/manifest.json"));
+ Ok((serde_json::from_str(file)?, file.to_string()))
}
- pub fn get_template(&self, path: &str) -> Option {
- match &self._reloader {
- None => {
- self.templates.get_template(path).ok()
- }
- Some(reloader) => {
- reloader.acquire_env().unwrap().get_template(path).ok()
- }
- }
+ #[cfg(not(feature = "embed"))]
+ fn load_vite_manifest(config: &Config) -> Result<(ViteManifest, String)> {
+ let file = std::fs::read_to_string(config.public_dir.join(".vite/manifest.json"))?;
+ Ok((serde_json::from_str(file.as_str())?, file))
+ }
+
+ pub fn get_vite_asset(&self, asset: &str) -> Option<&ViteAsset> {
+ self.manifest.get(asset)
+ }
+
+ pub fn render_template(&self, path: &str, ctx: S) -> ResultTemplate {
+ let env = match &self._reloader {
+ None => &self.templates,
+ Some(reloader) => &reloader.acquire_env()?,
+ };
+
+ let result = env.get_template(path)?.render(ctx)?;
+
+ Ok(Html(result))
+ }
+
+ pub fn render_template_block(
+ &self,
+ path: &str,
+ block: &str,
+ ctx: S,
+ ) -> ResultTemplate {
+ let env = match &self._reloader {
+ None => &self.templates,
+ Some(reloader) => &reloader.acquire_env()?,
+ };
+
+ let result = env
+ .get_template(path)?
+ .eval_to_state(ctx)?
+ .render_block(block)?;
+
+ Ok(Html(result))
}
}
-
diff --git a/src/static_assets/template_functions.rs b/src/static_assets/template_functions.rs
new file mode 100644
index 0000000..aabe5fd
--- /dev/null
+++ b/src/static_assets/template_functions.rs
@@ -0,0 +1,36 @@
+use crate::static_assets::ViteManifest;
+use minijinja::{Environment, Error, ErrorKind, State};
+use serde_json::from_str;
+
+
+fn load_asset(state: &State, asset: String) -> Result {
+ let manifest: ViteManifest = from_str(
+ state.lookup("vite_manifest")
+ .ok_or(ErrorKind::EvalBlock)?
+ .as_str()
+ .ok_or(ErrorKind::EvalBlock)?
+ ).map_err(|_| ErrorKind::EvalBlock)?;
+
+ let local_asset = manifest.get(asset.as_str());
+
+ Ok(match local_asset {
+ None => asset,
+ Some(asset) => {
+ format!("/public/{}", asset.file.clone())
+ }
+ })
+}
+
+pub(super) fn load_functions(env: &mut Environment) {
+ env.add_function("asset", load_asset);
+
+ #[cfg(debug_assertions)]
+ {
+ env.add_global("is_production", false);
+ }
+
+ #[cfg(not(debug_assertions))]
+ {
+ env.add_global("is_production", true);
+ }
+}
diff --git a/vite.config.ts b/vite.config.ts
index 6c6a600..95bb5f5 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,8 +1,20 @@
import {defineConfig} from "vite";
export default defineConfig({
+ root: "frontend/assets",
+ publicDir: "frontend/static",
+ base: "/public/assets",
plugins: [],
+ server: {
+ port: 3001,
+ origin: 'http://localhost:3000',
+ cors: true,
+ },
build: {
+ outDir: "../../public", // outDir is relative to the root config
+ assetsDir: "assets",
+ emptyOutDir: true,
+ manifest: true,
rollupOptions: {
input: [
"frontend/assets/js/index.ts",