diff --git a/.gitignore b/.gitignore index ea8c4bf..fabfb87 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ /target +/config.toml diff --git a/2026-01-12_16:52.patch b/2026-01-12_16:52.patch deleted file mode 100644 index d4e4bee..0000000 --- a/2026-01-12_16:52.patch +++ /dev/null @@ -1,67 +0,0 @@ -diff --git a/src/web/mod.rs b/src/web/mod.rs -index f919e6e..4f03aa9 100644 ---- a/src/web/mod.rs -+++ b/src/web/mod.rs -@@ -11,6 +11,7 @@ pub async fn start() -> anyhow::Result<()> { - let addr = "0.0.0.0:3000"; - let app = Router::new() - .route("/", get(pages::home::get_page)) -+ .route("/login", get(pages::login::get_page)) - .nest_service( - "/static", - ServiceBuilder::new() -diff --git a/src/web/pages/mod.rs b/src/web/pages/mod.rs -index 0c1f8ad..d4fc7a0 100644 ---- a/src/web/pages/mod.rs -+++ b/src/web/pages/mod.rs -@@ -1,6 +1,8 @@ - - -+ - pub mod home; -+pub mod login; - pub mod error; - - -diff --git a/templates/base.html b/templates/base.html -index 6731e87..86574ca 100644 ---- a/templates/base.html -+++ b/templates/base.html -@@ -3,6 +3,7 @@ - - - {{ self.title() }} -+ - - - {% include "header.html" %} -diff --git a/templates/header.html b/templates/header.html -index c247db4..cf4fb71 100644 ---- a/templates/header.html -+++ b/templates/header.html -@@ -1,6 +1,25 @@ -
- - -+ - - -
diff --git a/2026-01-12_16:55.patch b/2026-01-12_16:55.patch deleted file mode 100644 index d4e4bee..0000000 --- a/2026-01-12_16:55.patch +++ /dev/null @@ -1,67 +0,0 @@ -diff --git a/src/web/mod.rs b/src/web/mod.rs -index f919e6e..4f03aa9 100644 ---- a/src/web/mod.rs -+++ b/src/web/mod.rs -@@ -11,6 +11,7 @@ pub async fn start() -> anyhow::Result<()> { - let addr = "0.0.0.0:3000"; - let app = Router::new() - .route("/", get(pages::home::get_page)) -+ .route("/login", get(pages::login::get_page)) - .nest_service( - "/static", - ServiceBuilder::new() -diff --git a/src/web/pages/mod.rs b/src/web/pages/mod.rs -index 0c1f8ad..d4fc7a0 100644 ---- a/src/web/pages/mod.rs -+++ b/src/web/pages/mod.rs -@@ -1,6 +1,8 @@ - - -+ - pub mod home; -+pub mod login; - pub mod error; - - -diff --git a/templates/base.html b/templates/base.html -index 6731e87..86574ca 100644 ---- a/templates/base.html -+++ b/templates/base.html -@@ -3,6 +3,7 @@ - - - {{ self.title() }} -+ - - - {% include "header.html" %} -diff --git a/templates/header.html b/templates/header.html -index c247db4..cf4fb71 100644 ---- a/templates/header.html -+++ b/templates/header.html -@@ -1,6 +1,25 @@ -
- - -+ - - -
diff --git a/Cargo.lock b/Cargo.lock index 2aa9463..901c20a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -300,6 +300,12 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +[[package]] +name = "camino" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" + [[package]] name = "cc" version = "1.2.52" @@ -325,6 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", + "clap_derive", ] [[package]] @@ -339,6 +346,18 @@ dependencies = [ "strsim", ] +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_lex" version = "0.7.6" @@ -399,12 +418,107 @@ dependencies = [ "typenum", ] +[[package]] +name = "darling" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +dependencies = [ + "darling_core", + "quote", + "syn", +] + [[package]] name = "data-encoding" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diesel" +version = "2.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e130c806dccc85428c564f2dc5a96e05b6615a27c9a28776bd7761a9af4bb552" +dependencies = [ + "bitflags", + "byteorder", + "diesel_derives", + "downcast-rs", + "ipnet", + "itoa", + "libc", + "pq-sys", + "time", + "uuid", +] + +[[package]] +name = "diesel_derives" +version = "2.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c30b2969f923fa1f73744b92bb7df60b858df8832742d9a3aceb79236c0be1d2" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "745fd255645f0f1135f9ec55c7b00e0882192af9683ab4731e4bba3da82b8f9c" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" +dependencies = [ + "syn", +] + [[package]] name = "digest" version = "0.10.7" @@ -415,6 +529,43 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + +[[package]] +name = "dsl_auto_type" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd122633e4bef06db27737f21d3738fb89c8f6d5360d6d9d7635dda142a7757e" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "env_filter" version = "0.1.4" @@ -461,23 +612,18 @@ dependencies = [ ] [[package]] -name = "form_urlencoded" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" -dependencies = [ - "percent-encoding", -] - -[[package]] -name = "fuck_microsoft_access" +name = "fma" version = "0.1.0" dependencies = [ "anyhow", "askama", "axum", + "camino", "clap", + "diesel", + "diesel_migrations", "env_logger", + "ipnet", "log", "serde", "serde_json", @@ -485,6 +631,22 @@ dependencies = [ "toml", "tower", "tower-http", + "url", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", ] [[package]] @@ -566,6 +728,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "http" version = "1.4.0" @@ -654,6 +822,114 @@ dependencies = [ "tower-service", ] +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + [[package]] name = "indexmap" version = "2.13.0" @@ -664,6 +940,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + [[package]] name = "iri-string" version = "0.7.10" @@ -736,6 +1018,12 @@ version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + [[package]] name = "log" version = "0.4.29" @@ -754,6 +1042,27 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "migrations_internals" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c791ecdf977c99f45f23280405d7723727470f6689a5e6dbf513ac547ae10d" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36fc5ac76be324cfd2d3f2cf0fdf5d5d3c4f14ed8aaebadb09e304ba42282703" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + [[package]] name = "mime" version = "0.3.17" @@ -791,6 +1100,12 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + [[package]] name = "num-traits" version = "0.2.19" @@ -851,6 +1166,21 @@ dependencies = [ "portable-atomic", ] +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + [[package]] name = "ppv-lite86" version = "0.2.21" @@ -860,6 +1190,17 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "pq-sys" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "574ddd6a267294433f140b02a726b0640c43cf7c6f717084684aaa3b285aba61" +dependencies = [ + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "proc-macro2" version = "1.0.105" @@ -1080,6 +1421,12 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + [[package]] name = "strsim" version = "0.11.1" @@ -1103,6 +1450,17 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "thiserror" version = "2.0.17" @@ -1123,6 +1481,46 @@ dependencies = [ "syn", ] +[[package]] +name = "time" +version = "0.3.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9e442fc33d7fdb45aa9bfeb312c095964abdf596f7567261062b2a7107aaabd" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b36ee98fd31ec7426d599183e8fe26932a8dc1fb76ddb6214d05493377d34ca" + +[[package]] +name = "time-macros" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e552d1249bf61ac2a52db88179fd0673def1e1ad8243a00d9ec9ed71fee3dd" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + [[package]] name = "tokio" version = "1.49.0" @@ -1331,12 +1729,31 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + [[package]] name = "utf-8" version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.2" @@ -1354,6 +1771,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -1524,6 +1947,35 @@ version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.8.33" @@ -1544,6 +1996,60 @@ dependencies = [ "syn", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "zmij" version = "1.0.13" diff --git a/Cargo.toml b/Cargo.toml index 02e16e0..aa636da 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "fuck_microsoft_access" +name = "fma" version = "0.1.0" edition = "2024" @@ -7,8 +7,12 @@ edition = "2024" anyhow = "1.0.100" askama = "0.15.1" axum = { version = "0.8.8", features = ["macros", "ws"] } -clap = "4.5.54" +camino = "1.2.2" +clap = { version = "4.5.54", features = ["derive"] } +diesel = { version = "2.3.5", features = ["uuid", "time", "postgres", "ipnet-address"] } +diesel_migrations = { version = "2.3.1", features = ["postgres"] } env_logger = "0.11.8" +ipnet = "2.11.0" log = "0.4.29" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.149" @@ -16,3 +20,4 @@ tokio = { version = "1.49.0", features = ["io-util", "macros", "net", "rt-multi- toml = "0.9.11" tower = { version = "0.5.3", features = ["full"] } tower-http = { version = "0.6.8", features = ["full"] } +url = { version = "2.5.8", features = ["serde"] } diff --git a/diesel.toml b/diesel.toml new file mode 100644 index 0000000..bb1d1f7 --- /dev/null +++ b/diesel.toml @@ -0,0 +1,9 @@ +# For documentation on how to configure this file, +# see https://diesel.rs/guides/configuring-diesel-cli + +[print_schema] +file = "src/db/schema.rs" +custom_type_derives = ["diesel::query_builder::QueryId", "Clone"] + +[migrations_directory] +dir = "migrations" diff --git a/migrations/.diesel_lock b/migrations/.diesel_lock new file mode 100644 index 0000000..e69de29 diff --git a/migrations/00000000000000_diesel_initial_setup/down.sql b/migrations/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 0000000..a9f5260 --- /dev/null +++ b/migrations/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,6 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/migrations/00000000000000_diesel_initial_setup/up.sql b/migrations/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 0000000..d68895b --- /dev/null +++ b/migrations/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/migrations/2026-01-13-095817-0000_users/down.sql b/migrations/2026-01-13-095817-0000_users/down.sql new file mode 100644 index 0000000..c99ddcd --- /dev/null +++ b/migrations/2026-01-13-095817-0000_users/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS users; diff --git a/migrations/2026-01-13-095817-0000_users/up.sql b/migrations/2026-01-13-095817-0000_users/up.sql new file mode 100644 index 0000000..3cb8c4a --- /dev/null +++ b/migrations/2026-01-13-095817-0000_users/up.sql @@ -0,0 +1,18 @@ +CREATE TABLE IF NOT EXISTS users ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + username TEXT NOT NULL UNIQUE, + email TEXT NOT NULL UNIQUE, + password_hash TEXT NOT NULL, + password_salt TEXT NOT NULL, + first_name TEXT NOT NULL, + last_name TEXT NOT NULL, + display_name TEXT, + date_of_birth DATE, + phone_number TEXT, + + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + last_login_at TIMESTAMPTZ, + + -- u128 bitfield for permissions + permissions NUMERIC(39,0) NOT NULL DEFAULT 0 +) diff --git a/migrations/2026-01-13-095838-0000_clients/down.sql b/migrations/2026-01-13-095838-0000_clients/down.sql new file mode 100644 index 0000000..1100f5d --- /dev/null +++ b/migrations/2026-01-13-095838-0000_clients/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS clients; diff --git a/migrations/2026-01-13-095838-0000_clients/up.sql b/migrations/2026-01-13-095838-0000_clients/up.sql new file mode 100644 index 0000000..b2ed366 --- /dev/null +++ b/migrations/2026-01-13-095838-0000_clients/up.sql @@ -0,0 +1,21 @@ +CREATE TABLE IF NOT EXISTS clients ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + email TEXT NOT NULL UNIQUE, + + first_name TEXT NOT NULL, + last_name TEXT NOT NULL, + + date_of_birth DATE, + phone_number TEXT, + gov_id_number TEXT, + house_number TEXT, + address_line TEXT, + city TEXT, + state TEXT, + postal_code TEXT, + country TEXT, + + worker_user_id BIGINT, + + CONSTRAINT fk_worker_user_id FOREIGN KEY (worker_user_id) REFERENCES users(id) ON DELETE CASCADE +) diff --git a/migrations/2026-01-13-095842-0000_warehouses/down.sql b/migrations/2026-01-13-095842-0000_warehouses/down.sql new file mode 100644 index 0000000..548e385 --- /dev/null +++ b/migrations/2026-01-13-095842-0000_warehouses/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS warehouses; diff --git a/migrations/2026-01-13-095842-0000_warehouses/up.sql b/migrations/2026-01-13-095842-0000_warehouses/up.sql new file mode 100644 index 0000000..19c8692 --- /dev/null +++ b/migrations/2026-01-13-095842-0000_warehouses/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS warehouses ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name TEXT NOT NULL, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +) diff --git a/migrations/2026-01-13-095843-0000_inventory_catalog/down.sql b/migrations/2026-01-13-095843-0000_inventory_catalog/down.sql new file mode 100644 index 0000000..8f5ac6c --- /dev/null +++ b/migrations/2026-01-13-095843-0000_inventory_catalog/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS inventory_catalog; diff --git a/migrations/2026-01-13-095843-0000_inventory_catalog/up.sql b/migrations/2026-01-13-095843-0000_inventory_catalog/up.sql new file mode 100644 index 0000000..4c77ca4 --- /dev/null +++ b/migrations/2026-01-13-095843-0000_inventory_catalog/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE IF NOT EXISTS inventory_catalog ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name TEXT NOT NULL, + description TEXT, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() +) diff --git a/migrations/2026-01-13-095844-0000_inventory/down.sql b/migrations/2026-01-13-095844-0000_inventory/down.sql new file mode 100644 index 0000000..c81108c --- /dev/null +++ b/migrations/2026-01-13-095844-0000_inventory/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS inventory; diff --git a/migrations/2026-01-13-095844-0000_inventory/up.sql b/migrations/2026-01-13-095844-0000_inventory/up.sql new file mode 100644 index 0000000..2ec7512 --- /dev/null +++ b/migrations/2026-01-13-095844-0000_inventory/up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS inventory ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + warehouse_id BIGINT NOT NULL, + catalog_id BIGINT NOT NULL, + count BIGINT NOT NULL DEFAULT 0, + + UNIQUE (warehouse_id, catalog_id), + + CONSTRAINT fk_warehouse_id FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE, + CONSTRAINT fk_catalog_id FOREIGN KEY (catalog_id) REFERENCES inventory_catalog(id) ON DELETE CASCADE +) diff --git a/migrations/2026-01-13-095855-0000_tickets/down.sql b/migrations/2026-01-13-095855-0000_tickets/down.sql new file mode 100644 index 0000000..7d4d917 --- /dev/null +++ b/migrations/2026-01-13-095855-0000_tickets/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS tickets; diff --git a/migrations/2026-01-13-095855-0000_tickets/up.sql b/migrations/2026-01-13-095855-0000_tickets/up.sql new file mode 100644 index 0000000..c5053d7 --- /dev/null +++ b/migrations/2026-01-13-095855-0000_tickets/up.sql @@ -0,0 +1,3 @@ +CREATE TABLE IF NOT EXISTS tickets ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY +) diff --git a/migrations/2026-01-13-101555-0000_invoices/down.sql b/migrations/2026-01-13-101555-0000_invoices/down.sql new file mode 100644 index 0000000..b30b8a4 --- /dev/null +++ b/migrations/2026-01-13-101555-0000_invoices/down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS invoices; + diff --git a/migrations/2026-01-13-101555-0000_invoices/up.sql b/migrations/2026-01-13-101555-0000_invoices/up.sql new file mode 100644 index 0000000..2bd97d9 --- /dev/null +++ b/migrations/2026-01-13-101555-0000_invoices/up.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS invoices ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + client_id BIGINT NOT NULL, + amount REAL NOT NULL, -- f32 + + CONSTRAINT fk_client_id FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE +) diff --git a/migrations/2026-01-13-101601-0000_services/down.sql b/migrations/2026-01-13-101601-0000_services/down.sql new file mode 100644 index 0000000..49ef0d0 --- /dev/null +++ b/migrations/2026-01-13-101601-0000_services/down.sql @@ -0,0 +1,2 @@ +DROP TABLE IF EXISTS services; + diff --git a/migrations/2026-01-13-101601-0000_services/up.sql b/migrations/2026-01-13-101601-0000_services/up.sql new file mode 100644 index 0000000..af48d55 --- /dev/null +++ b/migrations/2026-01-13-101601-0000_services/up.sql @@ -0,0 +1,7 @@ +CREATE TABLE IF NOT EXISTS services ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + name TEXT NOT NULL, + client_id BIGINT NOT NULL, + + CONSTRAINT fk_client_id FOREIGN KEY (client_id) REFERENCES clients(id) ON DELETE CASCADE +) diff --git a/migrations/2026-01-13-103156-0000_assigned_warehouse_managers/down.sql b/migrations/2026-01-13-103156-0000_assigned_warehouse_managers/down.sql new file mode 100644 index 0000000..10c044e --- /dev/null +++ b/migrations/2026-01-13-103156-0000_assigned_warehouse_managers/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS assigned_warehouse_managers; diff --git a/migrations/2026-01-13-103156-0000_assigned_warehouse_managers/up.sql b/migrations/2026-01-13-103156-0000_assigned_warehouse_managers/up.sql new file mode 100644 index 0000000..a572fe4 --- /dev/null +++ b/migrations/2026-01-13-103156-0000_assigned_warehouse_managers/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE IF NOT EXISTS assigned_warehouse_managers ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + user_id BIGINT NOT NULL, + warehouse_id BIGINT NOT NULL, + assigned_at TIMESTAMPTZ NOT NULL DEFAULT now(), + + CONSTRAINT fk_warehouse_id FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE, + CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) diff --git a/migrations/2026-01-13-103443-0000_warehouse_actions/down.sql b/migrations/2026-01-13-103443-0000_warehouse_actions/down.sql new file mode 100644 index 0000000..e8cbd41 --- /dev/null +++ b/migrations/2026-01-13-103443-0000_warehouse_actions/down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS warehouse_actions; diff --git a/migrations/2026-01-13-103443-0000_warehouse_actions/up.sql b/migrations/2026-01-13-103443-0000_warehouse_actions/up.sql new file mode 100644 index 0000000..a3bc127 --- /dev/null +++ b/migrations/2026-01-13-103443-0000_warehouse_actions/up.sql @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS warehouse_actions ( + id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY, + user_id BIGINT NOT NULL, + warehouse_id BIGINT NOT NULL, + count BIGINT NOT NULL, + reason TEXT NOT NULL, + timestamp TIMESTAMPTZ NOT NULL DEFAULT now(), + + CONSTRAINT fk_warehouse_id FOREIGN KEY (warehouse_id) REFERENCES warehouses(id) ON DELETE CASCADE, + CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE +) diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..e35a857 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,89 @@ +use anyhow::bail; +use clap::Parser; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Clone, clap::Parser)] +pub struct CliArgs { + + /// Path to config file + #[arg(long="config", short='C', default_value="./config.toml")] + config_path: camino::Utf8PathBuf, + + #[arg(long="host", short='H')] + host: Option, + #[arg(long="port", short='p')] + port: Option, + #[arg(long="database", short='D')] + database: Option, +} + +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +pub struct Config { + pub web: ConfigWeb, + pub database: ConfigDb +} + +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +pub struct ConfigWeb { + pub host: String, + pub port: u16, +} + +#[derive(Debug, Clone, Default, Deserialize, Serialize)] +pub struct ConfigDb { + host: String, + port: u16, + username: String, + password: String, + database: String, +} + + +impl Config { + pub fn parse() -> anyhow::Result { + let cli = CliArgs::parse(); + let mut cfg = Self::default(); + let mut is_new = true; + if cli.config_path.exists() { + log::info!("Config file exists, reading"); + let cfg_s = std::fs::read_to_string(&cli.config_path)?; + cfg = toml::from_str(&cfg_s)?; + is_new = false; + } + cfg.web.host = cli.host.unwrap_or("0.0.0.0".to_string()); + cfg.web.port = cli.port.unwrap_or(3000); + if let Some(db) = cli.database { + cfg.database.host = db.host_str().unwrap_or("0.0.0.0").to_string(); + cfg.database.port = db.port().unwrap_or(5432); + cfg.database.username = db.username().to_string(); + cfg.database.password = db.password().unwrap_or("").to_string(); + cfg.database.database = { + match db.path_segments() { + Some(mut seg) => seg.next().unwrap_or("").to_string(), + None => String::new() + } + } + } + + if is_new { + log::info!("Config file doesnt exist, creating new"); + let content = toml::to_string_pretty(&cfg)?; + std::fs::write(&cli.config_path, content)?; + } + Ok(cfg) + } + + pub fn database_url(&self) -> anyhow::Result { + let mut u = url::Url::parse("postgres://0.0.0.0/")?; + u.set_host(Some(&self.database.host))?; + let _ = u.set_port(Some(self.database.port)); + let _ = u.set_username(&self.database.username); + if self.database.password.is_empty() { + let _ = u.set_password(None); + } else { + let _ = u.set_password(Some(&self.database.password)); + } + u.set_path(&self.database.database); + Ok(u) + } +} diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..a6a27b2 --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,19 @@ +use diesel::{Connection, PgConnection}; +use diesel_migrations::{EmbeddedMigrations, MigrationHarness, embed_migrations}; + +pub mod schema; + +pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!("./migrations"); + +fn run_migrations(conn: &mut impl MigrationHarness) { + conn.run_pending_migrations(MIGRATIONS).unwrap(); + log::info!("Running migrations"); +} + + +pub fn start(cfg: &crate::config::Config) -> anyhow::Result { + let mut connection = PgConnection::establish(&cfg.database_url()?.to_string())?; + run_migrations(&mut connection); + Ok(connection) +} + diff --git a/src/db/schema.rs b/src/db/schema.rs new file mode 100644 index 0000000..fc0768c --- /dev/null +++ b/src/db/schema.rs @@ -0,0 +1,129 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + assigned_warehouse_managers (id) { + id -> Int8, + user_id -> Int8, + warehouse_id -> Int8, + assigned_at -> Timestamptz, + } +} + +diesel::table! { + clients (id) { + id -> Int8, + email -> Text, + first_name -> Text, + last_name -> Text, + date_of_birth -> Nullable, + phone_number -> Nullable, + gov_id_number -> Nullable, + house_number -> Nullable, + address_line -> Nullable, + city -> Nullable, + state -> Nullable, + postal_code -> Nullable, + country -> Nullable, + worker_user_id -> Nullable, + } +} + +diesel::table! { + inventory (id) { + id -> Int8, + warehouse_id -> Int8, + catalog_id -> Int8, + count -> Int8, + } +} + +diesel::table! { + inventory_catalog (id) { + id -> Int8, + name -> Text, + description -> Nullable, + created_at -> Timestamptz, + } +} + +diesel::table! { + invoices (id) { + id -> Int8, + client_id -> Int8, + amount -> Float4, + } +} + +diesel::table! { + services (id) { + id -> Int8, + name -> Text, + client_id -> Int8, + } +} + +diesel::table! { + tickets (id) { + id -> Int8, + } +} + +diesel::table! { + users (id) { + id -> Int8, + username -> Text, + email -> Text, + password_hash -> Text, + password_salt -> Text, + first_name -> Text, + last_name -> Text, + display_name -> Nullable, + date_of_birth -> Nullable, + phone_number -> Nullable, + created_at -> Timestamptz, + last_login_at -> Nullable, + permissions -> Numeric, + } +} + +diesel::table! { + warehouse_actions (id) { + id -> Int8, + user_id -> Int8, + warehouse_id -> Int8, + count -> Int8, + reason -> Text, + timestamp -> Timestamptz, + } +} + +diesel::table! { + warehouses (id) { + id -> Int8, + name -> Text, + created_at -> Timestamptz, + } +} + +diesel::joinable!(assigned_warehouse_managers -> users (user_id)); +diesel::joinable!(assigned_warehouse_managers -> warehouses (warehouse_id)); +diesel::joinable!(clients -> users (worker_user_id)); +diesel::joinable!(inventory -> inventory_catalog (catalog_id)); +diesel::joinable!(inventory -> warehouses (warehouse_id)); +diesel::joinable!(invoices -> clients (client_id)); +diesel::joinable!(services -> clients (client_id)); +diesel::joinable!(warehouse_actions -> users (user_id)); +diesel::joinable!(warehouse_actions -> warehouses (warehouse_id)); + +diesel::allow_tables_to_appear_in_same_query!( + assigned_warehouse_managers, + clients, + inventory, + inventory_catalog, + invoices, + services, + tickets, + users, + warehouse_actions, + warehouses, +); diff --git a/src/main.rs b/src/main.rs index 152dd05..6f840c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,24 @@ use log::LevelFilter; mod web; - +mod db; +mod config; #[tokio::main] async fn main() -> anyhow::Result<()> { env_logger::builder() - .filter_module("fuck_microsoft_access", LevelFilter::Debug) + .filter_module("fma", LevelFilter::Debug) + .filter_module("diesel_migrations", LevelFilter::Debug) .init(); + let cfg = config::Config::parse()?; + + unsafe { + // SAFETY: This runs while there are no other threads running, which is required for this + // function to be safe + std::env::set_var("DATABASE_URL", cfg.database_url()?.to_string()); + } - web::start().await?; + let db = db::start(&cfg)?; + web::start(&cfg).await?; Ok(()) } diff --git a/src/schema.rs b/src/schema.rs new file mode 100644 index 0000000..6077008 --- /dev/null +++ b/src/schema.rs @@ -0,0 +1,132 @@ +// @generated automatically by Diesel CLI. + +diesel::table! { + assigned_warehouse_managers (id) { + id -> Int8, + user_id -> Int8, + warehouse_id -> Int8, + assigned_at -> Timestamptz, + } +} + +diesel::table! { + clients (id) { + id -> Int8, + email -> Text, + first_name -> Text, + last_name -> Text, + date_of_birth -> Nullable, + phone_number -> Nullable, + gov_id_number -> Nullable, + house_number -> Nullable, + address_line -> Nullable, + city -> Nullable, + state -> Nullable, + postal_code -> Nullable, + country -> Nullable, + worker_user_id -> Nullable, + } +} + +diesel::table! { + inventory (id) { + id -> Int8, + warehouse_id -> Int8, + catalog_id -> Int8, + count -> Int8, + } +} + +diesel::table! { + inventory_catalog (id) { + id -> Int4, + title -> Varchar, + body -> Text, + published -> Bool, + } +} + +diesel::table! { + invoices (id) { + id -> Int8, + client_id -> Int8, + amount -> Float4, + } +} + +diesel::table! { + services (id) { + id -> Int8, + name -> Text, + client_id -> Int8, + } +} + +diesel::table! { + tickets (id) { + id -> Int4, + title -> Varchar, + body -> Text, + published -> Bool, + } +} + +diesel::table! { + users (id) { + id -> Int8, + username -> Text, + email -> Text, + password_hash -> Text, + password_salt -> Text, + first_name -> Text, + last_name -> Text, + display_name -> Nullable, + date_of_birth -> Nullable, + phone_number -> Nullable, + created_at -> Timestamptz, + last_login_at -> Nullable, + permissions -> Numeric, + } +} + +diesel::table! { + warehouse_actions (id) { + id -> Int8, + user_id -> Int8, + warehouse_id -> Int8, + count -> Int8, + reason -> Text, + timestamp -> Timestamptz, + } +} + +diesel::table! { + warehouses (id) { + id -> Int8, + name -> Text, + created_at -> Timestamptz, + } +} + +diesel::joinable!(assigned_warehouse_managers -> users (user_id)); +diesel::joinable!(assigned_warehouse_managers -> warehouses (warehouse_id)); +diesel::joinable!(clients -> users (worker_user_id)); +diesel::joinable!(inventory -> inventory_catalog (catalog_id)); +diesel::joinable!(inventory -> warehouses (warehouse_id)); +diesel::joinable!(invoices -> clients (client_id)); +diesel::joinable!(services -> clients (client_id)); +diesel::joinable!(warehouse_actions -> users (user_id)); +diesel::joinable!(warehouse_actions -> warehouses (warehouse_id)); + +diesel::allow_tables_to_appear_in_same_query!( + assigned_warehouse_managers, + clients, + inventory, + inventory_catalog, + invoices, + services, + tickets, + users, + warehouse_actions, + warehouses, +); diff --git a/src/web/mod.rs b/src/web/mod.rs index 4f03aa9..ba6e387 100644 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -7,8 +7,8 @@ pub mod pages; -pub async fn start() -> anyhow::Result<()> { - let addr = "0.0.0.0:3000"; +pub async fn start(cfg: &crate::config::Config) -> anyhow::Result<()> { + let addr = format!("{}:{}", cfg.web.host, cfg.web.port); let app = Router::new() .route("/", get(pages::home::get_page)) .route("/login", get(pages::login::get_page)) @@ -18,7 +18,7 @@ pub async fn start() -> anyhow::Result<()> { .service(ServeDir::new("static")) ); - let listener = tokio::net::TcpListener::bind(addr).await.unwrap(); + let listener = tokio::net::TcpListener::bind(&addr).await.unwrap(); log::info!("Listening on http://{addr}"); axum::serve(listener, app).await.unwrap(); Ok(()) diff --git a/src/web/pages/clients.rs b/src/web/pages/clients.rs new file mode 100644 index 0000000..4c73322 --- /dev/null +++ b/src/web/pages/clients.rs @@ -0,0 +1,51 @@ + + + +use askama::Template; +use axum::response::{Html, IntoResponse, Response}; + +use axum::{ + routing::{get, post}, + http::StatusCode, + Json, Router, +}; + +use crate::web::pages::{BaseTemplate, BaseTemplateCtx}; + +#[derive(Template)] +#[template(path = "home.html")] +pub struct PageTemplate { + pub ctx: BaseTemplateCtx, + +} + +impl BaseTemplate for PageTemplate { + fn ctx(&self) -> &BaseTemplateCtx { + &self.ctx + } + fn ctx_mut(&mut self) -> &mut BaseTemplateCtx { + &mut self.ctx + } + +} + +#[axum::debug_handler] +pub async fn get_page() -> Response { + fn inner() -> anyhow::Result<(StatusCode, String)> { + let mut template = PageTemplate { + ctx: Default::default() + }; + + template.set_title("Home"); + + Ok((StatusCode::OK, template.render()?)) + } + + match inner() { + Ok((status, s)) => (status, Html(s)).into_response(), + Err(e) => { + let s = crate::web::pages::error::get_error_page(e.to_string()).await; + (StatusCode::INTERNAL_SERVER_ERROR, Html(s)).into_response() + } + } +} diff --git a/src/web/pages/error.rs b/src/web/pages/error.rs index 8b9b295..dce6785 100644 --- a/src/web/pages/error.rs +++ b/src/web/pages/error.rs @@ -3,12 +3,12 @@ use crate::web::pages::{BaseTemplate, BaseTemplateCtx}; #[derive(Template)] #[template(path = "error.html")] -pub struct ErrorTemplate { +pub struct PageTemplate { pub ctx: BaseTemplateCtx, pub error: String } -impl BaseTemplate for ErrorTemplate { +impl BaseTemplate for PageTemplate { fn ctx(&self) -> &BaseTemplateCtx { &self.ctx } @@ -20,7 +20,7 @@ impl BaseTemplate for ErrorTemplate { #[axum::debug_handler] pub async fn get_error_page(e: String) -> String { - let mut template = ErrorTemplate { + let mut template = PageTemplate { ctx: Default::default(), error: e.to_string() }; diff --git a/src/web/pages/home.rs b/src/web/pages/home.rs index 60466b7..4c73322 100644 --- a/src/web/pages/home.rs +++ b/src/web/pages/home.rs @@ -14,12 +14,12 @@ use crate::web::pages::{BaseTemplate, BaseTemplateCtx}; #[derive(Template)] #[template(path = "home.html")] -pub struct HomeTemplate { +pub struct PageTemplate { pub ctx: BaseTemplateCtx, } -impl BaseTemplate for HomeTemplate { +impl BaseTemplate for PageTemplate { fn ctx(&self) -> &BaseTemplateCtx { &self.ctx } @@ -32,7 +32,7 @@ impl BaseTemplate for HomeTemplate { #[axum::debug_handler] pub async fn get_page() -> Response { fn inner() -> anyhow::Result<(StatusCode, String)> { - let mut template = HomeTemplate { + let mut template = PageTemplate { ctx: Default::default() }; diff --git a/src/web/pages/inventory.rs b/src/web/pages/inventory.rs new file mode 100644 index 0000000..60466b7 --- /dev/null +++ b/src/web/pages/inventory.rs @@ -0,0 +1,51 @@ + + + +use askama::Template; +use axum::response::{Html, IntoResponse, Response}; + +use axum::{ + routing::{get, post}, + http::StatusCode, + Json, Router, +}; + +use crate::web::pages::{BaseTemplate, BaseTemplateCtx}; + +#[derive(Template)] +#[template(path = "home.html")] +pub struct HomeTemplate { + pub ctx: BaseTemplateCtx, + +} + +impl BaseTemplate for HomeTemplate { + fn ctx(&self) -> &BaseTemplateCtx { + &self.ctx + } + fn ctx_mut(&mut self) -> &mut BaseTemplateCtx { + &mut self.ctx + } + +} + +#[axum::debug_handler] +pub async fn get_page() -> Response { + fn inner() -> anyhow::Result<(StatusCode, String)> { + let mut template = HomeTemplate { + ctx: Default::default() + }; + + template.set_title("Home"); + + Ok((StatusCode::OK, template.render()?)) + } + + match inner() { + Ok((status, s)) => (status, Html(s)).into_response(), + Err(e) => { + let s = crate::web::pages::error::get_error_page(e.to_string()).await; + (StatusCode::INTERNAL_SERVER_ERROR, Html(s)).into_response() + } + } +} diff --git a/src/web/pages/login.rs b/src/web/pages/login.rs index e824252..b1dfcbd 100644 --- a/src/web/pages/login.rs +++ b/src/web/pages/login.rs @@ -11,12 +11,12 @@ use crate::web::pages::{BaseTemplate, BaseTemplateCtx}; #[derive(Template)] #[template(path = "login.html")] -pub struct HomeTemplate { +pub struct PageTemplate { pub ctx: BaseTemplateCtx, } -impl BaseTemplate for HomeTemplate { +impl BaseTemplate for PageTemplate { fn ctx(&self) -> &BaseTemplateCtx { &self.ctx } @@ -29,7 +29,7 @@ impl BaseTemplate for HomeTemplate { #[axum::debug_handler] pub async fn get_page() -> Response { fn inner() -> anyhow::Result<(StatusCode, String)> { - let mut template = HomeTemplate { + let mut template = PageTemplate { ctx: Default::default() }; diff --git a/src/web/pages/mod.rs b/src/web/pages/mod.rs index d4fc7a0..91fa2b4 100644 --- a/src/web/pages/mod.rs +++ b/src/web/pages/mod.rs @@ -1,6 +1,8 @@ - +pub mod clients; +pub mod inventory; +pub mod tickets; pub mod home; pub mod login; pub mod error; diff --git a/src/web/pages/tickets.rs b/src/web/pages/tickets.rs new file mode 100644 index 0000000..0ecb5a3 --- /dev/null +++ b/src/web/pages/tickets.rs @@ -0,0 +1,51 @@ + + + +use askama::Template; +use axum::response::{Html, IntoResponse, Response}; + +use axum::{ + routing::{get, post}, + http::StatusCode, + Json, Router, +}; + +use crate::web::pages::{BaseTemplate, BaseTemplateCtx}; + +#[derive(Template)] +#[template(path = "home.html")] +pub struct PageTemplate { + pub ctx: BaseTemplateCtx, + +} + +impl BaseTemplate for PageTemplate { + fn ctx(&self) -> &BaseTemplateCtx { + &self.ctx + } + fn ctx_mut(&mut self) -> &mut BaseTemplateCtx { + &mut self.ctx + } + +} + +#[axum::debug_handler] +pub async fn get_page() -> Response { + fn inner() -> anyhow::Result<(StatusCode, String)> { + let mut template = PageTemplate { + ctx: Default::default() + }; + + template.set_title("Tickets"); + + Ok((StatusCode::OK, template.render()?)) + } + + match inner() { + Ok((status, s)) => (status, Html(s)).into_response(), + Err(e) => { + let s = crate::web::pages::error::get_error_page(e.to_string()).await; + (StatusCode::INTERNAL_SERVER_ERROR, Html(s)).into_response() + } + } +}