Add basic db structure

This commit is contained in:
Gvidas Juknevičius 2026-01-13 13:36:57 +02:00
parent 63376af86f
commit bcbbef1a82
42 changed files with 1235 additions and 162 deletions

1
.gitignore vendored
View File

@ -1 +1,2 @@
/target /target
/config.toml

View File

@ -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 @@
<head>
<meta charset="utf-8">
<title>{{ self.title() }}</title>
+ <link rel="stylesheet" href="/static/index.css">
</head>
<body>
{% 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 @@
<header>
+ <ul class="nav">
+ <li><a href="/">Home</a></li>
+ <li><a href="/personnel">Personnel</a></li>
+ <li><a href="/clients">Clients</a></li>
+ <li><a href="/tickets">Tickets</a></li>
+ <li><a href="/inventory">Inventory</a></li>
+
+ <!- not logged in>
+ <!--
+ <li class="right"><a href="/login">Sign in</a></li>
+ !-->
+
+ <!- logged in>
+ <li class="right">
+ <img class="pfp" src="/static/usericon.png">
+ <a>John Doe</a>
+ </li>
+
+ </ul>
</header>

View File

@ -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 @@
<head>
<meta charset="utf-8">
<title>{{ self.title() }}</title>
+ <link rel="stylesheet" href="/static/index.css">
</head>
<body>
{% 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 @@
<header>
+ <ul class="nav">
+ <li><a href="/">Home</a></li>
+ <li><a href="/personnel">Personnel</a></li>
+ <li><a href="/clients">Clients</a></li>
+ <li><a href="/tickets">Tickets</a></li>
+ <li><a href="/inventory">Inventory</a></li>
+
+ <!- not logged in>
+ <!--
+ <li class="right"><a href="/login">Sign in</a></li>
+ !-->
+
+ <!- logged in>
+ <li class="right">
+ <img class="pfp" src="/static/usericon.png">
+ <a>John Doe</a>
+ </li>
+
+ </ul>
</header>

526
Cargo.lock generated
View File

@ -300,6 +300,12 @@ version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3"
[[package]]
name = "camino"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.2.52" version = "1.2.52"
@ -325,6 +331,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive",
] ]
[[package]] [[package]]
@ -339,6 +346,18 @@ dependencies = [
"strsim", "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]] [[package]]
name = "clap_lex" name = "clap_lex"
version = "0.7.6" version = "0.7.6"
@ -399,12 +418,107 @@ dependencies = [
"typenum", "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]] [[package]]
name = "data-encoding" name = "data-encoding"
version = "2.9.0" version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" 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]] [[package]]
name = "digest" name = "digest"
version = "0.10.7" version = "0.10.7"
@ -415,6 +529,43 @@ dependencies = [
"crypto-common", "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]] [[package]]
name = "env_filter" name = "env_filter"
version = "0.1.4" version = "0.1.4"
@ -461,23 +612,18 @@ dependencies = [
] ]
[[package]] [[package]]
name = "form_urlencoded" name = "fma"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
dependencies = [
"percent-encoding",
]
[[package]]
name = "fuck_microsoft_access"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"askama", "askama",
"axum", "axum",
"camino",
"clap", "clap",
"diesel",
"diesel_migrations",
"env_logger", "env_logger",
"ipnet",
"log", "log",
"serde", "serde",
"serde_json", "serde_json",
@ -485,6 +631,22 @@ dependencies = [
"toml", "toml",
"tower", "tower",
"tower-http", "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]] [[package]]
@ -566,6 +728,12 @@ dependencies = [
"num-traits", "num-traits",
] ]
[[package]]
name = "heck"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "http" name = "http"
version = "1.4.0" version = "1.4.0"
@ -654,6 +822,114 @@ dependencies = [
"tower-service", "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]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.13.0" version = "2.13.0"
@ -664,6 +940,12 @@ dependencies = [
"hashbrown", "hashbrown",
] ]
[[package]]
name = "ipnet"
version = "2.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130"
[[package]] [[package]]
name = "iri-string" name = "iri-string"
version = "0.7.10" version = "0.7.10"
@ -736,6 +1018,12 @@ version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
[[package]]
name = "litemap"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.29" version = "0.4.29"
@ -754,6 +1042,27 @@ version = "2.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 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]] [[package]]
name = "mime" name = "mime"
version = "0.3.17" version = "0.3.17"
@ -791,6 +1100,12 @@ dependencies = [
"windows-sys 0.61.2", "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]] [[package]]
name = "num-traits" name = "num-traits"
version = "0.2.19" version = "0.2.19"
@ -851,6 +1166,21 @@ dependencies = [
"portable-atomic", "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]] [[package]]
name = "ppv-lite86" name = "ppv-lite86"
version = "0.2.21" version = "0.2.21"
@ -860,6 +1190,17 @@ dependencies = [
"zerocopy", "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]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.105" version = "1.0.105"
@ -1080,6 +1421,12 @@ dependencies = [
"windows-sys 0.60.2", "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]] [[package]]
name = "strsim" name = "strsim"
version = "0.11.1" version = "0.11.1"
@ -1103,6 +1450,17 @@ version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" 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]] [[package]]
name = "thiserror" name = "thiserror"
version = "2.0.17" version = "2.0.17"
@ -1123,6 +1481,46 @@ dependencies = [
"syn", "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]] [[package]]
name = "tokio" name = "tokio"
version = "1.49.0" version = "1.49.0"
@ -1331,12 +1729,31 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 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]] [[package]]
name = "utf-8" name = "utf-8"
version = "0.7.6" version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
[[package]]
name = "utf8_iter"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
[[package]] [[package]]
name = "utf8parse" name = "utf8parse"
version = "0.2.2" version = "0.2.2"
@ -1354,6 +1771,12 @@ dependencies = [
"wasm-bindgen", "wasm-bindgen",
] ]
[[package]]
name = "vcpkg"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.5" version = "0.9.5"
@ -1524,6 +1947,35 @@ version = "0.46.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 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]] [[package]]
name = "zerocopy" name = "zerocopy"
version = "0.8.33" version = "0.8.33"
@ -1544,6 +1996,60 @@ dependencies = [
"syn", "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]] [[package]]
name = "zmij" name = "zmij"
version = "1.0.13" version = "1.0.13"

View File

@ -1,5 +1,5 @@
[package] [package]
name = "fuck_microsoft_access" name = "fma"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
@ -7,8 +7,12 @@ edition = "2024"
anyhow = "1.0.100" anyhow = "1.0.100"
askama = "0.15.1" askama = "0.15.1"
axum = { version = "0.8.8", features = ["macros", "ws"] } 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" env_logger = "0.11.8"
ipnet = "2.11.0"
log = "0.4.29" log = "0.4.29"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
serde_json = "1.0.149" 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" toml = "0.9.11"
tower = { version = "0.5.3", features = ["full"] } tower = { version = "0.5.3", features = ["full"] }
tower-http = { version = "0.6.8", features = ["full"] } tower-http = { version = "0.6.8", features = ["full"] }
url = { version = "2.5.8", features = ["serde"] }

9
diesel.toml Normal file
View File

@ -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"

0
migrations/.diesel_lock Normal file
View File

View File

@ -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();

View File

@ -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;

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS users;

View File

@ -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
)

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS clients;

View File

@ -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
)

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS warehouses;

View File

@ -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()
)

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS inventory_catalog;

View File

@ -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()
)

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS inventory;

View File

@ -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
)

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS tickets;

