From 894f969d0f55aae697c4008faf838068ffe09ff8 Mon Sep 17 00:00:00 2001 From: didier amyot Date: Mon, 30 Jan 2023 07:52:38 -0500 Subject: [PATCH] Added trivial web ui. --- Cargo.lock | 1122 ++++++++++++++++++++++++++++++++- Cargo.toml | 6 + README.md | 12 + src/db/_accounts.rs | 12 +- src/db/mod.rs | 2 +- src/main.rs | 83 ++- src/services/_transactions.rs | 6 +- src/webui/mod.rs | 129 ++++ src/webui/views.rs | 11 + templates/base.html | 13 + templates/index.html | 5 + 11 files changed, 1382 insertions(+), 19 deletions(-) create mode 100644 src/webui/mod.rs create mode 100644 src/webui/views.rs create mode 100644 templates/base.html create mode 100644 templates/index.html diff --git a/Cargo.lock b/Cargo.lock index bc1b98c..ab2ba50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,266 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "actix-codec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +dependencies = [ + "bitflags", + "bytes", + "futures-core", + "futures-sink", + "log", + "memchr", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "actix-files" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d832782fac6ca7369a70c9ee9a20554623c5e51c76e190ad151780ebea1cf689" +dependencies = [ + "actix-http", + "actix-service", + "actix-utils", + "actix-web", + "askama_escape", + "bitflags", + "bytes", + "derive_more", + "futures-core", + "http-range", + "log", + "mime", + "mime_guess", + "percent-encoding", + "pin-project-lite", +] + +[[package]] +name = "actix-http" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0070905b2c4a98d184c4e81025253cb192aa8a73827553f38e9410801ceb35bb" +dependencies = [ + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "ahash 0.7.6", + "base64", + "bitflags", + "brotli", + "bytes", + "bytestring", + "derive_more", + "encoding_rs", + "flate2", + "futures-core", + "h2", + "http", + "httparse", + "httpdate", + "itoa 1.0.4", + "language-tags", + "local-channel", + "mime", + "percent-encoding", + "pin-project-lite", + "rand", + "sha1", + "smallvec", + "tokio", + "tokio-util", + "tracing", + "zstd 0.12.2+zstd.1.5.2", +] + +[[package]] +name = "actix-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "actix-router" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +dependencies = [ + "bytestring", + "http", + "regex", + "serde", + "tracing", +] + +[[package]] +name = "actix-rt" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "actix-server" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" +dependencies = [ + "actix-rt", + "actix-service", + "actix-utils", + "futures-core", + "futures-util", + "mio", + "num_cpus", + "socket2", + "tokio", + "tracing", +] + +[[package]] +name = "actix-service" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" +dependencies = [ + "futures-core", + "paste", + "pin-project-lite", +] + +[[package]] +name = "actix-utils" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" +dependencies = [ + "local-waker", + "pin-project-lite", +] + +[[package]] +name = "actix-web" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464e0fddc668ede5f26ec1f9557a8d44eda948732f40c6b0ad79126930eb775f" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "ahash 0.7.6", + "bytes", + "bytestring", + "cfg-if", + "cookie", + "derive_more", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "itoa 1.0.4", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2", + "time", + "url", +] + +[[package]] +name = "actix-web-codegen" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa9362663c8643d67b2d5eafba49e4cb2c8a053a29ed00a0bea121f17c76b13" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "actix-web-lab" +version = "0.18.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55b7ac6309cfb46949171c49f7bc81d021310205a39bf0b0d0465e80a4857e3c" +dependencies = [ + "actix-files", + "actix-http", + "actix-router", + "actix-service", + "actix-utils", + "actix-web", + "actix-web-lab-derive", + "ahash 0.8.3", + "arc-swap", + "async-trait", + "bytes", + "bytestring", + "csv", + "derive_more", + "futures-core", + "futures-util", + "http", + "impl-more", + "itertools", + "local-channel", + "mediatype", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "serde", + "serde_cbor_2", + "serde_html_form", + "serde_json", + "tokio", + "tracing", +] + +[[package]] +name = "actix-web-lab-derive" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4419acd7924ac473ccd00a6efea324dd382b0b7ae31b8e06cd2c58d4d7afae6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + [[package]] name = "adler32" version = "1.2.0" @@ -19,6 +279,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -28,6 +300,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + [[package]] name = "apache-avro" version = "0.14.0" @@ -51,7 +338,30 @@ dependencies = [ "typed-builder", "uuid", "zerocopy", - "zstd", + "zstd 0.11.2+zstd.1.5.2", +] + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "askama_escape" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "619743e34b5ba4e9703bba34deac3427c72507c7159f5fd030aea8cac0cfe341" + +[[package]] +name = "async-trait" +version = "0.1.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff18d764974428cf3a9328e23fc5c986f5fbed46e6cd4cdf42544df5d297ec1" +dependencies = [ + "proc-macro2", + "quote", + "syn", ] [[package]] @@ -60,7 +370,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" dependencies = [ - "hermit-abi", + "hermit-abi 0.1.19", "libc", "winapi", ] @@ -71,6 +381,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base64" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" + [[package]] name = "bitflags" version = "1.3.2" @@ -86,12 +402,60 @@ dependencies = [ "generic-array", ] +[[package]] +name = "brotli" +version = "3.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" + +[[package]] +name = "bytestring" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7f83e57d9154148e355404702e2694463241880b939570d7c97c014da7a69a1" +dependencies = [ + "bytes", +] + [[package]] name = "cc" version = "1.0.76" @@ -165,6 +529,23 @@ dependencies = [ "winapi", ] +[[package]] +name = "convert_case" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" + +[[package]] +name = "cookie" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + [[package]] name = "cpufeatures" version = "0.2.5" @@ -193,6 +574,41 @@ dependencies = [ "typenum", ] +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa 0.4.8", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "digest" version = "0.10.6" @@ -203,10 +619,28 @@ dependencies = [ "crypto-common", ] +[[package]] +name = "either" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" + +[[package]] +name = "encoding_rs" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9852635589dc9f9ea1b6fe9f05b50ef208c85c834a562f0c6abb1c475736ec2b" +dependencies = [ + "cfg-if", +] + [[package]] name = "escarmouche-ledger" version = "0.1.0" dependencies = [ + "actix-utils", + "actix-web", + "actix-web-lab", "apache-avro", "clap", "clap-verbosity-flag", @@ -215,6 +649,8 @@ dependencies = [ "home", "lazy_static", "log", + "minijinja", + "minijinja-autoreload", "regex", "resource", "rusqlite", @@ -223,6 +659,7 @@ dependencies = [ "sha2", "simple_logger", "tempfile", + "tokio", ] [[package]] @@ -246,6 +683,43 @@ dependencies = [ "instant", ] +[[package]] +name = "filetime" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e884668cd0c7480504233e951174ddc3b382f7c2666e3b7310b5c4e7b0c37f9" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[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.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +dependencies = [ + "percent-encoding", +] + [[package]] name = "fraction" version = "0.13.0" @@ -256,6 +730,46 @@ dependencies = [ "num", ] +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc", +] + +[[package]] +name = "futures-core" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac" + +[[package]] +name = "futures-sink" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39c15cf1a4aa79df40f1bb462fb39676d0ad9e366c2a33b590d7c66f4f81fcf9" + +[[package]] +name = "futures-task" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea" + +[[package]] +name = "futures-util" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", +] + [[package]] name = "generic-array" version = "0.14.6" @@ -277,13 +791,38 @@ dependencies = [ "wasi", ] +[[package]] +name = "h2" +version = "0.3.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f9f29bc9dda355256b2916cf526ab02ce0aeaaaf2bad60d65ef3f12f11dd0f4" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + [[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", ] [[package]] @@ -310,6 +849,15 @@ dependencies = [ "libc", ] +[[package]] +name = "hermit-abi" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" +dependencies = [ + "libc", +] + [[package]] name = "hex" version = "0.4.3" @@ -325,6 +873,81 @@ dependencies = [ "winapi", ] +[[package]] +name = "http" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75f43d41e26995c17e71ee126451dd3941010b0514a81a9d11f3b341debc2399" +dependencies = [ + "bytes", + "fnv", + "itoa 1.0.4", +] + +[[package]] +name = "http-range" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21dec9db110f5f872ed9699c3ecf50cf16f423502706ba5c72462e28d3157573" + +[[package]] +name = "httparse" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" + +[[package]] +name = "httpdate" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "idna" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "impl-more" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d14c422088773284a62e155b3e2f700a52cd3731dabe2cffd914e13f71b6bd4e" + +[[package]] +name = "indexmap" +version = "1.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "inotify" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8069d3ec154eb856955c1c0fbffefbf5f3c40a104ec912d4797314c1801abff" +dependencies = [ + "bitflags", + "inotify-sys", + "libc", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc", +] + [[package]] name = "instant" version = "0.1.12" @@ -334,6 +957,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + [[package]] name = "itoa" version = "1.0.4" @@ -349,6 +987,32 @@ dependencies = [ "libc", ] +[[package]] +name = "kqueue" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c8fc60ba15bf51257aa9807a48a61013db043fcf3a78cb0d916e8e396dcad98" +dependencies = [ + "kqueue-sys", + "libc", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8367585489f01bc55dd27404dcf56b95e6da061a256a666ab23be9ba96a2e587" +dependencies = [ + "bitflags", + "libc", +] + +[[package]] +name = "language-tags" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + [[package]] name = "lazy_static" version = "1.4.0" @@ -392,6 +1056,34 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "local-channel" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +dependencies = [ + "futures-core", + "futures-sink", + "futures-util", + "local-waker", +] + +[[package]] +name = "local-waker" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" + +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -401,12 +1093,99 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "mediatype" +version = "0.19.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea6e62614ab2fc0faa58bb15102a0382d368f896a9fa4776592589ab55c4de7" + [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memo-map" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aec276c09560ce4447087aaefc19eb0c18d97e31bd05ebac38881c4723400c40" + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minijinja" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18b0af1900a114a8183b10b4266a9a4fc2816afdb7089592492c8eadd07f1629" +dependencies = [ + "memo-map", + "self_cell", + "serde", +] + +[[package]] +name = "minijinja-autoreload" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d45e7ce77eb20a0ccfd8004184c90e0549806b16bbd0f05755d3156dbedbad34" +dependencies = [ + "minijinja", + "notify", +] + +[[package]] +name = "miniz_oxide" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +dependencies = [ + "adler", +] + +[[package]] +name = "mio" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de" +dependencies = [ + "libc", + "log", + "wasi", + "windows-sys", +] + +[[package]] +name = "notify" +version = "5.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ea850aa68a06e48fdb069c0ec44d0d64c8dbffa49bf3b6f7f0a901fdea1ba9" +dependencies = [ + "bitflags", + "filetime", + "fsevent-sys", + "inotify", + "kqueue", + "libc", + "mio", + "walkdir", + "windows-sys", +] + [[package]] name = "num" version = "0.4.0" @@ -483,6 +1262,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +dependencies = [ + "hermit-abi 0.2.6", + "libc", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -504,6 +1293,53 @@ version = "6.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b5bf27447411e9ee3ff51186bf7a08e16c341efdde93f4d823e8844429bed7e" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ef8814b5c993410bb3adfad7a5ed269563e4a2f90c41f5d85be7fb47133bf" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + +[[package]] +name = "paste" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01a5bd0424d00070b0098dd17ebca6f961a959dead1dbcbbbc1d1cd8d3deeba" + +[[package]] +name = "percent-encoding" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" + +[[package]] +name = "pin-project-lite" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + [[package]] name = "pkg-config" version = "0.3.26" @@ -614,6 +1450,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.28" @@ -655,6 +1497,15 @@ dependencies = [ "smallvec", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -667,6 +1518,33 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + +[[package]] +name = "semver" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58bc9567378fc7690d6b2addae4e60ac2eeea07becb2c64b9f218b53865cba2a" + [[package]] name = "serde" version = "1.0.147" @@ -676,6 +1554,16 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde_cbor_2" +version = "0.12.0-dev" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b46d75f449e01f1eddbe9b00f432d616fbbd899b809c837d0fbc380496a0dd55" +dependencies = [ + "half", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.147" @@ -687,17 +1575,53 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_html_form" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d051fb33111db0e81673ed8c55db741952a19ad81dc584960c8aec836498ba5" +dependencies = [ + "form_urlencoded", + "indexmap", + "itoa 1.0.4", + "ryu", + "serde", +] + [[package]] name = "serde_json" version = "1.0.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "020ff22c755c2ed3f8cf162dbb41a7268d934702f3ed3631656ea597e08fc3db" dependencies = [ - "itoa", + "itoa 1.0.4", "ryu", "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa 1.0.4", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.6" @@ -709,6 +1633,15 @@ dependencies = [ "digest", ] +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + [[package]] name = "simple_logger" version = "4.0.0" @@ -722,12 +1655,31 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "slab" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4614a76b2a8be0058caa9dbbaf66d988527d86d003c11a94fbd335d7661edcef" +dependencies = [ + "autocfg", +] + [[package]] name = "smallvec" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +[[package]] +name = "socket2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2d2db9033d13a1567121ddd7a095ee144db4e1ca1b1bda3419bc0da294ebd" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "strsim" version = "0.10.0" @@ -813,7 +1765,7 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a561bf4617eebd33bca6434b988f39ed798e527f51a1e797d0ee4f61c0a38376" dependencies = [ - "itoa", + "itoa 1.0.4", "libc", "num_threads", "serde", @@ -836,6 +1788,99 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a12a59981d9e3c38d216785b0c37399f6e415e8d0712047620f189371b0bb" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys", +] + +[[package]] +name = "tokio-macros" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d266c00fde287f55d3f1c3e96c500c362a2b8c695076ec180f27918820bc6df8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-util" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb2e075f03b3d66d8d8785356224ba688d2906a371015e225beeb65ca92c740" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", + "tracing", +] + +[[package]] +name = "tracing" +version = "0.1.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +dependencies = [ + "cfg-if", + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +dependencies = [ + "once_cell", +] + [[package]] name = "typed-builder" version = "0.10.0" @@ -853,12 +1898,47 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54675592c1dbefd78cbd98db9bacd89886e1ca50692a0692baefffdeb92dd58" + [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + [[package]] name = "uuid" version = "1.2.2" @@ -880,6 +1960,17 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1001,7 +2092,16 @@ version = "0.11.2+zstd.1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" dependencies = [ - "zstd-safe", + "zstd-safe 5.0.2+zstd.1.5.2", +] + +[[package]] +name = "zstd" +version = "0.12.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9262a83dc741c0b0ffec209881b45dbc232c21b02a2b9cb1adb93266e41303d" +dependencies = [ + "zstd-safe 6.0.2+zstd.1.5.2", ] [[package]] @@ -1014,6 +2114,16 @@ dependencies = [ "zstd-sys", ] +[[package]] +name = "zstd-safe" +version = "6.0.2+zstd.1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cf39f730b440bab43da8fb5faf5f254574462f73f260f85f7987f32154ff17" +dependencies = [ + "libc", + "zstd-sys", +] + [[package]] name = "zstd-sys" version = "2.0.4+zstd.1.5.2" diff --git a/Cargo.toml b/Cargo.toml index 7611f2e..9ac8095 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,12 @@ regex = "1.7.0" lazy_static = "1.4.0" tempfile = "3.3.0" fraction = "0.13.0" +actix-web = "4.3.0" +tokio = { version = "1.24.2", features = ["full"] } +minijinja = { version = "0.30.2", features = ["source"] } +actix-utils = "3.0.1" +actix-web-lab = "0.18.9" +minijinja-autoreload = "0.30.2" [dependencies.apache-avro] version = "0.14.0" diff --git a/README.md b/README.md index 443f049..298e6cc 100644 --- a/README.md +++ b/README.md @@ -22,3 +22,15 @@ nix build .#development-environment nix develop ``` +## Workwork + +* [ ] Parametrize `CURRENT_TIMESTAMP` + +### Todo + +#### API + + +#### Front end + +* [ ] return hello world. diff --git a/src/db/_accounts.rs b/src/db/_accounts.rs index d47166c..7397d88 100644 --- a/src/db/_accounts.rs +++ b/src/db/_accounts.rs @@ -190,16 +190,16 @@ pub fn is_owner(tx: &Connection, account_id: &AccountId, user_id: &UserId) -> Cu Ok(option.is_some()) } -pub struct Receipt { +pub struct AccountChangeReceipt { pub account_id: AccountId, pub version: i64, pub timestamp: i64, pub amount: i64, } -impl Receipt { - pub fn from_row(row: &Row) -> Result { - Ok(Receipt { +impl AccountChangeReceipt { + pub fn from_row(row: &Row) -> Result { + Ok(AccountChangeReceipt { account_id: AccountId::from_id(row.get("id")?), version: row.get("version")?, amount: row.get("amount")?, @@ -224,7 +224,7 @@ fn update_account_history(tx: &Connection, account_id: &AccountId) -> CustomResu pub fn add_to_account(tx: &Connection, account_id: &AccountId, - amount: i64) -> CustomResult { + amount: i64) -> CustomResult { let q = " UPDATE accounts SET amount = CASE account_type WHEN 'security_emitter' @@ -236,7 +236,7 @@ pub fn add_to_account(tx: &Connection, RETURNING id, version, amount, UNIXEPOCH(CURRENT_TIMESTAMP) as timestamp "; let params = params![amount, account_id]; - let ret = tx.query_row(q, params, Receipt::from_row).map_err(Error::SqlError); + let ret = tx.query_row(q, params, AccountChangeReceipt::from_row).map_err(Error::SqlError); update_account_history(tx, account_id)?; return ret; } diff --git a/src/db/mod.rs b/src/db/mod.rs index 4481c6c..bc19e9b 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -20,7 +20,7 @@ pub mod users { pub mod accounts { pub use super::_accounts::{Account, - Receipt, + AccountChangeReceipt, create_checking_account, create_security_emitter_account, is_owner, diff --git a/src/main.rs b/src/main.rs index a5bdb1f..ad0d37b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; +use clap::{Arg, ArgAction, ArgMatches, Command, value_parser}; use clap::parser::ValuesRef; -use clap::{value_parser, Arg, ArgAction, ArgMatches, Command}; use log::{debug, error, info}; use rusqlite::Connection; use serde::Serialize; @@ -10,14 +10,16 @@ use simple_logger::SimpleLogger; use db::securities::list_securities; use crate::db::users::{create_user, list_users}; -use crate::event_processor::{process_event_files}; +use crate::event_processor::process_event_files; use crate::migration::apply_migrations; +use crate::webui::web_ui_main; mod db; mod event_processor; mod migration; mod services; mod util; +mod webui; #[derive(Debug, Serialize)] struct Output { @@ -42,6 +44,8 @@ trait CommandLineUserLogic { fn list_securities(&mut self, conn: &Connection); fn process_events(&mut self, conn: &mut Connection, files: ValuesRef); + + fn run_webui(&mut self, conn: &mut Connection, port: u16, enable_template_autoreload: bool); } fn verbosity_arg() -> Arg { @@ -130,12 +134,36 @@ fn ls_securities_command_line_parser() -> Command { .about("List all securities."); } +fn run_web_ui_command_line_parser() -> Command { + return Command::new("run") + .arg(Arg::new("port") + .long("port") + .value_parser(clap::value_parser!(u16).range(3000..)) + .action(ArgAction::Set) + .default_value("3200")) + .arg(Arg::new("enable-template-autoreload") + .long("enable-template-autoreload") + .required(false) + .num_args(0) + .help("Reload underlying template (for development)") + ) + .about("Run web user interface."); +} + fn securities_command_line_parser() -> Command { return Command::new("securities") + .about("Handy command about securities.") .subcommand(ls_securities_command_line_parser()) .arg_required_else_help(true); } +fn web_ui_command_line_parser() -> Command { + return Command::new("wui") + .about("Web user interface.") + .subcommand(run_web_ui_command_line_parser()) + .arg_required_else_help(true); +} + fn command_line_parser() -> Command { return Command::new("prog") .arg(verbosity_arg()) @@ -143,6 +171,7 @@ fn command_line_parser() -> Command { .subcommand(event_command_line_parser()) .subcommand(user_command_line_parser()) .subcommand(securities_command_line_parser()) + .subcommand(web_ui_command_line_parser()) .arg_required_else_help(true); } @@ -207,6 +236,17 @@ fn process_process_events_command_line( logic.process_events(conn, files); } +fn process_run_wui_command_line( + conn: &mut Connection, + m: &ArgMatches, + logic: &mut dyn CommandLineUserLogic, +) { + debug!("Processing run wui command line {:?}.", m); + let mut port = m.get_one::("port").unwrap_or(&3200); + let enable_template_autoreload = m.get_flag("enable-template-autoreload"); + logic.run_webui(conn, *port, enable_template_autoreload); +} + fn process_securities_command_line( conn: &Connection, m: &ArgMatches, @@ -235,6 +275,20 @@ fn process_events_command_line( } } +fn process_wui_command_line( + conn: &mut Connection, + m: &ArgMatches, + logic: &mut dyn CommandLineUserLogic, +) { + debug!("Processing wui command line {:?}.", m); + match m.subcommand() { + Some(("run", run_wui_match)) => { + process_run_wui_command_line(conn, run_wui_match, logic); + } + _ => unreachable!(), + } +} + fn process_command_line( conn: &mut Connection, m: &ArgMatches, @@ -245,13 +299,15 @@ fn process_command_line( Some(("users", user_match)) => { process_user_command_line(conn, user_match, logic); } - Some(("securities", securities_match)) => { process_securities_command_line(conn, securities_match, logic); } Some(("events", events_match)) => { process_events_command_line(conn, events_match, logic); } + Some(("wui", wui_match)) => { + process_wui_command_line(conn, wui_match, logic); + } _ => { println!("Received un parsable command. {:?}", m.subcommand()); unreachable!(); @@ -268,6 +324,7 @@ mod cli_test { create_user_count: usize, list_securities_count: usize, process_events_count: usize, + run_webui_count: usize, } impl TestCommandLineLogic { @@ -277,6 +334,7 @@ mod cli_test { create_user_count: 0, list_securities_count: 0, process_events_count: 0, + run_webui_count: 0, }; } } @@ -297,6 +355,10 @@ mod cli_test { fn process_events(&mut self, conn: &mut Connection, _files: ValuesRef) { self.process_events_count += 1; } + + fn run_webui(&mut self, conn: &mut Connection, port: u16, enable_template_autoreload: bool) { + self.run_webui_count += 1; + } } #[test] @@ -378,6 +440,16 @@ mod cli_test { process_command_line(&mut conn, &m, &mut logic); assert_eq!(logic.process_events_count, 1); } + + // #[test] + // fn run_web_ui() { + // let cmd = command_line_parser(); + // let m = cmd.get_matches_from(vec!["test", "webui", "run"]); + // let mut logic = TestCommandLineLogic::new(); + // let mut conn = Connection::open_in_memory().unwrap(); + // process_command_line(&mut conn, &m, &mut logic); + // assert_eq!(logic.run_webui_count, 1); + // } } struct ProCommandLineLogic {} @@ -426,6 +498,11 @@ impl CommandLineUserLogic for ProCommandLineLogic { println!("fuck me"); } + + fn run_webui(&mut self, conn: &mut Connection, port: u16, enable_template_autoreload: bool) { + info!("Serving web-ui on port {:?}", port); + web_ui_main(port, enable_template_autoreload); + } } fn get_connection(m: &ArgMatches) -> Connection { diff --git a/src/services/_transactions.rs b/src/services/_transactions.rs index 5962981..bb17e57 100644 --- a/src/services/_transactions.rs +++ b/src/services/_transactions.rs @@ -8,7 +8,7 @@ use crate::util::*; fn _move_securities(tx: &Savepoint, cheque: &Cheque, - account_id: &AccountId) -> CustomResult<(accounts::Receipt, accounts::Receipt)> { + account_id: &AccountId) -> CustomResult<(accounts::AccountChangeReceipt, accounts::AccountChangeReceipt)> { let drawer_receipt = accounts::add_to_account(tx, &cheque.drawer_account_id, -cheque.amount)?; let payee_receipt = accounts::add_to_account(tx, account_id, cheque.amount)?; if drawer_receipt.amount < 0 { @@ -20,8 +20,8 @@ fn _move_securities(tx: &Savepoint, fn _add_cheque_to_cashed_cheques_log(tx: &Connection, cheque: &Cheque, - drawer_receipt: &accounts::Receipt, - payee_receipt: &accounts::Receipt) -> Result<(), Error> { + drawer_receipt: &accounts::AccountChangeReceipt, + payee_receipt: &accounts::AccountChangeReceipt) -> Result<(), Error> { let q = " INSERT INTO cashed_cheques( drawer_user_id, diff --git a/src/webui/mod.rs b/src/webui/mod.rs new file mode 100644 index 0000000..7ae83e2 --- /dev/null +++ b/src/webui/mod.rs @@ -0,0 +1,129 @@ +mod views; + +use actix_utils::future::{ready, Ready}; +use minijinja; +use minijinja_autoreload::AutoReloader; +use std::{collections::HashMap, env, fs, io, path::PathBuf}; +use std::io::{Error, ErrorKind}; +use std::path::Path; + +use actix_web::{ + dev::{ServiceResponse}, + error, + http::{header::ContentType, StatusCode}, + middleware::{ErrorHandlerResponse, ErrorHandlers, Logger}, + web, App, FromRequest, HttpRequest, HttpResponse, HttpServer, Responder, Result, +}; +use actix_web_lab::respond::Html; + +struct MiniJinjaRenderer { + tmpl_env: web::Data, +} + +impl MiniJinjaRenderer { + fn render( + &self, + tmpl: &str, + ctx: impl Into, + ) -> actix_web::Result { + log::info!("Rendering template {:?}", tmpl); + self.tmpl_env + .acquire_env() + .map_err(|_| error::ErrorInternalServerError("could not acquire template env"))? + .get_template(tmpl) + .map_err(|e| error::ErrorInternalServerError(format!("{:?}", e)))? + .render(ctx.into()) + .map(Html) + .map_err(|err| { + log::error!("{err}"); + error::ErrorInternalServerError("template error") + }) + } +} + +impl FromRequest for MiniJinjaRenderer { + type Error = actix_web::Error; + type Future = actix_utils::future::Ready>; + + fn from_request(req: &HttpRequest, _pl: &mut actix_web::dev::Payload) -> Self::Future { + let tmpl_env = >::extract(req) + .into_inner() + .unwrap(); + + actix_utils::future::ready(Ok(Self { tmpl_env })) + } +} + + +#[tokio::main] +pub async fn web_ui_main(port: u16, enable_template_autoreload: bool) -> std::io::Result<()> { + // The closure is invoked every time the environment is outdated to recreate it. + let tmpl_reloader = AutoReloader::new(move |notifier| { + let mut env: minijinja::Environment<'static> = minijinja::Environment::new(); + + let tmpl_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("templates"); + log::info!("Using template dir {:?}", tmpl_path); + + // if watch_path is never called, no fs watcher is created + if enable_template_autoreload { + notifier.watch_path(&tmpl_path, true); + } + env.set_source(minijinja::Source::from_path(tmpl_path)); + + Ok(env) + }); + + let tmpl_reloader = web::Data::new(tmpl_reloader); + + HttpServer::new(move || { + App::new() + .app_data(tmpl_reloader.clone()) + .service(views::index) + .wrap(ErrorHandlers::new().handler(StatusCode::NOT_FOUND, not_found)) + .wrap(Logger::default()) + }) + .workers(2) + .bind(("0.0.0.0", port))? + .run() + .await +} + +/// Error handler for a 404 Page not found error. +fn not_found(svc_res: ServiceResponse) -> Result> { + let res = get_error_response(&svc_res, "Page not found"); + + Ok(ErrorHandlerResponse::Response(ServiceResponse::new( + svc_res.into_parts().0, + res.map_into_right_body(), + ))) +} + +/// Generic error handler. +fn get_error_response(res: &ServiceResponse, error: &str) -> HttpResponse { + let req = res.request(); + + let tmpl_env = MiniJinjaRenderer::extract(req).into_inner().unwrap(); + + // Provide a fallback to a simple plain text response in case an error occurs during the + // rendering of the error page. + let fallback = |err: &str| { + HttpResponse::build(res.status()) + .content_type(ContentType::plaintext()) + .body(err.to_string()) + }; + + let ctx = minijinja::context! { + error => error, + status_code => res.status().as_str(), + }; + + match tmpl_env.render("error.html", ctx) { + Ok(body) => body + .customize() + .with_status(res.status()) + .respond_to(req) + .map_into_boxed_body(), + + Err(_) => fallback(error), + } +} diff --git a/src/webui/views.rs b/src/webui/views.rs new file mode 100644 index 0000000..f1a5957 --- /dev/null +++ b/src/webui/views.rs @@ -0,0 +1,11 @@ +use std::collections::HashMap; +use actix_web::{Responder, web}; +use crate::webui::MiniJinjaRenderer; + +#[actix_web::get("/")] +pub async fn index( + tmpl_env: MiniJinjaRenderer, + query: web::Query>, +) -> actix_web::Result { + tmpl_env.render("index.html", ()) +} diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000..3c712cf --- /dev/null +++ b/templates/base.html @@ -0,0 +1,13 @@ + + + + + + ledger + + + + + {% block body %}{% endblock %} + + diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..786f006 --- /dev/null +++ b/templates/index.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} + +{% block body %} +

Hello World!

+{% endblock %} \ No newline at end of file