View File

@ -0,0 +1,3 @@
CREATE TABLE IF NOT EXISTS tickets (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY
)

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS invoices;

View File

@ -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
)

View File

@ -0,0 +1,2 @@
DROP TABLE IF EXISTS services;

View File

@ -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
)

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS assigned_warehouse_managers;

View File

@ -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
)

View File

@ -0,0 +1 @@
DROP TABLE IF EXISTS warehouse_actions;

View File

@ -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
)

89
src/config.rs Normal file
View File

@ -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<String>,
#[arg(long="port", short='p')]
port: Option<u16>,
#[arg(long="database", short='D')]
database: Option<url::Url>,
}
#[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<Self> {
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<url::Url> {
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)
}
}

19
src/db/mod.rs Normal file
View File

@ -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<diesel::pg::Pg>) {
conn.run_pending_migrations(MIGRATIONS).unwrap();
log::info!("Running migrations");
}
pub fn start(cfg: &crate::config::Config) -> anyhow::Result<PgConnection> {
let mut connection = PgConnection::establish(&cfg.database_url()?.to_string())?;
run_migrations(&mut connection);
Ok(connection)
}

129
src/db/schema.rs Normal file
View File

@ -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<Date>,
phone_number -> Nullable<Text>,
gov_id_number -> Nullable<Text>,
house_number -> Nullable<Text>,
address_line -> Nullable<Text>,
city -> Nullable<Text>,
state -> Nullable<Text>,
postal_code -> Nullable<Text>,
country -> Nullable<Text>,
worker_user_id -> Nullable<Int8>,
}
}
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<Text>,
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<Text>,
date_of_birth -> Nullable<Date>,
phone_number -> Nullable<Text>,
created_at -> Timestamptz,
last_login_at -> Nullable<Timestamptz>,
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,
);

View File

@ -1,14 +1,24 @@
use log::LevelFilter; use log::LevelFilter;
mod web; mod web;
mod db;
mod config;
#[tokio::main] #[tokio::main]
async fn main() -> anyhow::Result<()> { async fn main() -> anyhow::Result<()> {
env_logger::builder() env_logger::builder()
.filter_module("fuck_microsoft_access", LevelFilter::Debug) .filter_module("fma", LevelFilter::Debug)
.filter_module("diesel_migrations", LevelFilter::Debug)
.init(); .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(()) Ok(())
} }

132
src/schema.rs Normal file
View File

@ -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<Date>,
phone_number -> Nullable<Text>,
gov_id_number -> Nullable<Text>,
house_number -> Nullable<Text>,
address_line -> Nullable<Text>,
city -> Nullable<Text>,
state -> Nullable<Text>,
postal_code -> Nullable<Text>,
country -> Nullable<Text>,
worker_user_id -> Nullable<Int8>,
}
}
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<Text>,
date_of_birth -> Nullable<Date>,
phone_number -> Nullable<Text>,
created_at -> Timestamptz,
last_login_at -> Nullable<Timestamptz>,
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,
);

View File

@ -7,8 +7,8 @@ pub mod pages;
pub async fn start() -> anyhow::Result<()> { pub async fn start(cfg: &crate::config::Config) -> anyhow::Result<()> {
let addr = "0.0.0.0:3000"; let addr = format!("{}:{}", cfg.web.host, cfg.web.port);
let app = Router::new() let app = Router::new()
.route("/", get(pages::home::get_page)) .route("/", get(pages::home::get_page))
.route("/login", get(pages::login::get_page)) .route("/login", get(pages::login::get_page))
@ -18,7 +18,7 @@ pub async fn start() -> anyhow::Result<()> {
.service(ServeDir::new("static")) .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}"); log::info!("Listening on http://{addr}");
axum::serve(listener, app).await.unwrap(); axum::serve(listener, app).await.unwrap();
Ok(()) Ok(())

51
src/web/pages/clients.rs Normal file
View File

@ -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()
}
}
}

View File

@ -3,12 +3,12 @@ use crate::web::pages::{BaseTemplate, BaseTemplateCtx};
#[derive(Template)] #[derive(Template)]
#[template(path = "error.html")] #[template(path = "error.html")]
pub struct ErrorTemplate { pub struct PageTemplate {
pub ctx: BaseTemplateCtx, pub ctx: BaseTemplateCtx,
pub error: String pub error: String
} }
impl BaseTemplate for ErrorTemplate { impl BaseTemplate for PageTemplate {
fn ctx(&self) -> &BaseTemplateCtx { fn ctx(&self) -> &BaseTemplateCtx {
&self.ctx &self.ctx
} }
@ -20,7 +20,7 @@ impl BaseTemplate for ErrorTemplate {
#[axum::debug_handler] #[axum::debug_handler]
pub async fn get_error_page(e: String) -> String { pub async fn get_error_page(e: String) -> String {
let mut template = ErrorTemplate { let mut template = PageTemplate {
ctx: Default::default(), ctx: Default::default(),
error: e.to_string() error: e.to_string()
}; };

View File

@ -14,12 +14,12 @@ use crate::web::pages::{BaseTemplate, BaseTemplateCtx};
#[derive(Template)] #[derive(Template)]
#[template(path = "home.html")] #[template(path = "home.html")]
pub struct HomeTemplate { pub struct PageTemplate {
pub ctx: BaseTemplateCtx, pub ctx: BaseTemplateCtx,
} }
impl BaseTemplate for HomeTemplate { impl BaseTemplate for PageTemplate {
fn ctx(&self) -> &BaseTemplateCtx { fn ctx(&self) -> &BaseTemplateCtx {
&self.ctx &self.ctx
} }
@ -32,7 +32,7 @@ impl BaseTemplate for HomeTemplate {
#[axum::debug_handler] #[axum::debug_handler]
pub async fn get_page() -> Response { pub async fn get_page() -> Response {
fn inner() -> anyhow::Result<(StatusCode, String)> { fn inner() -> anyhow::Result<(StatusCode, String)> {
let mut template = HomeTemplate { let mut template = PageTemplate {
ctx: Default::default() ctx: Default::default()
}; };

View File

@ -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()
}
}
}

View File

@ -11,12 +11,12 @@ use crate::web::pages::{BaseTemplate, BaseTemplateCtx};
#[derive(Template)] #[derive(Template)]
#[template(path = "login.html")] #[template(path = "login.html")]
pub struct HomeTemplate { pub struct PageTemplate {
pub ctx: BaseTemplateCtx, pub ctx: BaseTemplateCtx,
} }
impl BaseTemplate for HomeTemplate { impl BaseTemplate for PageTemplate {
fn ctx(&self) -> &BaseTemplateCtx { fn ctx(&self) -> &BaseTemplateCtx {
&self.ctx &self.ctx
} }
@ -29,7 +29,7 @@ impl BaseTemplate for HomeTemplate {
#[axum::debug_handler] #[axum::debug_handler]
pub async fn get_page() -> Response { pub async fn get_page() -> Response {
fn inner() -> anyhow::Result<(StatusCode, String)> { fn inner() -> anyhow::Result<(StatusCode, String)> {
let mut template = HomeTemplate { let mut template = PageTemplate {
ctx: Default::default() ctx: Default::default()
}; };

View File

@ -1,6 +1,8 @@
pub mod clients;
pub mod inventory;
pub mod tickets;
pub mod home; pub mod home;
pub mod login; pub mod login;
pub mod error; pub mod error;

51
src/web/pages/tickets.rs Normal file
View File

@ -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()
}
}
}