From 0c0c0a103860251ec4b5cca5164d377928e42214 Mon Sep 17 00:00:00 2001 From: Kyle Baumgarten Date: Sun, 25 Jan 2026 19:52:36 -0500 Subject: [PATCH 1/3] move to postgres --- Cargo.lock | 4451 +++++++++-------------------------- Cargo.toml | 21 +- README.md | 124 + docker-compose.yml | 24 +- sql-scripts/initapi.sql | 28 + src/connkey.rs | 76 +- src/createdatabase.rs | 45 +- src/createrecord.rs | 4 +- src/createrelationship.rs | 28 +- src/dbconnect.rs | 80 +- src/delete.rs | 21 +- src/insertrecords.rs | 12 +- src/main.rs | 66 +- src/pushdata.rs | 11 +- src/pushdata/gettablecol.rs | 17 +- src/querytable.rs | 133 +- src/relationships.rs | 51 +- src/tablecreate.rs | 21 +- src/update.rs | 13 +- 19 files changed, 1676 insertions(+), 3550 deletions(-) create mode 100644 README.md create mode 100644 sql-scripts/initapi.sql diff --git a/Cargo.lock b/Cargo.lock index 0e2a6e7..7fe6e4a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,502 +1,123 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 - -[[package]] -name = "actix" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f728064aca1c318585bf4bb04ffcfac9e75e508ab4e8b1bd9ba5dfe04e2cbed5" -dependencies = [ - "actix-rt 2.8.0", - "bitflags", - "bytes 1.4.0", - "crossbeam-channel", - "futures-core", - "futures-sink", - "futures-task", - "futures-util", - "log", - "once_cell", - "parking_lot 0.12.1", - "pin-project-lite 0.2.9", - "smallvec", - "tokio 1.27.0", - "tokio-util 0.7.7", -] - -[[package]] -name = "actix-codec" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e55f0a5c2ca15795035d90c46bd0e73a5123b72f68f12596d6ba5282051380" -dependencies = [ - "bitflags", - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "tokio 0.2.25", - "tokio-util 0.2.0", -] - -[[package]] -name = "actix-codec" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d1833b3838dbe990df0f1f87baf640cf6146e898166afe401839d1b001e570" -dependencies = [ - "bitflags", - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project 0.4.30", - "tokio 0.2.25", - "tokio-util 0.3.1", -] +version = 4 [[package]] name = "actix-codec" -version = "0.5.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a7559404a7f3573127aab53c08ce37a6c6a315c374a31070f3c91cd1b4a7fe" +checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ "bitflags", - "bytes 1.4.0", + "bytes", "futures-core", "futures-sink", - "log", "memchr", - "pin-project-lite 0.2.9", - "tokio 1.27.0", - "tokio-util 0.7.7", -] - -[[package]] -name = "actix-connect" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95cc9569221e9802bf4c377f6c18b90ef10227d787611decf79fd47d2a8e76c" -dependencies = [ - "actix-codec 0.2.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "actix-utils 1.0.6", - "derive_more", - "either", - "futures", - "http", - "log", - "trust-dns-proto", - "trust-dns-resolver", -] - -[[package]] -name = "actix-http" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c16664cc4fdea8030837ad5a845eb231fb93fc3c5c171edfefb52fad92ce9019" -dependencies = [ - "actix-codec 0.2.0", - "actix-connect", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "actix-threadpool", - "actix-utils 1.0.6", - "base64 0.11.0", - "bitflags", - "bytes 0.5.6", - "chrono", - "copyless", - "derive_more", - "either", - "encoding_rs", - "futures-channel", - "futures-core", - "futures-util", - "fxhash", - "h2 0.2.7", - "http", - "httparse", - "indexmap", - "language-tags 0.2.2", - "lazy_static", - "log", - "mime", - "percent-encoding", - "pin-project 0.4.30", - "rand 0.7.3", - "regex", - "ring", - "serde", - "serde_json", - "serde_urlencoded 0.6.1", - "sha1 0.6.1", - "slab", - "time 0.1.45", + "pin-project-lite", + "tokio", + "tokio-util", + "tracing", ] [[package]] name = "actix-http" -version = "3.3.1" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2079246596c18b4a33e274ae10c0e50613f4d32a4198e09c7b93771013fed74" +checksum = "44dfe5c9e0004c623edc65391dfd51daa201e7e30ebd9c9bedf873048ec32bc2" dependencies = [ - "actix-codec 0.5.0", - "actix-rt 2.8.0", - "actix-service 2.0.2", - "actix-utils 3.0.1", - "ahash 0.8.3", - "base64 0.21.0", + "actix-codec", + "actix-rt", + "actix-service", + "actix-utils", + "base64", "bitflags", "brotli", - "bytes 1.4.0", + "bytes", "bytestring", "derive_more", "encoding_rs", "flate2", + "foldhash", "futures-core", - "h2 0.3.16", + "h2", "http", "httparse", "httpdate", - "itoa 1.0.6", - "language-tags 0.3.2", + "itoa", + "language-tags", "local-channel", "mime", "percent-encoding", - "pin-project-lite 0.2.9", - "rand 0.8.5", - "sha1 0.10.5", + "pin-project-lite", + "rand 0.9.2", + "sha1", "smallvec", - "tokio 1.27.0", - "tokio-util 0.7.7", + "tokio", + "tokio-util", "tracing", "zstd", ] -[[package]] -name = "actix-identity" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a379b0639c293292d71defb8cc1f94c87b7705c904adf044338ad392df77c7a" -dependencies = [ - "actix-service 1.0.6", - "actix-web 2.0.0", - "futures", - "serde", - "serde_json", - "time 0.1.45", -] - -[[package]] -name = "actix-macros" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ca8ce00b267af8ccebbd647de0d61e0674b6e61185cc7a592ff88772bed655" -dependencies = [ - "quote", - "syn 1.0.109", -] - [[package]] name = "actix-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" -dependencies = [ - "quote", - "syn 1.0.109", -] - -[[package]] -name = "actix-multipart" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee489e3c01eae4d1c35b03c4493f71cb40d93f66b14558feb1b1a807671cc4e" -dependencies = [ - "actix-multipart-derive", - "actix-utils 3.0.1", - "actix-web 4.3.1", - "bytes 1.4.0", - "derive_more", - "futures-core", - "futures-util", - "httparse", - "local-waker", - "log", - "memchr", - "mime", - "serde", - "serde_json", - "serde_plain", - "tempfile", - "tokio 1.27.0", -] - -[[package]] -name = "actix-multipart-derive" -version = "0.6.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ec592f234db8a253cf80531246a4407c8a70530423eea80688a6c5a44a110e7" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ - "darling", - "parse-size", - "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "actix-redis" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38446dc11c743f4f0023b1067c7cfb8f2548f24418e31a193b324e35fa059279" -dependencies = [ - "actix", - "actix-rt 2.8.0", - "actix-service 2.0.2", - "actix-tls 3.0.3", - "actix-web 4.3.1", - "backoff", - "derive_more", - "futures-core", - "log", - "redis-async", - "time 0.3.20", - "tokio 1.27.0", - "tokio-util 0.7.7", -] - -[[package]] -name = "actix-router" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ad299af73649e1fc893e333ccf86f377751eb95ff875d095131574c6f43452c" -dependencies = [ - "bytestring", - "http", - "log", - "regex", - "serde", + "syn", ] [[package]] name = "actix-router" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66ff4d247d2b160861fa2866457e85706833527840e4133f8f49aa423a38799" +checksum = "13d324164c51f63867b57e73ba5936ea151b8a41a1d23d1031eeb9f70d0236f8" dependencies = [ "bytestring", + "cfg-if", "http", "regex", + "regex-lite", "serde", "tracing", ] [[package]] name = "actix-rt" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "143fcc2912e0d1de2bcf4e2f720d2a60c28652ab4179685a1ee159e0fb3db227" -dependencies = [ - "actix-macros 0.1.3", - "actix-threadpool", - "copyless", - "futures-channel", - "futures-util", - "smallvec", - "tokio 0.2.25", -] - -[[package]] -name = "actix-rt" -version = "2.8.0" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15265b6b8e2347670eb363c47fc8c75208b4a4994b27192f345fcbe707804f3e" +checksum = "24eda4e2a6e042aa4e55ac438a2ae052d3b5da0ecf83d7411e1a368946925208" dependencies = [ "futures-core", - "tokio 1.27.0", -] - -[[package]] -name = "actix-server" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45407e6e672ca24784baa667c5d32ef109ccdd8d5e0b5ebb9ef8a67f4dfb708e" -dependencies = [ - "actix-codec 0.3.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "actix-utils 2.0.0", - "futures-channel", - "futures-util", - "log", - "mio 0.6.23", - "mio-uds", - "num_cpus", - "slab", - "socket2 0.3.19", + "tokio", ] [[package]] name = "actix-server" -version = "2.2.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e8613a75dd50cc45f473cee3c34d59ed677c0f7b44480ce3b8247d7dc519327" +checksum = "a65064ea4a457eaf07f2fba30b4c695bf43b721790e9530d26cb6f9019ff7502" dependencies = [ - "actix-rt 2.8.0", - "actix-service 2.0.2", - "actix-utils 3.0.1", + "actix-rt", + "actix-service", + "actix-utils", "futures-core", "futures-util", - "mio 0.8.6", - "num_cpus", - "socket2 0.4.9", - "tokio 1.27.0", + "mio", + "socket2 0.5.10", + "tokio", "tracing", ] [[package]] name = "actix-service" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0052435d581b5be835d11f4eb3bce417c8af18d87ddf8ace99f8e67e595882bb" -dependencies = [ - "futures-util", - "pin-project 0.4.30", -] - -[[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 0.2.9", -] - -[[package]] -name = "actix-session" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43da8b818ae1f11049a4d218975345fe8e56ce5a5f92c11f972abcff5ff80e87" -dependencies = [ - "actix", - "actix-redis", - "actix-service 2.0.2", - "actix-utils 3.0.1", - "actix-web 4.3.1", - "anyhow", - "async-trait", - "derive_more", - "futures-core", - "rand 0.8.5", - "serde", - "serde_json", - "tracing", -] - -[[package]] -name = "actix-testing" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47239ca38799ab74ee6a8a94d1ce857014b2ac36f242f70f3f75a66f691e791c" -dependencies = [ - "actix-macros 0.1.3", - "actix-rt 1.1.1", - "actix-server 1.0.4", - "actix-service 1.0.6", - "log", - "socket2 0.3.19", -] - -[[package]] -name = "actix-threadpool" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d209f04d002854b9afd3743032a27b066158817965bf5d036824d19ac2cc0e30" -dependencies = [ - "derive_more", - "futures-channel", - "lazy_static", - "log", - "num_cpus", - "parking_lot 0.11.2", - "threadpool", -] - -[[package]] -name = "actix-tls" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e5b4faaf105e9a6d389c606c298dcdb033061b00d532af9df56ff3a54995a8" -dependencies = [ - "actix-codec 0.2.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "actix-utils 1.0.6", - "derive_more", - "either", - "futures", - "log", -] - -[[package]] -name = "actix-tls" -version = "3.0.3" +version = "2.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fde0cf292f7cdc7f070803cb9a0d45c018441321a78b1042ffbbb81ec333297" +checksum = "9e46f36bf0e5af44bdc4bdb36fbbd421aa98c79a9bce724e1edeb3894e10dc7f" dependencies = [ - "actix-codec 0.5.0", - "actix-rt 2.8.0", - "actix-service 2.0.2", - "actix-utils 3.0.1", "futures-core", - "log", - "pin-project-lite 0.2.9", - "tokio-util 0.7.7", -] - -[[package]] -name = "actix-utils" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf8f5631bf01adec2267808f00e228b761c60c0584cc9fa0b5364f41d147f4e" -dependencies = [ - "actix-codec 0.2.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "bitflags", - "bytes 0.5.6", - "either", - "futures", - "log", - "pin-project 0.4.30", - "slab", -] - -[[package]] -name = "actix-utils" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9022dec56632d1d7979e59af14f0597a28a830a9c1c7fec8b2327eb9f16b5a" -dependencies = [ - "actix-codec 0.3.0", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "bitflags", - "bytes 0.5.6", - "either", - "futures-channel", - "futures-sink", - "futures-util", - "log", - "pin-project 0.4.30", - "slab", + "pin-project-lite", ] [[package]] @@ -506,3385 +127,1614 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" dependencies = [ "local-waker", - "pin-project-lite 0.2.9", -] - -[[package]] -name = "actix-web" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3158e822461040822f0dbf1735b9c2ce1f95f93b651d7a7aded00b1efbb1f635" -dependencies = [ - "actix-codec 0.2.0", - "actix-http 1.0.1", - "actix-macros 0.1.3", - "actix-router 0.2.7", - "actix-rt 1.1.1", - "actix-server 1.0.4", - "actix-service 1.0.6", - "actix-testing", - "actix-threadpool", - "actix-tls 1.0.0", - "actix-utils 1.0.6", - "actix-web-codegen 0.2.2", - "awc", - "bytes 0.5.6", - "derive_more", - "encoding_rs", - "futures", - "fxhash", - "log", - "mime", - "net2", - "pin-project 0.4.30", - "regex", - "serde", - "serde_json", - "serde_urlencoded 0.6.1", - "time 0.1.45", - "url", + "pin-project-lite", ] [[package]] name = "actix-web" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd3cb42f9566ab176e1ef0b8b3a896529062b4efc6be0123046095914c4c1c96" -dependencies = [ - "actix-codec 0.5.0", - "actix-http 3.3.1", - "actix-macros 0.2.3", - "actix-router 0.5.1", - "actix-rt 2.8.0", - "actix-server 2.2.0", - "actix-service 2.0.2", - "actix-utils 3.0.1", - "actix-web-codegen 4.2.0", - "ahash 0.7.6", - "bytes 1.4.0", +version = "4.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a597b77b5c6d6a1e1097fddde329a83665e25c5437c696a3a9a4aa514a614dea" +dependencies = [ + "actix-codec", + "actix-http", + "actix-macros", + "actix-router", + "actix-rt", + "actix-server", + "actix-service", + "actix-utils", + "actix-web-codegen", + "bytes", "bytestring", - "cfg-if 1.0.0", + "cfg-if", "cookie", "derive_more", "encoding_rs", + "foldhash", "futures-core", "futures-util", - "http", - "itoa 1.0.6", - "language-tags 0.3.2", - "log", - "mime", - "once_cell", - "pin-project-lite 0.2.9", - "regex", - "serde", - "serde_json", - "serde_urlencoded 0.7.1", - "smallvec", - "socket2 0.4.9", - "time 0.3.20", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a71bf475cbe07281d0b3696abb48212db118e7e23219f13596ce865235ff5766" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "actix-web-codegen" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2262160a7ae29e3415554a3f1fc04c764b1540c116aa524683208078b7a75bc9" -dependencies = [ - "actix-router 0.5.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "aead" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" -dependencies = [ - "crypto-common", - "generic-array", -] - -[[package]] -name = "aes" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "433cfd6710c9986c576a25ca913c39d66a6474107b406f34f91d4a8923395241" -dependencies = [ - "cfg-if 1.0.0", - "cipher", - "cpufeatures", -] - -[[package]] -name = "aes-gcm" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" -dependencies = [ - "aead", - "aes", - "cipher", - "ctr", - "ghash", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" -dependencies = [ - "getrandom 0.2.9", - "once_cell", - "version_check", -] - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if 1.0.0", - "getrandom 0.2.9", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "0.7.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" -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 = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-wincon", - "concolor-override", - "concolor-query", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2" - -[[package]] -name = "anstyle-parse" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-wincon" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa" -dependencies = [ - "anstyle", - "windows-sys 0.45.0", -] - -[[package]] -name = "anyhow" -version = "1.0.71" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -[[package]] -name = "async-trait" -version = "0.1.68" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ccdd8f2a161be9bd5c023df56f1b2a0bd1d83872ae53b71a84a12c9bf6e842" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.14", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "awc" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7601d4d1d7ef2335d6597a41b5fe069f6ab799b85f53565ab390e7b7065aac5" -dependencies = [ - "actix-codec 0.2.0", - "actix-http 1.0.1", - "actix-rt 1.1.1", - "actix-service 1.0.6", - "base64 0.11.0", - "bytes 0.5.6", - "derive_more", - "futures-core", - "log", - "mime", - "percent-encoding", - "rand 0.7.3", - "serde", - "serde_json", - "serde_urlencoded 0.6.1", -] - -[[package]] -name = "backoff" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" -dependencies = [ - "getrandom 0.2.9", - "instant", - "rand 0.8.5", -] - -[[package]] -name = "backtrace" -version = "0.3.67" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233d376d6d185f2a3093e58f283f60f880315b6c60075b01f36b3b85154564ca" -dependencies = [ - "addr2line", - "cc", - "cfg-if 1.0.0", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] - -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5" - -[[package]] -name = "base64" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" - -[[package]] -name = "bigdecimal" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" -dependencies = [ - "num-bigint", - "num-integer", - "num-traits", -] - -[[package]] -name = "bindgen" -version = "0.59.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" -dependencies = [ - "bitflags", - "cexpr", - "clang-sys", - "lazy_static", - "lazycell", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex 1.1.0", -] - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitvec" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - -[[package]] -name = "block-buffer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" -dependencies = [ - "generic-array", -] - -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -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 = "bufstream" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40e38929add23cdf8a366df9b0e088953150724bcbe5fc330b0d8eb3b328eec8" - -[[package]] -name = "bumpalo" -version = "3.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" - -[[package]] -name = "bytes" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89b2fd2a0dcf38d7971e2194b6b6eebab45ae01067456a7fd93d5547a61b70be" - -[[package]] -name = "bytestring" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238e4886760d98c4f899360c834fa93e62cf7f721ac3c2da375cbdf4b8679aae" -dependencies = [ - "bytes 1.4.0", -] - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cexpr" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "serde", - "time 0.1.45", - "wasm-bindgen", - "winapi 0.3.9", -] - -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common", - "inout", -] - -[[package]] -name = "clang-sys" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "4.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap_builder" -version = "4.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f" -dependencies = [ - "anstream", - "anstyle", - "bitflags", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.14", -] - -[[package]] -name = "clap_lex" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" - -[[package]] -name = "cmake" -version = "0.1.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" -dependencies = [ - "cc", -] - -[[package]] -name = "concolor-override" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f" - -[[package]] -name = "concolor-query" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf" -dependencies = [ - "windows-sys 0.45.0", -] - -[[package]] -name = "const_fn" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935" - -[[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 = [ - "aes-gcm", - "base64 0.20.0", - "hkdf", - "hmac 0.12.1", - "percent-encoding", - "rand 0.8.5", - "sha2 0.10.6", - "subtle", - "time 0.3.20", - "version_check", -] - -[[package]] -name = "copyless" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2df960f5d869b2dd8532793fde43eb5427cceb126c929747a26823ab0eeb536" - -[[package]] -name = "core-foundation" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "cpufeatures" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "280a9f2d8b3a38871a3c8a46fb80db65e5e5ed97da80c4d08bf27fb63e35e181" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crossbeam" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2801af0d36612ae591caa9568261fddce32ce6e08a7275ea334a06a4ad021a2c" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a33c2bf77f2df06183c3aa30d1e96c0695a313d4f9c453cc3762a6db39f99200" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695" -dependencies = [ - "autocfg", - "cfg-if 1.0.0", - "crossbeam-utils", - "memoffset", - "scopeguard", -] - -[[package]] -name = "crossbeam-queue" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1cfb3ea8a53f37c40dea2c7bedcbd88bdfae54f5e2175d6ecaff1c988353add" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "rand_core 0.6.4", - "typenum", -] - -[[package]] -name = "crypto-mac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" -dependencies = [ - "generic-array", - "subtle", -] - -[[package]] -name = "csv" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b015497079b9a9d69c02ad25de6c0a6edef051ea6360a327d0bd05802ef64ad" -dependencies = [ - "csv-core", - "itoa 1.0.6", - "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 = "ctr" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835" -dependencies = [ - "cipher", -] - -[[package]] -name = "darling" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn 1.0.109", -] - -[[package]] -name = "darling_macro" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" -dependencies = [ - "darling_core", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "data-encoding" -version = "2.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2e66c9d817f1720209181c316d28635c050fa304f9c79e47a520882661b7308" - -[[package]] -name = "dbwebconnect" -version = "0.1.0" -dependencies = [ - "actix-identity", - "actix-multipart", - "actix-session", - "actix-web 4.3.1", - "clap", - "csv", - "data-encoding", - "futures-util", - "log", - "mysql", - "mysql_common", - "rand 0.8.5", - "ring", - "rusoto_core", - "rusoto_s3", - "sanitize-filename", - "serde", - "serde_json", - "uuid", -] - -[[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 0.4.0", - "syn 1.0.109", -] - -[[package]] -name = "derive_utils" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff8f6a793f528719e1ad4425a52a213ac1214ac7158c5fb97a7f50a64bfc96d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.14", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array", -] - -[[package]] -name = "digest" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" -dependencies = [ - "block-buffer 0.10.4", - "crypto-common", - "subtle", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi 0.3.9", -] - -[[package]] -name = "discard" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dtoa" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" - -[[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.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071a31f4ee85403370b58aca746f01041ede6f0da2730960ad001edc2b71b394" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "enum-as-inner" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "570d109b813e904becc80d8d5da38376818a143348413f7149f1340fe04754d4" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "errno" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "failure" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "synstructure", -] - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] - -[[package]] -name = "flate2" -version = "1.0.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" -dependencies = [ - "crc32fast", - "libz-sys", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -[[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 = "fuchsia-zircon" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" -dependencies = [ - "bitflags", - "fuchsia-zircon-sys", -] - -[[package]] -name = "fuchsia-zircon-sys" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" - -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - -[[package]] -name = "futures" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-channel" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "955518d47e09b25bbebc7a18df10b81f0c766eaf4c4f1cccef2fca5f2a4fb5f2" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" - -[[package]] -name = "futures-executor" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" - -[[package]] -name = "futures-macro" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.14", -] - -[[package]] -name = "futures-sink" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f43be4fe21a13b9781a69afa4985b0f6ee0e1afab2c6f454a8cf30e2b2237b6e" - -[[package]] -name = "futures-task" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76d3d132be6c0e6aa1534069c705a74a5997a356c0dc2f86a47765e5617c5b65" - -[[package]] -name = "futures-util" -version = "0.3.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite 0.2.9", - "pin-utils", - "slab", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - -[[package]] -name = "getrandom" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "ghash" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" -dependencies = [ - "opaque-debug", - "polyval", -] - -[[package]] -name = "gimli" -version = "0.27.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0a93d233ebf96623465aad4046a8d3aa4da22d4f4beba5388838c8a434bbb4" - -[[package]] -name = "glob" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" - -[[package]] -name = "h2" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" -dependencies = [ - "bytes 0.5.6", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio 0.2.25", - "tokio-util 0.3.1", - "tracing", - "tracing-futures", -] - -[[package]] -name = "h2" -version = "0.3.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5be7b54589b581f624f566bf5d8eb2bab1db736c51528720b6bd36b96b55924d" -dependencies = [ - "bytes 1.4.0", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http", - "indexmap", - "slab", - "tokio 1.27.0", - "tokio-util 0.7.7", - "tracing", -] - -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hkdf" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791a029f6b9fc27657f6f188ec6e5e43f6911f6f878e0dc5501396e09809d437" -dependencies = [ - "hmac 0.12.1", -] - -[[package]] -name = "hmac" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" -dependencies = [ - "crypto-mac", - "digest 0.9.0", -] - -[[package]] -name = "hmac" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" -dependencies = [ - "digest 0.10.6", -] - -[[package]] -name = "hostname" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" -dependencies = [ - "libc", - "match_cfg", - "winapi 0.3.9", -] - -[[package]] -name = "http" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd6effc99afb63425aff9b05836f029929e345a6148a14b7ecd5ab67af944482" -dependencies = [ - "bytes 1.4.0", - "fnv", - "itoa 1.0.6", -] - -[[package]] -name = "http-body" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -dependencies = [ - "bytes 1.4.0", - "http", - "pin-project-lite 0.2.9", -] - -[[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 = "hyper" -version = "0.14.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc5e554ff619822309ffd57d8734d77cd5ce6238bc956f037ea06c58238c9899" -dependencies = [ - "bytes 1.4.0", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "httparse", - "httpdate", - "itoa 1.0.6", - "pin-project-lite 0.2.9", - "socket2 0.4.9", - "tokio 1.27.0", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper-tls" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" -dependencies = [ - "bytes 1.4.0", - "hyper", - "native-tls", - "tokio 1.27.0", - "tokio-native-tls", -] - -[[package]] -name = "iana-time-zone" -version = "0.1.56" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0722cd7114b7de04316e7ea5456a0bbb20e4adb46fd27a3697adb812cff0f37c" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[package]] -name = "idna" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[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 = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown", -] - -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if 1.0.0", -] - -[[package]] -name = "io-enum" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c662c349c9c9f542e7bfd9134143beb27da4b20dfbc3b3ef5b2a5b507dafbd" -dependencies = [ - "derive_utils", - "syn 2.0.14", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" -dependencies = [ - "hermit-abi 0.3.1", - "libc", - "windows-sys 0.48.0", -] - -[[package]] -name = "iovec" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" -dependencies = [ - "libc", -] - -[[package]] -name = "ipconfig" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7e2f18aece9709094573a9f24f483c4f65caa4298e2f7ae1b71cc65d853fad7" -dependencies = [ - "socket2 0.3.19", - "widestring", - "winapi 0.3.9", - "winreg", -] - -[[package]] -name = "is-terminal" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" -dependencies = [ - "hermit-abi 0.3.1", - "io-lifetimes", - "rustix", - "windows-sys 0.48.0", -] - -[[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.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -[[package]] -name = "jobserver" -version = "0.1.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936cfd212a0155903bcbc060e316fb6cc7cbf2e1907329391ebadc1fe0ce77c2" -dependencies = [ - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.61" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "language-tags" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" - -[[package]] -name = "language-tags" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" + "impl-more", + "itoa", + "language-tags", + "log", + "mime", + "once_cell", + "pin-project-lite", + "regex", + "regex-lite", + "serde", + "serde_json", + "serde_urlencoded", + "smallvec", + "socket2 0.5.10", + "time", + "tracing", + "url", +] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "actix-web-codegen" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "f591380e2e68490b5dfaf1dd1aa0ebe78d84ba7067078512b4ea6e4492d622b8" +dependencies = [ + "actix-router", + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "lazycell" -version = "1.3.0" +name = "addr2line" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] [[package]] -name = "lexical" -version = "6.1.1" +name = "adler2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7aefb36fd43fef7003334742cbf77b243fcd36418a1d1bdd480d613a67968f6" -dependencies = [ - "lexical-core", -] +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] -name = "lexical-core" -version = "0.8.5" +name = "aho-corasick" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", + "memchr", ] [[package]] -name = "lexical-parse-float" -version = "0.8.5" +name = "alloc-no-stdlib" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" -dependencies = [ - "lexical-parse-integer", - "lexical-util", - "static_assertions", -] +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" [[package]] -name = "lexical-parse-integer" -version = "0.8.6" +name = "alloc-stdlib" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" dependencies = [ - "lexical-util", - "static_assertions", + "alloc-no-stdlib", ] [[package]] -name = "lexical-util" -version = "0.8.5" +name = "android-tzdata" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" -dependencies = [ - "static_assertions", -] +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" [[package]] -name = "lexical-write-float" -version = "0.8.5" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ - "lexical-util", - "lexical-write-integer", - "static_assertions", + "libc", ] [[package]] -name = "lexical-write-integer" -version = "0.8.5" +name = "anstream" +version = "0.6.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" dependencies = [ - "lexical-util", - "static_assertions", + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", ] [[package]] -name = "libc" -version = "0.2.141" +name = "anstyle" +version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" [[package]] -name = "libloading" -version = "0.7.4" +name = "anstyle-parse" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ - "cfg-if 1.0.0", - "winapi 0.3.9", + "utf8parse", ] [[package]] -name = "libz-sys" -version = "1.1.8" +name = "anstyle-query" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9702761c3935f8cc2f101793272e202c72b99da8f4224a19ddcf1279a6450bbf" +checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" dependencies = [ - "cc", - "pkg-config", - "vcpkg", + "windows-sys 0.59.0", ] [[package]] -name = "linked-hash-map" -version = "0.5.6" +name = "anstyle-wincon" +version = "3.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f" +checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.59.0", +] [[package]] -name = "linux-raw-sys" -version = "0.3.1" +name = "autocfg" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] -name = "local-channel" -version = "0.1.3" +name = "backtrace" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f303ec0e94c6c54447f84f3b0ef7af769858a9c4ef56ef2a986d3dcd4c3fc9c" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ - "futures-core", - "futures-sink", - "futures-util", - "local-waker", + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", ] [[package]] -name = "local-waker" -version = "0.1.3" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e34f76eb3611940e0e7d53a9aaa4e6a3151f69541a282fd0dad5571420c53ff1" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "lock_api" -version = "0.4.9" +name = "bitflags" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" -dependencies = [ - "autocfg", - "scopeguard", -] +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" [[package]] -name = "log" -version = "0.4.17" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "cfg-if 1.0.0", + "generic-array", ] [[package]] -name = "lru" -version = "0.8.1" +name = "brotli" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" +checksum = "9991eea70ea4f293524138648e41ee89b0b2b12ddef3b255effa43c8056e0e0d" dependencies = [ - "hashbrown", + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", ] [[package]] -name = "lru-cache" -version = "0.1.2" +name = "brotli-decompressor" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +checksum = "874bb8112abecc98cbd6d81ea4fa7e94fb9449648c93cc89aa40c81c24d7de03" dependencies = [ - "linked-hash-map", + "alloc-no-stdlib", + "alloc-stdlib", ] [[package]] -name = "match_cfg" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" - -[[package]] -name = "matches" -version = "0.1.10" +name = "bumpalo" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2532096657941c2fea9c289d370a250971c689d4f143798ff67113ec042024a5" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] -name = "md5" -version = "0.7.0" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "490cc448043f947bae3cbee9c203358d62dbee0db12107a74be5c30ccfd09771" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "memchr" -version = "2.5.0" +name = "bytes" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] -name = "memoffset" -version = "0.8.0" +name = "bytestring" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +checksum = "e465647ae23b2823b0753f50decb2d5a86d2bb2cac04788fafd1f80e45378e5f" dependencies = [ - "autocfg", + "bytes", ] [[package]] -name = "mime" -version = "0.3.17" +name = "cc" +version = "1.2.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +dependencies = [ + "jobserver", + "libc", + "shlex", +] [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "cfg-if" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" [[package]] -name = "miniz_oxide" -version = "0.6.2" +name = "chrono" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ - "adler", + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", ] [[package]] -name = "mio" -version = "0.6.23" +name = "clap" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882" dependencies = [ - "cfg-if 0.1.10", - "fuchsia-zircon", - "fuchsia-zircon-sys", - "iovec", - "kernel32-sys", - "libc", - "log", - "miow", - "net2", - "slab", - "winapi 0.2.8", + "clap_builder", + "clap_derive", ] [[package]] -name = "mio" -version = "0.8.6" +name = "clap_builder" +version = "4.5.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b9d9a46eff5b4ff64b45a9e316a6d1e0bc719ef429cbec4dc630684212bfdf9" +checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966" dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.45.0", + "anstream", + "anstyle", + "clap_lex", + "strsim", ] [[package]] -name = "mio-uds" -version = "0.6.8" +name = "clap_derive" +version = "4.5.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491" dependencies = [ - "iovec", - "libc", - "mio 0.6.23", + "heck", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "miow" -version = "0.2.2" +name = "clap_lex" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" -dependencies = [ - "kernel32-sys", - "net2", - "winapi 0.2.8", - "ws2_32-sys", -] +checksum = "b94f61472cee1439c0b966b47e3aca9ae07e45d070759512cd390ea2bebc6675" [[package]] -name = "mysql" -version = "23.0.1" +name = "colorchoice" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f11339ca5c251941805d51362a07823605a80586ced92914ab7de84fba813f" -dependencies = [ - "bufstream", - "bytes 1.4.0", - "crossbeam", - "flate2", - "io-enum", - "libc", - "lru", - "mysql_common", - "named_pipe", - "once_cell", - "pem", - "percent-encoding", - "rustls", - "rustls-pemfile", - "serde", - "serde_json", - "socket2 0.4.9", - "twox-hash", - "url", - "webpki", - "webpki-roots", -] +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] -name = "mysql_common" -version = "0.29.2" +name = "cookie" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9006c95034ccf7b903d955f210469119f6c3477fc9c9e7a7845ce38a3e665c2a" +checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" dependencies = [ - "base64 0.13.1", - "bigdecimal", - "bindgen", - "bitflags", - "bitvec", - "byteorder", - "bytes 1.4.0", - "cc", - "cmake", - "crc32fast", - "flate2", - "lazy_static", - "lexical", - "num-bigint", - "num-traits", - "rand 0.8.5", - "regex", - "saturating", - "serde", - "serde_json", - "sha1 0.10.5", - "sha2 0.10.6", - "smallvec", - "subprocess", - "thiserror", - "time 0.3.20", - "uuid", + "percent-encoding", + "time", + "version_check", ] [[package]] -name = "named_pipe" -version = "0.4.1" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9c443cce91fc3e12f017290db75dde490d685cdaaf508d7159d7cf41f0eb2b" -dependencies = [ - "winapi 0.3.9", -] +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "native-tls" -version = "0.2.11" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ - "lazy_static", "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", ] [[package]] -name = "net2" -version = "0.2.38" +name = "crc32fast" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d0df99cfcd2530b2e694f6e17e7f37b8e26bb23983ac530c0c97408837c631" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ - "cfg-if 0.1.10", - "libc", - "winapi 0.3.9", + "cfg-if", ] [[package]] -name = "nom" -version = "7.1.3" +name = "crypto-common" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ - "memchr", - "minimal-lexical", + "generic-array", + "typenum", ] [[package]] -name = "num-bigint" -version = "0.4.3" +name = "csv" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f" +checksum = "acdc4883a9c96732e4733212c01447ebd805833b7275a73ca3ee080fd77afdaf" dependencies = [ - "autocfg", - "num-integer", - "num-traits", + "csv-core", + "itoa", + "ryu", + "serde", ] [[package]] -name = "num-integer" -version = "0.1.45" +name = "csv-core" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +checksum = "7d02f3b0da4c6504f86e9cd789d8dbafab48c2321be74e9987593de5a894d93d" dependencies = [ - "autocfg", - "num-traits", + "memchr", ] [[package]] -name = "num-traits" -version = "0.2.15" +name = "darling" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "autocfg", + "darling_core", + "darling_macro", ] [[package]] -name = "num_cpus" -version = "1.15.0" +name = "darling_core" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ - "hermit-abi 0.2.6", - "libc", + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", ] [[package]] -name = "object" -version = "0.30.3" +name = "darling_macro" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea86265d3d3dcb6a27fc51bd29a4bf387fae9d2986b823079d4986af253eb439" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ - "memchr", + "darling_core", + "quote", + "syn", ] [[package]] -name = "once_cell" -version = "1.17.1" +name = "data-encoding" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" +checksum = "2a2330da5de22e8a3cb63252ce2abb30116bf5265e89c0e01bc17015ce30a476" + +[[package]] +name = "dbwebconnect" +version = "0.1.0" +dependencies = [ + "actix-web", + "chrono", + "clap", + "csv", + "data-encoding", + "diesel", + "dotenvy", + "futures-util", + "log", + "rand 0.8.5", + "ring", + "sanitize-filename", + "serde", + "serde_json", + "tokio", + "uuid", +] [[package]] -name = "opaque-debug" -version = "0.3.0" +name = "deranged" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", +] [[package]] -name = "openssl" -version = "0.10.55" +name = "derive_more" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "345df152bc43501c5eb9e4654ff05f794effb78d4efe3d53abc158baddc0703d" +checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678" dependencies = [ - "bitflags", - "cfg-if 1.0.0", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", + "derive_more-impl", ] [[package]] -name = "openssl-macros" -version = "0.1.1" +name = "derive_more-impl" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn", + "unicode-xid", ] [[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.90" +name = "diesel" +version = "2.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "374533b0e45f3a7ced10fcaeccca020e66656bc03dac384f852e4e5a7a8104a6" +checksum = "229850a212cd9b84d4f0290ad9d294afc0ae70fccaa8949dbe8b43ffafa1e20c" dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "bitflags", + "byteorder", + "chrono", + "diesel_derives", + "itoa", + "pq-sys", ] [[package]] -name = "parking_lot" -version = "0.11.2" +name = "diesel_derives" +version = "2.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +checksum = "1b96984c469425cb577bf6f17121ecb3e4fe1e81de5d8f780dd372802858d756" dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "parking_lot" -version = "0.12.1" +name = "diesel_table_macro_syntax" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "lock_api", - "parking_lot_core 0.9.7", + "syn", ] [[package]] -name = "parking_lot_core" -version = "0.8.6" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "cfg-if 1.0.0", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi 0.3.9", + "block-buffer", + "crypto-common", ] [[package]] -name = "parking_lot_core" -version = "0.9.7" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "cfg-if 1.0.0", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "windows-sys 0.45.0", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "parse-size" -version = "1.0.0" +name = "dotenvy" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944553dd59c802559559161f9816429058b869003836120e262e8caec061b7ae" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] -name = "paste" -version = "1.0.12" +name = "dsl_auto_type" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f746c4065a8fa3fe23974dd82f15431cc8d40779821001404d10d2e79ca7d79" +checksum = "139ae9aca7527f85f26dd76483eb38533fd84bd571065da1739656ef71c5ff5b" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "peeking_take_while" -version = "0.1.2" +name = "either" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] -name = "pem" -version = "1.1.1" +name = "encoding_rs" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ - "base64 0.13.1", + "cfg-if", ] [[package]] -name = "percent-encoding" -version = "2.2.0" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "pin-project" -version = "0.4.30" +name = "flate2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef0f924a5ee7ea9cbcea77529dba45f8a9ba9f622419fe3386ca581a3ae9d5a" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ - "pin-project-internal 0.4.30", + "crc32fast", + "miniz_oxide", ] [[package]] -name = "pin-project" -version = "1.1.0" +name = "fnv" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c95a7476719eab1e366eaf73d0260af3021184f18177925b07f54b30089ceead" -dependencies = [ - "pin-project-internal 1.1.0", -] +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] -name = "pin-project-internal" -version = "0.4.30" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "851c8d0ce9bebe43790dedfc86614c23494ac9f423dd618d3a61fc693eafe61e" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] -name = "pin-project-internal" -version = "1.1.0" +name = "form_urlencoded" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39407670928234ebc5e6e580247dd567ad73a3578460c5990f9503df207e8f07" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.14", + "percent-encoding", ] [[package]] -name = "pin-project-lite" -version = "0.1.12" +name = "futures-core" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] -name = "pin-project-lite" -version = "0.2.9" +name = "futures-macro" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "pin-utils" -version = "0.1.0" +name = "futures-sink" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] -name = "pkg-config" -version = "0.3.26" +name = "futures-task" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] -name = "polyval" -version = "0.6.0" +name = "futures-util" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ef234e08c11dfcb2e56f79fd70f6f2eb7f025c0ce2333e82f4f0518ecad30c6" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "opaque-debug", - "universal-hash", + "futures-core", + "futures-macro", + "futures-task", + "pin-project-lite", + "pin-utils", + "slab", ] [[package]] -name = "ppv-lite86" -version = "0.2.17" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] [[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" +name = "getrandom" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.1+wasi-snapshot-preview1", +] [[package]] -name = "proc-macro2" -version = "1.0.56" +name = "getrandom" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ - "unicode-ident", + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] -name = "quick-error" -version = "1.2.3" +name = "gimli" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] -name = "quote" -version = "1.0.26" +name = "h2" +version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" dependencies = [ - "proc-macro2", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", ] [[package]] -name = "radium" -version = "0.7.0" +name = "hashbrown" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" +checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" [[package]] -name = "rand" -version = "0.7.3" +name = "heck" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom 0.1.16", - "libc", - "rand_chacha 0.2.2", - "rand_core 0.5.1", - "rand_hc", -] +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] -name = "rand" -version = "0.8.5" +name = "http" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", + "bytes", + "fnv", + "itoa", ] [[package]] -name = "rand_chacha" -version = "0.2.2" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core 0.5.1", -] +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "rand_chacha" -version = "0.3.1" +name = "httpdate" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] -name = "rand_core" -version = "0.5.1" +name = "iana-time-zone" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ - "getrandom 0.1.16", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "rand_core" -version = "0.6.4" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ - "getrandom 0.2.9", + "cc", ] [[package]] -name = "rand_hc" -version = "0.2.0" +name = "icu_collections" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" dependencies = [ - "rand_core 0.5.1", + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", ] [[package]] -name = "redis-async" -version = "0.13.0" +name = "icu_locale_core" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2777130e406c74c28b6cddc0194fcdc2553b5a8795eef9f6384bd3b70a07ba3f" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" dependencies = [ - "bytes 1.4.0", - "futures-channel", - "futures-sink", - "futures-util", - "log", - "tokio 1.27.0", - "tokio-util 0.7.7", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "icu_normalizer" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" dependencies = [ - "bitflags", + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", ] [[package]] -name = "redox_syscall" -version = "0.3.5" +name = "icu_normalizer_data" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags", -] +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" [[package]] -name = "redox_users" -version = "0.4.3" +name = "icu_properties" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" dependencies = [ - "getrandom 0.2.9", - "redox_syscall 0.2.16", - "thiserror", + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", ] [[package]] -name = "regex" -version = "1.7.3" +name = "icu_properties_data" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1f693b24f6ac912f4893ef08244d70b6067480d2f1a46e950c9691e6749d1d" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" [[package]] -name = "regex-syntax" -version = "0.6.29" +name = "icu_provider" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] [[package]] -name = "resolv-conf" -version = "0.6.3" +name = "ident_case" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11834e137f3b14e309437a8276714eed3a80d1ef894869e510f2c0c0b98b9f4a" -dependencies = [ - "hostname", - "quick-error", -] +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] -name = "ring" -version = "0.16.20" +name = "idna" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "cc", - "libc", - "once_cell", - "spin", - "untrusted", - "web-sys", - "winapi 0.3.9", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "rusoto_core" -version = "0.46.0" +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02aff20978970d47630f08de5f0d04799497818d16cafee5aec90c4b4d0806cf" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ - "async-trait", - "base64 0.13.1", - "bytes 1.4.0", - "crc32fast", - "futures", - "http", - "hyper", - "hyper-tls", - "lazy_static", - "log", - "rusoto_credential", - "rusoto_signature", - "rustc_version 0.2.3", - "serde", - "serde_json", - "tokio 1.27.0", - "xml-rs", + "icu_normalizer", + "icu_properties", ] [[package]] -name = "rusoto_credential" -version = "0.46.0" +name = "impl-more" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e91e4c25ea8bfa6247684ff635299015845113baaa93ba8169b9e565701b58e" -dependencies = [ - "async-trait", - "chrono", - "dirs-next", - "futures", - "hyper", - "serde", - "serde_json", - "shlex 0.1.1", - "tokio 1.27.0", - "zeroize", -] +checksum = "e8a5a9a0ff0086c7a148acb942baaabeadf9504d10400b5a05645853729b9cd2" [[package]] -name = "rusoto_s3" -version = "0.46.0" +name = "indexmap" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abc3f56f14ccf91f880b9a9c2d0556d8523e8c155041c54db155b384a1dd1119" +checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" dependencies = [ - "async-trait", - "bytes 1.4.0", - "futures", - "rusoto_core", - "xml-rs", + "equivalent", + "hashbrown", ] [[package]] -name = "rusoto_signature" -version = "0.46.0" +name = "io-uring" +version = "0.7.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5486e6b1673ab3e0ba1ded284fb444845fe1b7f41d13989a54dd60f62a7b2baa" +checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" dependencies = [ - "base64 0.13.1", - "bytes 1.4.0", - "futures", - "hex", - "hmac 0.10.1", - "http", - "hyper", - "log", - "md5", - "percent-encoding", - "pin-project-lite 0.2.9", - "rusoto_credential", - "rustc_version 0.2.3", - "serde", - "sha2 0.9.9", - "time 0.2.27", - "tokio 1.27.0", + "bitflags", + "cfg-if", + "libc", ] [[package]] -name = "rustc-demangle" -version = "0.1.23" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] -name = "rustc-hash" -version = "1.1.0" +name = "itoa" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] -name = "rustc_version" -version = "0.2.3" +name = "jobserver" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" dependencies = [ - "semver 0.9.0", + "getrandom 0.3.3", + "libc", ] [[package]] -name = "rustc_version" -version = "0.4.0" +name = "js-sys" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ - "semver 1.0.17", + "once_cell", + "wasm-bindgen", ] [[package]] -name = "rustix" -version = "0.37.11" +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.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" -dependencies = [ - "bitflags", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys", - "windows-sys 0.48.0", -] +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] -name = "rustls" -version = "0.20.8" +name = "libc" +version = "0.2.174" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fff78fc74d175294f4e83b28343315ffcfb114b156f0185e9741cb5570f50e2f" -dependencies = [ - "log", - "ring", - "sct", - "webpki", -] +checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] -name = "rustls-pemfile" -version = "1.0.2" +name = "local-channel" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d194b56d58803a43635bdc398cd17e383d6f71f9182b9a192c127ca42494a59b" +checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" dependencies = [ - "base64 0.21.0", + "futures-core", + "futures-sink", + "local-waker", ] [[package]] -name = "ryu" -version = "1.0.13" +name = "local-waker" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" [[package]] -name = "sanitize-filename" -version = "0.4.0" +name = "lock_api" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c502bdb638f1396509467cb0580ef3b29aa2a45c5d43e5d84928241280296c" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ - "lazy_static", - "regex", + "autocfg", + "scopeguard", ] [[package]] -name = "saturating" -version = "0.1.0" +name = "log" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ece8e78b2f38ec51c51f5d475df0a7187ba5111b2a28bdc761ee05b075d40a71" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] -name = "schannel" -version = "0.1.21" +name = "memchr" +version = "2.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713cfb06c7059f3588fb8044c0fad1d09e3c01d225e25b9220dbfdcf16dbb1b3" -dependencies = [ - "windows-sys 0.42.0", -] +checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] -name = "scopeguard" -version = "1.1.0" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "sct" -version = "0.7.0" +name = "miniz_oxide" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "ring", - "untrusted", + "adler2", ] [[package]] -name = "security-framework" -version = "2.9.1" +name = "mio" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", "libc", - "security-framework-sys", + "log", + "wasi 0.11.1+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] -name = "security-framework-sys" -version = "2.9.0" +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-traits" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ - "core-foundation-sys", - "libc", + "autocfg", ] [[package]] -name = "semver" -version = "0.9.0" +name = "object" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ - "semver-parser", + "memchr", ] [[package]] -name = "semver" -version = "1.0.17" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] -name = "semver-parser" -version = "0.7.0" +name = "once_cell_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" [[package]] -name = "serde" -version = "1.0.160" +name = "parking_lot" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ - "serde_derive", + "lock_api", + "parking_lot_core", ] [[package]] -name = "serde_derive" -version = "1.0.160" +name = "parking_lot_core" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291a097c63d8497e00160b166a967a4a79c64f3facdd01cbd7502231688d77df" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.14", + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", ] [[package]] -name = "serde_json" -version = "1.0.96" +name = "percent-encoding" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" -dependencies = [ - "itoa 1.0.6", - "ryu", - "serde", -] +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] -name = "serde_plain" -version = "1.0.1" +name = "pin-project-lite" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6018081315db179d0ce57b1fe4b62a12a0028c9cf9bbef868c9cf477b3c34ae" -dependencies = [ - "serde", -] +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] -name = "serde_urlencoded" -version = "0.6.1" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec5d77e2d4c73717816afac02670d5c4f534ea95ed430442cad02e7a6e32c97" -dependencies = [ - "dtoa", - "itoa 0.4.8", - "serde", - "url", -] +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "serde_urlencoded" -version = "0.7.1" +name = "pkg-config" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa 1.0.6", - "ryu", - "serde", -] +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] -name = "sha1" -version = "0.6.1" +name = "potential_utf" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" dependencies = [ - "sha1_smol", + "zerovec", ] [[package]] -name = "sha1" -version = "0.10.5" +name = "powerfmt" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3" -dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.6", -] +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] -name = "sha1_smol" -version = "1.0.0" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] -name = "sha2" -version = "0.9.9" +name = "pq-sys" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800" +checksum = "dfd6cf44cca8f9624bc19df234fc4112873432f5fda1caff174527846d026fa9" dependencies = [ - "block-buffer 0.9.0", - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.9.0", - "opaque-debug", + "libc", + "vcpkg", ] [[package]] -name = "sha2" -version = "0.10.6" +name = "proc-macro2" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ - "cfg-if 1.0.0", - "cpufeatures", - "digest 0.10.6", + "unicode-ident", ] [[package]] -name = "shlex" -version = "0.1.1" +name = "quote" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] [[package]] -name = "shlex" -version = "1.1.0" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "signal-hook-registry" -version = "1.4.1" +name = "rand" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", ] [[package]] -name = "slab" -version = "0.4.8" +name = "rand" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "autocfg", + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] -name = "smallvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" - -[[package]] -name = "socket2" -version = "0.3.19" +name = "rand_chacha" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi 0.3.9", + "ppv-lite86", + "rand_core 0.6.4", ] [[package]] -name = "socket2" -version = "0.4.9" +name = "rand_chacha" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64a4a911eed85daf18834cfaa86a79b7d266ff93ff5ba14005426219480ed662" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ - "libc", - "winapi 0.3.9", + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "standback" -version = "0.2.17" +name = "rand_core" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "version_check", + "getrandom 0.2.16", ] [[package]] -name = "static_assertions" -version = "1.1.0" +name = "rand_core" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", +] [[package]] -name = "stdweb" -version = "0.4.20" +name = "redox_syscall" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", + "bitflags", ] [[package]] -name = "stdweb-derive" -version = "0.5.3" +name = "regex" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "stdweb-internal-macros" -version = "0.2.9" +name = "regex-automata" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1 0.6.1", - "syn 1.0.109", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" +name = "regex-lite" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" +checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" [[package]] -name = "strsim" -version = "0.10.0" +name = "regex-syntax" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] -name = "subprocess" -version = "0.2.9" +name = "ring" +version = "0.16.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2e86926081dda636c546d8c5e641661049d7562a68f5488be4a1f7f66f6086" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" dependencies = [ + "cc", "libc", - "winapi 0.3.9", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi", ] [[package]] -name = "subtle" -version = "2.4.1" +name = "rustc-demangle" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] -name = "syn" -version = "1.0.109" +name = "rustversion" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] -name = "syn" -version = "2.0.14" +name = "ryu" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcf316d5356ed6847742d036f8a39c3b8435cac10bd528a4bd461928a6ab34d5" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] -name = "synstructure" -version = "0.12.6" +name = "sanitize-filename" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +checksum = "08c502bdb638f1396509467cb0580ef3b29aa2a45c5d43e5d84928241280296c" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", - "unicode-xid", + "lazy_static", + "regex", ] [[package]] -name = "tap" -version = "1.0.1" +name = "scopeguard" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "tempfile" -version = "3.5.0" +name = "serde" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9fbec84f381d5795b08656e4912bec604d162bff9291d6189a78f4c8ab87998" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ - "cfg-if 1.0.0", - "fastrand", - "redox_syscall 0.3.5", - "rustix", - "windows-sys 0.45.0", + "serde_derive", ] [[package]] -name = "thiserror" -version = "1.0.40" +name = "serde_derive" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "thiserror-impl" -version = "1.0.40" +name = "serde_json" +version = "1.0.142" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f" +checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.14", + "itoa", + "memchr", + "ryu", + "serde", ] [[package]] -name = "threadpool" -version = "1.8.1" +name = "serde_urlencoded" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" dependencies = [ - "num_cpus", + "form_urlencoded", + "itoa", + "ryu", + "serde", ] [[package]] -name = "time" -version = "0.1.45" +name = "sha1" +version = "0.10.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "time" -version = "0.2.27" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros 0.1.1", - "version_check", - "winapi 0.3.9", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "time" -version = "0.3.20" +name = "signal-hook-registry" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd0cbfecb4d19b5ea75bb31ad904eb5b9fa13f21079c3b92017ebdf4999a5890" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ - "itoa 1.0.6", - "serde", - "time-core", - "time-macros 0.2.8", + "libc", ] [[package]] -name = "time-core" -version = "0.1.0" +name = "slab" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e153e1f1acaef8acc537e68b44906d2db6436e2b35ac2c6b42640fff91f00fd" +checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" [[package]] -name = "time-macros" -version = "0.1.1" +name = "smallvec" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "time-macros" -version = "0.2.8" +name = "socket2" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd80a657e71da814b8e5d60d3374fc6d35045062245d80224748ae522dd76f36" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ - "time-core", + "libc", + "windows-sys 0.52.0", ] [[package]] -name = "time-macros-impl" -version = "0.1.2" +name = "socket2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", + "libc", + "windows-sys 0.59.0", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "spin" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" -dependencies = [ - "tinyvec_macros", -] +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" [[package]] -name = "tinyvec_macros" -version = "0.1.1" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] -name = "tokio" -version = "0.2.25" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "iovec", - "lazy_static", - "libc", - "memchr", - "mio 0.6.23", - "mio-uds", - "pin-project-lite 0.1.12", - "signal-hook-registry", - "slab", - "winapi 0.3.9", -] +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] -name = "tokio" -version = "1.27.0" +name = "syn" +version = "2.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0de47a4eecbe11f498978a9b29d792f0d2692d1dd003650c24c76510e3bc001" +checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" dependencies = [ - "autocfg", - "bytes 1.4.0", - "libc", - "mio 0.8.6", - "num_cpus", - "parking_lot 0.12.1", - "pin-project-lite 0.2.9", - "signal-hook-registry", - "socket2 0.4.9", - "tokio-macros", - "windows-sys 0.45.0", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "tokio-macros" -version = "2.0.0" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61a573bdc87985e9d6ddeed1b3d864e8a302c847e40d647746df2f1de209d1ce" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.14", + "syn", ] [[package]] -name = "tokio-native-tls" -version = "0.3.1" +name = "time" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ - "native-tls", - "tokio 1.27.0", + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", ] [[package]] -name = "tokio-util" -version = "0.2.0" +name = "time-core" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" -dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.1.12", - "tokio 0.2.25", -] +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] -name = "tokio-util" -version = "0.3.1" +name = "time-macros" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ - "bytes 0.5.6", - "futures-core", - "futures-sink", - "log", - "pin-project-lite 0.1.12", - "tokio 0.2.25", + "num-conv", + "time-core", ] [[package]] -name = "tokio-util" -version = "0.7.7" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5427d89453009325de0d8f342c9490009f76e999cb7672d77e46267448f7e6b2" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" dependencies = [ - "bytes 1.4.0", - "futures-core", - "futures-sink", - "pin-project-lite 0.2.9", - "tokio 1.27.0", - "tracing", + "displaydoc", + "zerovec", ] [[package]] -name = "tower-service" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" - -[[package]] -name = "tracing" -version = "0.1.37" +name = "tokio" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ - "cfg-if 1.0.0", - "log", - "pin-project-lite 0.2.9", - "tracing-core", + "backtrace", + "bytes", + "io-uring", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "slab", + "socket2 0.6.0", + "tokio-macros", + "windows-sys 0.59.0", ] [[package]] -name = "tracing-core" -version = "0.1.30" +name = "tokio-macros" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ - "once_cell", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "tracing-futures" -version = "0.2.5" +name = "tokio-util" +version = "0.7.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +checksum = "14307c986784f72ef81c89db7d9e28d6ac26d16213b109ea501696195e6e3ce5" dependencies = [ - "pin-project 1.1.0", - "tracing", + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", ] [[package]] -name = "trust-dns-proto" -version = "0.18.0-alpha.2" +name = "tracing" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a7f3a2ab8a919f5eca52a468866a67ed7d3efa265d48a652a9a3452272b413f" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ - "async-trait", - "enum-as-inner", - "failure", - "futures", - "idna 0.2.3", - "lazy_static", "log", - "rand 0.7.3", - "smallvec", - "socket2 0.3.19", - "tokio 0.2.25", - "url", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "trust-dns-resolver" -version = "0.18.0-alpha.2" +name = "tracing-attributes" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f90b1502b226f8b2514c6d5b37bafa8c200d7ca4102d57dc36ee0f3b7a04a2f" +checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ - "cfg-if 0.1.10", - "failure", - "futures", - "ipconfig", - "lazy_static", - "log", - "lru-cache", - "resolv-conf", - "smallvec", - "tokio 0.2.25", - "trust-dns-proto", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "try-lock" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" - -[[package]] -name = "twox-hash" -version = "1.6.3" +name = "tracing-core" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" +checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" dependencies = [ - "cfg-if 1.0.0", - "rand 0.8.5", - "static_assertions", + "once_cell", ] [[package]] name = "typenum" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba" - -[[package]] -name = "unicode-bidi" -version = "0.3.13" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "unicode-ident" -version = "1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" - -[[package]] -name = "unicode-normalization" -version = "0.1.22" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-xid" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" - -[[package]] -name = "universal-hash" -version = "0.5.1" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" -dependencies = [ - "crypto-common", - "subtle", -] +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "untrusted" @@ -3894,28 +1744,36 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" [[package]] name = "url" -version = "2.3.1" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", - "idna 0.3.0", + "idna", "percent-encoding", ] +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.3.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b55a3fef2a1e3b3a00ce878640918820d3c51081576ac657d23af9fc7928fdb" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom 0.2.9", + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", ] [[package]] @@ -3926,67 +1784,56 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] [[package]] name = "wasm-bindgen" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ - "cfg-if 1.0.0", + "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3994,64 +1841,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.84" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "web-sys" -version = "0.3.61" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33b99f4b23ba3eec1a53ac264e35a755f00e966e0065077d6027c0f575b0b97" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", ] -[[package]] -name = "webpki" -version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f095d78192e208183081cc07bc5515ef55216397af48b873e5edcd72637fa1bd" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "webpki-roots" -version = "0.22.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c71e40d7d2c34a5106301fb632274ca37242cd0c9d3e64dbece371a40a2d87" -dependencies = [ - "webpki", -] - -[[package]] -name = "widestring" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c168940144dd21fd8046987c16a46a33d5fc84eec29ef9dcddc2ac9e31526b7c" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - [[package]] name = "winapi" version = "0.3.9" @@ -4062,12 +1881,6 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -4081,227 +1894,283 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "windows" -version = "0.48.0" +name = "windows-core" +version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-targets 0.48.0", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", ] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-implement" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-interface" +version = "0.59.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ - "windows-targets 0.42.2", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-result" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-targets 0.48.0", + "windows-link", ] [[package]] -name = "windows-targets" -version = "0.42.2" +name = "windows-strings" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-link", ] [[package]] -name = "windows-targets" -version = "0.48.0" +name = "windows-sys" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows_aarch64_gnullvm 0.48.0", - "windows_aarch64_msvc 0.48.0", - "windows_i686_gnu 0.48.0", - "windows_i686_msvc 0.48.0", - "windows_x86_64_gnu 0.48.0", - "windows_x86_64_gnullvm 0.48.0", - "windows_x86_64_msvc 0.48.0", + "windows-targets", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" +name = "windows-sys" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" +name = "windows-targets" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" +name = "windows_aarch64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" -version = "0.48.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] -name = "windows_i686_gnu" -version = "0.48.0" +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" -version = "0.42.2" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] -name = "windows_i686_msvc" -version = "0.48.0" +name = "windows_x86_64_gnu" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" +name = "windows_x86_64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" +name = "windows_x86_64_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" +name = "writeable" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" +name = "yoke" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" +name = "yoke-derive" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] -name = "winreg" -version = "0.6.2" +name = "zerofrom" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ - "winapi 0.3.9", + "zerofrom-derive", ] [[package]] -name = "ws2_32-sys" -version = "0.2.1" +name = "zerofrom-derive" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ - "winapi 0.2.8", - "winapi-build", + "proc-macro2", + "quote", + "syn", + "synstructure", ] [[package]] -name = "wyz" -version = "0.5.1" +name = "zerotrie" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" dependencies = [ - "tap", + "displaydoc", + "yoke", + "zerofrom", ] [[package]] -name = "xml-rs" -version = "0.8.14" +name = "zerovec" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] [[package]] -name = "zeroize" -version = "1.6.0" +name = "zerovec-derive" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0956f1ba7c7909bfb66c2e9e4124ab6f6482560f6628b5aaeba39207c9aad9" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "7.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" dependencies = [ - "libc", "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.8+zstd.1.5.5" +version = "2.0.15+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5556e6ee25d32df2586c098bbfa278803692a20d0ab9565e049480d52707ec8c" +checksum = "eb81183ddd97d0c74cedf1d50d85c8d08c1b8b68ee863bdee9e706eedba1a237" dependencies = [ "cc", - "libc", "pkg-config", ] diff --git a/Cargo.toml b/Cargo.toml index ae1540d..aaa0ad8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,16 +6,14 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -actix-web = "4.0.0-beta.8" -actix-session = {version="0.7.2",features = ["redis-actor-session"]} -actix-identity = "0.2.0" -serde = { version = "1.0.130", features = ["derive"] } -serde_json = "1.0.68" -mysql = { version = "*", default-features = false, features = ["minimal", "rustls-tls"] } -mysql_common = { version = "*", default-features = false, features = ["bigdecimal03", "time03", "uuid"]} -csv="1.1" +actix-web = "4" +#mysql = "24.0.0" +chrono = "0.4.40" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.142" +dotenvy = "0.15.7" +csv="1.3" clap = { version = "4.0", features = ["derive"] } -actix-multipart = "0.6.0" sanitize-filename = "0.4" uuid = { version = "1", features = ["v4"] } futures-util = "0.3.28" @@ -23,5 +21,6 @@ log = "0.4.17" rand = "0.8.4" ring = "0.16.20" data-encoding = "2.3.0" -rusoto_s3 = "0.46.0" -rusoto_core = "0.46.0" +diesel = { version = "2.2.9", features = ["postgres", "chrono"] } +tokio = { version = "1", features = ["full"] } + diff --git a/README.md b/README.md new file mode 100644 index 0000000..4a14771 --- /dev/null +++ b/README.md @@ -0,0 +1,124 @@ +# dbwebconnect + +This project provides a web interface and tooling for working with databases. It is now configured to use Diesel with PostgreSQL. + +## Requirements + +- Rust toolchain (e.g. via `rustup`) +- Docker and Docker Compose + +## First-Time Setup + +1. **Build the Rust application (optional sanity check)** + + ```bash + cargo build + ``` + +2. **Start PostgreSQL and initialize schemas/tables** + + From the project root: + + ```bash + docker compose up -d db + ``` + + On first startup, the `db` service will: + + - Create the `api_main` database (via `POSTGRES_DB`). + - Run all SQL files in `sql-scripts` inside `/docker-entrypoint-initdb.d`. + - Execute `sql-scripts/initapi.sql` to create the following in `api_main`: + - Schema `"ApiKey"` with table `apikeys`. + - Schema `"Relationships"` with table `relationships`. + +3. **Verify the database (optional)** + + ```bash + docker logs hosted_database + ``` + + If you have `psql` available: + + ```bash + docker exec -it hosted_database psql -U api_user -d api_main + ``` + + Inside `psql` you can run, for example: + + ```sql + \dn + \dt "ApiKey".* + \dt "Relationships".* + ``` + +4. **Start the application container** + + From the project root: + + ```bash + docker compose up -d app + ``` + + The `app` service is configured with: + + - `DATABASE_URL=postgres://api_user:secret@db:5432/api_main` + + and will connect to the `db` service using Diesel. + +5. **Access the application** + + Once the `app` container is running, the HTTP server listens on port `8080`: + + - http://localhost:8080/ + + (See `src/main.rs` for the available routes.) + +## Subsequent Runs + +For later sessions (after the first initialization): + +- Start both services: + + ```bash + docker compose up -d + ``` + +- Stop everything: + + ```bash + docker compose down + ``` + +Because the Postgres data directory is mounted from `./data`, your schemas, tables, and data are preserved across container restarts. + +## Local Development (without Docker app container) + +If you prefer to run the Rust binary directly on your host while still using the Dockerized Postgres: + +1. Ensure the `db` container is running: + + ```bash + docker compose up -d db + ``` + +2. Export a matching `DATABASE_URL` that points to `localhost` instead of `db`: + + ```bash + export DATABASE_URL=postgres://api_user:secret@localhost:5432/api_main + ``` + +3. Run the application: + + ```bash + cargo run + ``` + +The server will again be available on http://localhost:8080/. + +## Architecture & Migration Notes + +- The project was originally built against MariaDB/MySQL using the `mysql` crate. +- It has been migrated to PostgreSQL using Diesel (with the `postgres` feature) and a `PgConnection`-based connection layer. +- A single physical Postgres database (`api_main`) is used; logical "databases" are represented as schemas (for example, `"ApiKey"` and `"Relationships"`). +- The Rust code obtains a connection via a type alias `PooledConn = PgConnection` and reads its connection settings from the `DATABASE_URL` environment variable. +- Most queries are still expressed as raw SQL strings and executed via `diesel::sql_query(...)`, with lightweight structs implementing `QueryableByName` for mapping result rows. diff --git a/docker-compose.yml b/docker-compose.yml index d6ef773..1e5f629 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,26 @@ -version: '1' +version: '3.8' services: db: - image: mariadb + image: postgres:16-alpine + container_name: hosted_database + restart: unless-stopped environment: - MYSQL_ROOT_PASSWORD: secret - volumes: - - ./data:/var/lib/mysql + - POSTGRES_USER=api_user + - POSTGRES_PASSWORD=secret + - POSTGRES_DB=api_main ports: - - "3306:3306" + - "5432:5432" + volumes: + - ./data:/var/lib/postgresql/data + - ./sql-scripts:/docker-entrypoint-initdb.d app: - build: . + build: ./ ports: - - "8000:8000" + - "8080:8080" depends_on: - db environment: - PWD: secret \ No newline at end of file + - DATABASE_URL=postgres://api_user:secret@db:5432/api_main + - PWD=secret diff --git a/sql-scripts/initapi.sql b/sql-scripts/initapi.sql new file mode 100644 index 0000000..1ce7f60 --- /dev/null +++ b/sql-scripts/initapi.sql @@ -0,0 +1,28 @@ +-- Initialize core schemas and tables for Postgres + +-- In Postgres we treat logical databases as schemas. The main +-- database is created by POSTGRES_DB, so we only need schemas +-- and tables here. + +-- Schema and table for API keys +CREATE SCHEMA IF NOT EXISTS "ApiKey"; + +CREATE TABLE IF NOT EXISTS "ApiKey".apikeys ( + "INTERNAL_PRIMARY_KEY" SERIAL PRIMARY KEY, + databaseuser VARCHAR(100), + databasepasshash VARCHAR(100), + apikey VARCHAR(255) +); + +-- Schema and table for relationships metadata +CREATE SCHEMA IF NOT EXISTS "Relationships"; + +CREATE TABLE IF NOT EXISTS "Relationships".relationships ( + "INTERNAL_PRIMARY_KEY" SERIAL PRIMARY KEY, + targeted_database VARCHAR(100), + parent_table VARCHAR(100), + child_table VARCHAR(100), + where_clause VARCHAR(255), + relationship VARCHAR(100) UNIQUE +); + diff --git a/src/connkey.rs b/src/connkey.rs index c950cec..12596e0 100644 --- a/src/connkey.rs +++ b/src/connkey.rs @@ -5,7 +5,8 @@ //4. if apikey not found return error use data_encoding::HEXUPPER; -use mysql::prelude::Queryable; +use diesel::prelude::*; +use diesel::sql_query; use ring::digest::{Context, SHA256}; use crate::dbconnect; @@ -37,12 +38,18 @@ impl ApiKey { pub fn execute_apikey(stmt: &str) -> Result> { let mut conn = dbconnect::internalqueryconnapikey(); - let mut keyvec: Vec = Vec::new(); - let _ = conn.query_map(stmt, |apikey| { - keyvec.push(apikey); - })?; + #[derive(QueryableByName)] + struct ApiKeyRow { + #[diesel(sql_type = diesel::sql_types::Text)] + apikey: String, + } - Ok(keyvec[0].clone()) + let rows: Vec = sql_query(stmt).load(&mut conn)?; + if rows.is_empty() { + Err("No API key found".into()) + } else { + Ok(rows[0].apikey.clone()) + } } pub fn generate_apikey(database: &String) -> Result> { @@ -53,35 +60,37 @@ pub fn generate_apikey(database: &String) -> Result Result> { let mut conn = dbconnect::internalqueryconnapikey(); - let mut stmt = String::from("SELECT apikey FROM ApiKey.apikeys WHERE databaseuser= '"); + let mut stmt = String::from("SELECT apikey FROM apikeys WHERE databaseuser= '"); stmt.push_str(&database); stmt.push_str("'"); - let mut keyvec: Vec = Vec::new(); - - let _ = conn.query_map(stmt, |apikey| { - keyvec.push(apikey); - })?; + #[derive(QueryableByName)] + struct ApiKeyRow { + #[diesel(sql_type = diesel::sql_types::Text)] + apikey: String, + } - if apikey == keyvec[0] { - return Ok(true); + let rows: Vec = sql_query(stmt).load(&mut conn)?; + if rows.is_empty() { + Ok(false) } else { - return Ok(false); + Ok(apikey == rows[0].apikey) } } pub fn search_apikey_admin(apikey: &str) -> Result> { let mut conn = dbconnect::internalqueryconnapikey(); let stmt = String::from("SELECT apikey FROM apikeys WHERE databaseuser= 'root'"); - let mut keyvec: Vec = Vec::new(); - - let _ = conn.query_map(stmt, |apikey| { - keyvec.push(apikey); - })?; + #[derive(QueryableByName)] + struct ApiKeyRow { + #[diesel(sql_type = diesel::sql_types::Text)] + apikey: String, + } - if apikey == keyvec[0] { - return Ok(true); + let rows: Vec = sql_query(stmt).load(&mut conn)?; + if rows.is_empty() { + Ok(false) } else { - return Ok(false); + Ok(apikey == rows[0].apikey) } } @@ -100,7 +109,7 @@ pub fn insert_apikey(database: String, hash: String) -> Result Result, Box String { - //let mut conn = mysql::Conn::new("mysql://kylelocal:kcb@127.0.0.1:3306/").unwrap(); let mut conn = dbconnect::internalqueryconn(); let dbname = String::from(database); - //let mut conn=dbconnect::database_connection_no_db_web(database_user,database_password,database_host,port); let hash = connkey::random_password(); - //let query = format!("CREATE DATABASE IF NOT EXISTS {}", dbname); - let mut query = String::from("CREATE DATABASE IF NOT EXISTS "); + let mut query = String::from("CREATE SCHEMA IF NOT EXISTS "); query.push_str(&dbname); query.push_str(";"); - query.push_str("CREATE USER IF NOT EXISTS "); - query.push_str(&dbname); - query.push_str("@'%' IDENTIFIED BY '"); - query.push_str(&hash); - query.push_str("';"); - query.push_str("GRANT ALL PRIVILEGES ON "); - query.push_str(&dbname); - query.push_str(".* TO "); - query.push_str(&dbname); - query.push_str("@'%';"); - query.push_str("FLUSH PRIVILEGES;"); - conn.query_drop(query).unwrap(); + sql_query(query) + .execute(&mut conn) + .expect("Failed to create schema"); let apikey = connkey::insert_apikey(dbname.to_string(), hash); apikey.unwrap() diff --git a/src/createrecord.rs b/src/createrecord.rs index 21e37f0..39a295c 100644 --- a/src/createrecord.rs +++ b/src/createrecord.rs @@ -1,9 +1,9 @@ //creates single csv record to be written to file // use crate::pushdata::gettablecol; +use crate::PooledConn; use csv::Writer; -use mysql::*; -pub mod generateform; +//pub mod generateform; pub fn create_session_csv( conn: &mut PooledConn, table: &str, diff --git a/src/createrelationship.rs b/src/createrelationship.rs index 0adaca6..ca4b823 100644 --- a/src/createrelationship.rs +++ b/src/createrelationship.rs @@ -1,4 +1,5 @@ -use mysql::prelude::Queryable; +use diesel::prelude::*; +use diesel::sql_query; use crate::dbconnect; use crate::Reader; @@ -38,16 +39,15 @@ fn createrelationship(file: String) -> String { } String::from("Unable to create relationship") } - -pub fn commitrelationship(database: &str, file: String) -> Result, mysql::Error> { - //pub fn commitrelationship(database: &str,table1:&str, col1:&str, table2:&str, - //col2:&str,ondelete:&str, onupdate:&str)->Result, mysql::Error>{ +pub fn commitrelationship( + database: &str, + file: String, +) -> Result<(), diesel::result::Error> { let relation = createrelationship(file); - //let relation=createrelationship_fromhtml(table1, col1, table2, col2, ondelete, onupdate); let mut conn = dbconnect::database_connection(database); - let result: Vec = conn.query(relation)?; - return Ok(result); + sql_query(relation).execute(&mut conn)?; + Ok(()) } pub fn commitrelationshipdefined( @@ -58,13 +58,13 @@ pub fn commitrelationshipdefined( col2: &str, ondelete: &str, onupdate: &str, -) -> Result, mysql::Error> { +) -> Result<(), diesel::result::Error> { let relation = createrelationship_fromhtml(table1, col1, table2, col2, ondelete, onupdate); println!("{}", relation); let mut conn = dbconnect::database_connection(database); - let result: Vec = conn.query(relation)?; - return Ok(result); + sql_query(relation).execute(&mut conn)?; + Ok(()) } fn createrelationship_fromhtml( @@ -153,9 +153,9 @@ pub fn createrelationshipfromweb(database: &str, json: Vec<(String, String)>) -> println!("{}", query); return query; } -pub fn commitrelationshipfromweb(statement: String) -> Result { +pub fn commitrelationshipfromweb(statement: String) -> Result { let mut conn = dbconnect::internalqueryconn(); - conn.query_drop(statement)?; + sql_query(statement).execute(&mut conn)?; - return Ok(String::from("Success")); + Ok(String::from("Success")) } diff --git a/src/dbconnect.rs b/src/dbconnect.rs index db6d7f7..33166fe 100644 --- a/src/dbconnect.rs +++ b/src/dbconnect.rs @@ -1,53 +1,25 @@ -//use crate::Reader; use crate::LinkDataBase; use crate::PooledConn; use csv::ReaderBuilder; -use mysql::Pool; -pub fn database_connection(database: &str) -> PooledConn { - let form = grabfromfile(); - let mut url = String::new(); - url.push_str("mysql://"); - url.push_str(&form.dbuser); - url.push_str(":"); - url.push_str(&form.dbpass); - url.push_str("@"); - url.push_str(&form.dbhost); - url.push_str(":"); - url.push_str(&form.dbport); - url.push_str("/"); - println!("{}", url); +use diesel::pg::PgConnection; +use diesel::prelude::*; - let url = format!("{}{}", url, database); - let url = url.as_str(); - let pool = Pool::new(url).unwrap(); - let conn = pool.get_conn().unwrap(); - conn +// For Postgres/Diesel we maintain a single physical database and use +// the `database` argument only as a logical schema/table prefix inside SQL. +// All connections are created from a DATABASE_URL environment variable. +pub fn database_connection(_database: &str) -> PooledConn { + internalqueryconn() } pub fn database_connection_no_db_web( - dbuser: &str, - dbpassword: &str, - dbport: &str, - dbhost: &str, + _dbuser: &str, + _dbpassword: &str, + _dbport: &str, + _dbhost: &str, ) -> PooledConn { - let mut url = String::new(); - url.push_str("mysql://"); - url.push_str(&dbuser); - url.push_str(":"); - url.push_str(&dbpassword); - url.push_str("@"); - url.push_str(&dbhost); - url.push_str(":"); - url.push_str(&dbport); - url.push_str("/"); - println!("{}", url); - let url = url.as_str(); - let pool = Pool::new(url).unwrap(); - let conn = pool.get_conn().unwrap(); - return conn; + internalqueryconn() } pub fn database_connection_no_db() -> PooledConn { - let conn = internalqueryconn(); - return conn; + internalqueryconn() } fn grabfromfile() -> LinkDataBase { //igneroe header @@ -71,32 +43,14 @@ fn grabfromfile() -> LinkDataBase { form.dbhost = record[2].to_string(); form.dbport = record[3].to_string(); } - println!("grabfromfile"); - println!("{:?}", form); form } pub fn internalqueryconn() -> PooledConn { - let pword = std::env::args().nth(2).expect("no password given"); - //change - //let url=String::from("mysql://root:secret@localhost:3306/"); - let mut url = String::from("mysql://root:"); - url.push_str(&pword); - url.push_str("@localhost:3306/"); - let url = url.as_str(); - let pool = Pool::new(url).unwrap(); - let conn = pool.get_conn().unwrap(); - return conn; + let database_url = std::env::var("DATABASE_URL") + .expect("DATABASE_URL must be set for Postgres connection"); + PgConnection::establish(&database_url).expect("Failed to connect to Postgres") } pub fn internalqueryconnapikey() -> PooledConn { - //change - let pword = std::env::args().nth(2).expect("no password given"); - //let url=String::from("mysql://root:secret@localhost:3306/ApiKey"); - let mut url = String::from("mysql://root:"); - url.push_str(&pword); - url.push_str("@localhost:3306/ApiKey"); - let url = url.as_str(); - let pool = Pool::new(url).unwrap(); - let conn = pool.get_conn().unwrap(); - return conn; + internalqueryconn() } diff --git a/src/delete.rs b/src/delete.rs index 7fd1fbc..664ad03 100644 --- a/src/delete.rs +++ b/src/delete.rs @@ -1,5 +1,12 @@ -use mysql::{prelude::Queryable, *}; -pub fn deleterecord(database: &str, table: &str, id: Vec<(String, String)>) -> Result { +use diesel::prelude::*; +use diesel::sql_query; +use crate::PooledConn; + +pub fn deleterecord( + database: &str, + table: &str, + id: Vec<(String, String)>, +) -> std::result::Result { //grab second string from tuple let mut stmt = String::from("DELETE FROM "); stmt.push_str(database); @@ -19,7 +26,7 @@ pub fn deleterecord(database: &str, table: &str, id: Vec<(String, String)>) -> R Ok(stmt) } -pub fn droptable(database: &str, table: &str) -> Result { +pub fn droptable(database: &str, table: &str) -> std::result::Result { //grab second string from tuple let mut stmt = String::from("DROP TABLE "); stmt.push_str(database); @@ -28,13 +35,15 @@ pub fn droptable(database: &str, table: &str) -> Result { println!("{}", stmt); Ok(stmt) } -pub fn exec_statement(conn: &mut PooledConn, stmt: &str) -> Result { +pub fn exec_statement(conn: &mut PooledConn, stmt: &str) -> std::result::Result { //grab second string from tuple println!("{}", stmt); - conn.query_drop(stmt).unwrap(); + sql_query(stmt) + .execute(conn) + .expect("Failed to execute DELETE/DROP statement"); Ok(String::from("Executed")) } -pub fn generate_backup(database: &str, table: &str) -> Result { +pub fn generate_backup(database: &str, table: &str) -> std::result::Result { //grab second string from tuple let mut stmt = String::from("SELECT * INTO OUTFILE '/tmp/"); stmt.push_str(database); diff --git a/src/insertrecords.rs b/src/insertrecords.rs index 83f1b79..7974ac1 100644 --- a/src/insertrecords.rs +++ b/src/insertrecords.rs @@ -1,14 +1,10 @@ -//accept json to insert record into database -//use serde to deserialize json to struct -//use mysql to insert record into database - use crate::dbconnect; use crate::pushdata::gettablecol; use crate::querytable; -use mysql; -use mysql::prelude::Queryable; use serde::{Deserialize, Serialize}; use serde_json::Result; +use diesel::prelude::*; +use diesel::sql_query; #[derive(Serialize, Deserialize, Debug)] pub struct TableDef { @@ -177,6 +173,8 @@ pub fn insert_attachment( } pub fn exec_insert(statement: String) -> Result { let mut conn = dbconnect::internalqueryconn(); - conn.query_drop(statement).unwrap(); + sql_query(statement) + .execute(&mut conn) + .expect("Insert failed"); Ok(String::from("Success")) } diff --git a/src/main.rs b/src/main.rs index 4dbf316..dc8273a 100755 --- a/src/main.rs +++ b/src/main.rs @@ -1,10 +1,9 @@ -use actix_multipart::form::tempfile::TempFileConfig; -use actix_session::{storage::RedisActorSessionStore, SessionMiddleware}; use actix_web::{cookie, web, App, HttpResponse, HttpServer, Responder}; use clap::Parser; use csv::Reader; use data_encoding::BASE64; -use mysql::*; +use diesel::pg::PgConnection; +use diesel::prelude::*; use serde::{Deserialize, Serialize}; use serde_json::Value; pub mod connkey; @@ -21,6 +20,9 @@ pub mod querytable; pub mod relationships; pub mod tablecreate; pub mod update; + +// Global database connection type, backed by Diesel Postgres +pub type PooledConn = PgConnection; //use rusoto_s3::*; //use mysql::prelude::*; //use crate::createrecord::generateform::CreateRelation; @@ -35,23 +37,25 @@ async fn main() { args.push_str(":8080"); //let pword=std::env::args().nth(2).unwrap(); - let secretkey = cookie::Key::generate(); + //let secretkey = cookie:: let redisconnection = String::from("127.0.0.1:6379"); let server = HttpServer::new(move || { + //App::new() + // .wrap(SessionMiddleware::new( + // RedisActorSessionStore::new(&redisconnection), + // secretkey.clone(), + // )) App::new() - .wrap(SessionMiddleware::new( - RedisActorSessionStore::new(&redisconnection), - secretkey.clone(), - )) //session cookie - .app_data(TempFileConfig::default().directory("./tmp")) + //.app_data(TempFileConfig::default().directory("./tmp")) //force users to start at / before going to /main .service( web::resource("/") .route(web::get().to(getinitializeconnect)) .route(web::post().to(postinitializeconnect)), ) + .route("/health", web::get().to(health)) //.route("/main", web::get().to(index)) //.route("/auth", web::post().to(auth)) .route("/getkey/{database}&apikey={apikey}", web::get().to(getkey)) @@ -172,6 +176,50 @@ async fn getinitializeconnect() -> impl Responder { .content_type("text/html; charset=utf-8") .body(html) } +async fn health() -> impl Responder { + let db_url = std::env::var("DATABASE_URL"); + + match db_url { + Ok(url) => match PgConnection::establish(&url) { + Ok(_) => { + let info = serde_json::json!({ + "status": "ok", + "database": "postgres", + "connection": "ok", + "logical_model": "single_db_with_schemas", + }); + + HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body(info.to_string()) + } + Err(e) => { + let info = serde_json::json!({ + "status": "error", + "database": "postgres", + "connection": "failed", + "message": e.to_string(), + }); + + HttpResponse::InternalServerError() + .content_type("application/json; charset=utf-8") + .body(info.to_string()) + } + }, + Err(_) => { + let info = serde_json::json!({ + "status": "error", + "database": "postgres", + "connection": "failed", + "message": "DATABASE_URL not set", + }); + + HttpResponse::InternalServerError() + .content_type("application/json; charset=utf-8") + .body(info.to_string()) + } + } +} //async fn getcreaterelation() -> impl Responder { // let html = createrecord::generateform::getcreaterelationshipdefined(); // HttpResponse::Ok().body(html) diff --git a/src/pushdata.rs b/src/pushdata.rs index 051d221..ad8dde5 100644 --- a/src/pushdata.rs +++ b/src/pushdata.rs @@ -1,7 +1,9 @@ use crate::Data2; +use crate::PooledConn; //use crate::Data; -use mysql::prelude::*; -use mysql::*; +use diesel::prelude::*; + + pub mod createtablestruct; pub mod gettablecol; @@ -18,8 +20,9 @@ fn execute_insert2( let insertstatement = gettablecol::createinsertstatement(&mut conn, &tablename, data, &database); println!("{}", insertstatement); - - let _ = conn.query_drop(insertstatement); + diesel::sql_query(insertstatement) + .execute(&mut conn) + .expect("Bulk insert failed"); println!("Inserted data into table"); Ok(()) } diff --git a/src/pushdata/gettablecol.rs b/src/pushdata/gettablecol.rs index 7ca2706..78750ff 100644 --- a/src/pushdata/gettablecol.rs +++ b/src/pushdata/gettablecol.rs @@ -1,6 +1,9 @@ use crate::Data2; -use mysql::prelude::*; -use mysql::*; +use crate::PooledConn; +use diesel::prelude::*; +use diesel::sql_query; +use diesel::sql_types::Text; +use diesel::QueryableByName; pub fn get_table_col( conn: &mut PooledConn, table_name: &str, @@ -18,10 +21,14 @@ pub fn get_table_col( querystring.push_str(" and COLUMN_NAME != 'X_COORD'"); querystring.push_str(" and COLUMN_NAME != 'Y_COORD'"); querystring.push_str(" and COLUMN_NAME != 'Attachment'"); - //let columnname = conn.query_map("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='testcsv' AND TABLE_NAME='Data'", |(COLUMN_NAME)| COLUMN_NAME)?; - let columnname = conn.query_map(querystring, |column_name: String| column_name.to_string())?; + #[derive(QueryableByName)] + struct ColName { + #[diesel(sql_type = Text)] + column_name: String, + } - Ok(columnname) + let rows: Vec = sql_query(querystring).load(conn)?; + Ok(rows.into_iter().map(|r| r.column_name).collect()) } pub fn createinsertstatement( diff --git a/src/querytable.rs b/src/querytable.rs index 3735aaf..b576dda 100644 --- a/src/querytable.rs +++ b/src/querytable.rs @@ -1,5 +1,8 @@ -use mysql::prelude::*; -use mysql::*; +use crate::PooledConn; +use diesel::prelude::*; +use diesel::sql_query; +use diesel::sql_types::Text; +use diesel::QueryableByName; use serde_json::json; //use crate::{connkey, dbconnect}; @@ -32,19 +35,36 @@ pub fn exec_map( conn: &mut PooledConn, query: &str, ) -> std::result::Result, Box> { - let stmt: Vec = conn.query_map_opt(query, |data| data.unwrap_or("".to_string()))?; - - // + #[derive(QueryableByName)] + struct SingleString { + #[diesel(sql_type = Text)] + #[allow(non_snake_case)] + COLUMN_NAME: String, + } - Ok(stmt) + let rows: Vec = sql_query(query).load(conn)?; + Ok(rows.into_iter().map(|r| r.COLUMN_NAME).collect()) } pub fn exec_map_tuple( conn: &mut PooledConn, query: &str, ) -> std::result::Result, Box> { - let stmt: Vec<(String, String)> = conn.query_map(query, |(data1, data2)| (data1, data2))?; - Ok(stmt) + #[derive(QueryableByName)] + struct TwoStrings { + #[diesel(sql_type = Text)] + #[allow(non_snake_case)] + COLUMN_NAME: String, + #[diesel(sql_type = Text)] + #[allow(non_snake_case)] + CONSTRAINT_NAME: String, + } + + let rows: Vec = sql_query(query).load(conn)?; + Ok(rows + .into_iter() + .map(|r| (r.COLUMN_NAME, r.CONSTRAINT_NAME)) + .collect()) } pub fn grab_columntypes( table: &str, @@ -189,25 +209,30 @@ fn query_table( // })?; //?? let mut stmt = Vec::new(); - for i in 0..columntypes.len() { + for col in columntypes.iter() { let mut query = String::from("SELECT "); - query.push_str(&columntypes[i]); - query.push_str(" FROM "); + // Cast to text so Diesel can load everything as String + query.push_str(col); + query.push_str("::text AS value FROM "); query.push_str(database); query.push_str("."); query.push_str(table); - if whereclause != "" { + if !whereclause.is_empty() { query.push_str(" WHERE "); query.push_str(whereclause); } - let row = conn - .query_map(query.clone(), |columntypes: String| columntypes) - .unwrap(); - stmt.push(row); - query.clear(); + #[derive(QueryableByName)] + struct ValueRow { + #[diesel(sql_type = Text)] + value: String, + } + + let rows: Vec = sql_query(query.clone()).load(conn)?; + let row_vals: Vec = rows.into_iter().map(|r| r.value).collect(); + stmt.push(row_vals); } - //let mut fixedstmt:Vec>=vec![&columntypes.len(), &stmt.len()]; + Ok(stmt) } fn deconstruct_where(whereclause: &str) -> (String, String) { @@ -364,8 +389,15 @@ pub fn exec_grab_tablenames( conn: &mut PooledConn, query: &str, ) -> std::result::Result, Box> { - let stmt: Vec = conn.query_map(query, |data| data)?; - Ok(stmt) + #[derive(QueryableByName)] + struct SingleStringRow { + #[diesel(sql_type = Text)] + #[allow(non_snake_case)] + TABLE_NAME: String, + } + + let rows: Vec = sql_query(query).load(conn)?; + Ok(rows.into_iter().map(|r| r.TABLE_NAME).collect()) } pub fn json_table_names(queryresult: Vec, database: &str) -> serde_json::Value { @@ -463,35 +495,44 @@ fn exec_query_unique_relationships( parent_table: &str, _database: &str, ) -> std::result::Result, Box> { - //create stmt that is a Vec> - //let count_stmt: Vec = conn.query_map(query, |data| data)?; - //let countstmt = count_unique_relationships(parent_table).unwrap(); - //let count = exec_count_unique_relationships(conn, &countstmt).unwrap()[0].parse::().unwrap(); - - //let unique_child_tables = grab_child_unique_relationships(parent_table).unwrap(); - //let unique_child_tables = exec_count_unique_relationships(conn, &unique_child_tables).unwrap(); - let relationshiptablecolumns = vec![ - "TARGETED_DATABASE".to_string(), - "parent_table".to_string(), - "child_table".to_string(), - "where_clause".to_string(), - ]; - let mut vectors: Vec> = Vec::new(); - for column in relationshiptablecolumns.iter() { - let mut query = String::from("SELECT "); - query.push_str(column); - query.push_str(" FROM Relationships.relationships WHERE parent_table = '"); - query.push_str(parent_table); - query.push_str("'"); - let stmt: Vec = conn.query_map(&query, |data| data)?; - vectors.push(stmt); + #[derive(QueryableByName)] + struct RelRow { + #[diesel(sql_type = Text)] + #[allow(non_snake_case)] + TARGETED_DATABASE: String, + #[diesel(sql_type = Text)] + parent_table: String, + #[diesel(sql_type = Text)] + child_table: String, + #[diesel(sql_type = Text)] + where_clause: String, } - //put - //let mut unique_relation: Vec = Vec::new(); - let relation = create_uniques(vectors).unwrap(); + let mut query = String::from( + "SELECT TARGETED_DATABASE, parent_table, child_table, where_clause FROM Relationships.relationships WHERE parent_table = '", + ); + query.push_str(parent_table); + query.push_str("'"); - Ok(relation) + let rows: Vec = sql_query(&query).load(conn)?; + + let uniques = rows + .into_iter() + .map(|r| { + let split: Vec<&str> = r.where_clause.split('=').collect(); + let parent_column = split.get(0).unwrap_or(&"").to_string(); + let child_column = split.get(1).unwrap_or(&"").to_string(); + UniqueRelation::new( + &r.TARGETED_DATABASE, + &r.parent_table, + &parent_column, + &r.child_table, + &child_column, + ) + }) + .collect(); + + Ok(uniques) } pub fn initialize_db_table( diff --git a/src/relationships.rs b/src/relationships.rs index cd7dd81..60862cb 100644 --- a/src/relationships.rs +++ b/src/relationships.rs @@ -1,6 +1,7 @@ +use crate::PooledConn; +use diesel::prelude::*; +use diesel::sql_query; use serde::{Deserialize, Serialize}; -//use serde_json::Result; -use mysql::{*, prelude::Queryable}; #[derive(Serialize,Debug, Clone, Deserialize)] pub struct RelationshipBuilder{ @@ -30,7 +31,16 @@ impl RelationshipBuilder{ } pub fn check_relationship_name(&self, conn: &mut PooledConn)->bool{ let stmt = format!("SELECT relationship FROM Relationships.relationships WHERE relationship='{}'", self.relationship_name); - let result:Vec = conn.query_map(stmt, |relationship| relationship).unwrap(); + + #[derive(QueryableByName)] + struct RelRow { + #[diesel(sql_type = diesel::sql_types::Text)] + relationship: String, + } + + let result: Vec = sql_query(stmt) + .load(conn) + .expect("Failed to query relationships"); if result.len() > 0{ println!("Relationship Name Already Exists"); return false; @@ -49,15 +59,42 @@ pub fn create_relationship_stmt(relationship: &RelationshipBuilder) -> String{ stmt } pub fn execute_relationship_stmt(stmt: &str, conn: &mut PooledConn){ - let _=conn.query_drop(stmt); + let _ = sql_query(stmt) + .execute(conn) + .expect("Failed to execute relationship insert"); } pub fn query_relationships(conn: &mut PooledConn, relationship_name:&str)->Vec{ let mut stmt = String::from("SELECT targeted_database, parent_table, child_table, where_clause, relationship FROM Relationships.relationships"); stmt.push_str(" WHERE relationship='"); stmt.push_str(relationship_name); stmt.push_str("'"); - - let result:Vec = conn.query_map(stmt, |(targeted_database, parent_table, child_table, where_clause, relationship)| RelationshipBuilder{database: targeted_database, parent_table, child_table, where_clause, relationship_name: relationship}).unwrap(); - result + #[derive(QueryableByName)] + struct RelFullRow { + #[diesel(sql_type = diesel::sql_types::Text)] + targeted_database: String, + #[diesel(sql_type = diesel::sql_types::Text)] + parent_table: String, + #[diesel(sql_type = diesel::sql_types::Text)] + child_table: String, + #[diesel(sql_type = diesel::sql_types::Text)] + where_clause: String, + #[diesel(sql_type = diesel::sql_types::Text)] + relationship: String, + } + + let rows: Vec = sql_query(stmt) + .load(conn) + .expect("Failed to query relationship rows"); + + rows + .into_iter() + .map(|r| RelationshipBuilder { + database: r.targeted_database, + parent_table: r.parent_table, + child_table: r.child_table, + where_clause: r.where_clause, + relationship_name: r.relationship, + }) + .collect() } diff --git a/src/tablecreate.rs b/src/tablecreate.rs index 3e509f2..0c2f63a 100644 --- a/src/tablecreate.rs +++ b/src/tablecreate.rs @@ -1,9 +1,12 @@ -use mysql::prelude::*; -use mysql::*; -//read from csv file to create table in mariadb with given column names +use diesel::prelude::*; +use diesel::sql_query; +use crate::PooledConn; +//read from csv file to create table in postgres with given column names pub fn exec_statement(conn: &mut PooledConn, statement: &str) { - conn.query_drop(statement).unwrap(); + sql_query(statement) + .execute(conn) + .expect("Failed to execute DDL statement"); } pub fn create_table( conn: &mut PooledConn, @@ -29,7 +32,9 @@ pub fn create_table( query.pop(); query.push_str(")"); println!("{}", query); - conn.query_drop(query).unwrap(); + sql_query(query) + .execute(conn) + .expect("Failed to create table"); } pub fn create_table_web( database: &str, @@ -42,7 +47,7 @@ pub fn create_table_web( query.push_str("."); query.push_str(table_name); query.push_str(" ("); - query.push_str("INTERNAL_PRIMARY_KEY INT NOT NULL AUTO_INCREMENT PRIMARY KEY, "); + query.push_str("INTERNAL_PRIMARY_KEY SERIAL PRIMARY KEY, "); for i in 0..column_names.len() { let valid = validate_unprotected_term(column_names[i].0.as_str()); if valid.0 == false { @@ -81,12 +86,12 @@ pub fn create_table_web_gps(database: &str, table_name: &str) -> String { query.push_str(table_name); query.push_str("_GPS"); query.push_str(" ("); - query.push_str("INTERNAL_PRIMARY_KEY INT NOT NULL AUTO_INCREMENT PRIMARY KEY, "); + query.push_str("INTERNAL_PRIMARY_KEY SERIAL PRIMARY KEY, "); query.push_str("MAIN_TABLE_ID INT, "); query.push_str("GPS_ID INT, "); query.push_str("X_COORD VARCHAR(100), "); query.push_str("Y_COORD VARCHAR(100), "); - query.push_str("Attachment BLOB, "); + query.push_str("Attachment BYTEA, "); //for i in 0..column_names.len() { // let valid=validate_unprotected_term(column_names[i].1.as_str()); // if valid.0==false{ diff --git a/src/update.rs b/src/update.rs index 08b62a7..e14edd0 100644 --- a/src/update.rs +++ b/src/update.rs @@ -1,4 +1,6 @@ -use mysql::{prelude::Queryable, *}; +use diesel::prelude::*; +use diesel::sql_query; +use crate::PooledConn; pub fn updaterecord(database: &str, table: &str, date: Vec>) -> Vec { let mut stmts = Vec::new(); for data in date.iter() { @@ -29,8 +31,13 @@ pub fn updaterecord(database: &str, table: &str, date: Vec stmts } -pub fn executeupdaterecord(conn: &mut PooledConn, statement: &str) -> Result { - conn.query_drop(statement).unwrap(); +pub fn executeupdaterecord( + conn: &mut PooledConn, + statement: &str, +) -> std::result::Result { + sql_query(statement) + .execute(conn) + .expect("Failed to execute UPDATE statement"); Ok(String::from("Success")) } From 869f1ce71b8a9c682239f171186b0ddc9eaf72a4 Mon Sep 17 00:00:00 2001 From: Kyle Baumgarten Date: Sun, 25 Jan 2026 22:15:36 -0500 Subject: [PATCH 2/3] updates to postgres migration --- .dockerignore | 4 + .gitignore | 1 + Cargo.lock | 167 ++++++++++- Cargo.toml | 2 + Dockerfile | 2 +- ENDPOINTS.md | 521 ++++++++++++++++++++++++++++++++ README.md | 165 +++++++++++ docker-compose.yml | 11 +- sql-scripts/initapi.sql | 11 + src/auth.rs | 379 ++++++++++++++++++++++++ src/connkey.rs | 4 +- src/createdatabase.rs | 14 +- src/insertrecords.rs | 76 +---- src/main.rs | 573 +++++++++++++++++++----------------- src/pushdata/gettablecol.rs | 14 +- src/querytable.rs | 45 +-- src/tablecreate.rs | 28 +- src/update.rs | 45 ++- tests/api_flow.rs | 172 +++++++++++ 19 files changed, 1826 insertions(+), 408 deletions(-) create mode 100644 .dockerignore create mode 100644 ENDPOINTS.md create mode 100644 src/auth.rs create mode 100644 tests/api_flow.rs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b8a34fd --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +data + target + .git + .github diff --git a/.gitignore b/.gitignore index 1286264..d2af89d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ Cargo.lock .vscode .idea .docs/ +.env diff --git a/Cargo.lock b/Cargo.lock index 7fe6e4a..aba7400 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -289,6 +289,18 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "argon2" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072" +dependencies = [ + "base64ct", + "blake2", + "cpufeatures", + "password-hash", +] + [[package]] name = "autocfg" version = "1.5.0" @@ -316,12 +328,27 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + [[package]] name = "bitflags" version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "blake2" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +dependencies = [ + "digest", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -568,6 +595,7 @@ name = "dbwebconnect" version = "0.1.0" dependencies = [ "actix-web", + "argon2", "chrono", "clap", "csv", @@ -575,9 +603,10 @@ dependencies = [ "diesel", "dotenvy", "futures-util", + "jsonwebtoken", "log", "rand 0.8.5", - "ring", + "ring 0.16.20", "sanitize-filename", "serde", "serde_json", @@ -659,6 +688,7 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", "crypto-common", + "subtle", ] [[package]] @@ -804,8 +834,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi 0.11.1+wasi-snapshot-preview1", + "wasm-bindgen", ] [[package]] @@ -1076,6 +1108,21 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "jsonwebtoken" +version = "9.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a87cc7a48537badeae96744432de36f4be2b4a34a05a5ef32e9dd8a1c169dde" +dependencies = [ + "base64", + "js-sys", + "pem", + "ring 0.17.14", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "language-tags" version = "0.3.2" @@ -1166,12 +1213,31 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1225,6 +1291,27 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "password-hash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166" +dependencies = [ + "base64ct", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64", + "serde_core", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1420,11 +1507,25 @@ dependencies = [ "libc", "once_cell", "spin", - "untrusted", + "untrusted 0.7.1", "web-sys", "winapi", ] +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted 0.9.0", + "windows-sys 0.52.0", +] + [[package]] name = "rustc-demangle" version = "0.1.26" @@ -1461,18 +1562,28 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -1529,6 +1640,18 @@ dependencies = [ "libc", ] +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + [[package]] name = "slab" version = "0.4.10" @@ -1579,6 +1702,12 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "2.0.104" @@ -1601,6 +1730,26 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.41" @@ -1742,6 +1891,12 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.4" diff --git a/Cargo.toml b/Cargo.toml index aaa0ad8..883613b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,6 @@ ring = "0.16.20" data-encoding = "2.3.0" diesel = { version = "2.2.9", features = ["postgres", "chrono"] } tokio = { version = "1", features = ["full"] } +jsonwebtoken = "9" +argon2 = "0.5" diff --git a/Dockerfile b/Dockerfile index 167784c..a7ed652 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,4 +20,4 @@ COPY . . RUN cargo build --release EXPOSE 8080 # Set the entrypoint -ENTRYPOINT [ "./target/release/dbwebconnect" , "localhost", "$PWD"] \ No newline at end of file +ENTRYPOINT [ "./target/release/dbwebconnect" , "0.0.0.0", "$PWD"] \ No newline at end of file diff --git a/ENDPOINTS.md b/ENDPOINTS.md new file mode 100644 index 0000000..66fe42c --- /dev/null +++ b/ENDPOINTS.md @@ -0,0 +1,521 @@ +# API Endpoints Overview + +This document summarizes the HTTP endpoints exposed by `dbwebconnect`, their purpose, authentication and expected behavior. + +All examples assume the server is running on `http://localhost:8080`. + +- Authentication: JWT via `Authorization: Bearer ` for most routes +- Logical databases: implemented as Postgres schemas (e.g. `my_schema`) +- Legacy API-key path parameters exist for backward compatibility but are ignored + +--- + +## Auth & Administration + +### `POST /auth/register` +- **Auth**: none +- **Body (JSON)**: + - `email: string` + - `password: string` +- **Behavior**: + - Creates a new user in `"Auth".users` with an Argon2-hashed password. + - First user becomes `admin`, subsequent users default to `user`. +- **Response**: + - `200 OK` with JSON containing a message / basic user info. + +**Example** + +```bash +curl -X POST \ + http://localhost:8080/auth/register \ + -H "Content-Type: application/json" \ + -d '{"email":"admin@test.local","password":"Password123!"}' +``` + +### `POST /auth/login` +- **Auth**: none +- **Body (JSON)**: + - `email: string` + - `password: string` +- **Behavior**: + - Verifies credentials and issues a JWT (HS256) if valid. +- **Response**: + - `200 OK` with `{ "token": "" }`. + +**Example** + +```bash +curl -X POST \ + http://localhost:8080/auth/login \ + -H "Content-Type: application/json" \ + -d '{"email":"admin@test.local","password":"Password123!"}' +``` + +### `GET /admin/users` +- **Auth**: `admin` role required +- **Behavior**: + - Lists registered users and their roles/allowed_schemas. +- **Response**: + - `200 OK` JSON array of user objects. + +**Example** + +```bash +curl -X GET \ + http://localhost:8080/admin/users \ + -H "Authorization: Bearer $TOKEN" +``` + +### `POST /admin/users/schemas` +- **Auth**: `admin` role required +- **Body (JSON)**: + - typically `{ "email": "...", "allowed_schemas": "schema1,schema2" }` +- **Behavior**: + - Updates which logical schemas a user may access. +- **Response**: + - `200 OK` on success. + +**Example** + +```bash +curl -X POST \ + http://localhost:8080/admin/users/schemas \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"email":"user@test.local","allowed_schemas":"my_schema,other_schema"}' +``` + +### `GET /health` +- **Auth**: none +- **Behavior**: + - Checks `DATABASE_URL` and attempts to open a Postgres connection. +- **Response**: + - `200 OK` with `{ "status": "ok", ... }` on success. + - `500` JSON with error info if DB or env is misconfigured. + +**Example** + +```bash +curl -X GET http://localhost:8080/health +``` + +--- + +## Initialization & Legacy + +### `GET /` +- **Auth**: none +- **Behavior**: + - Returns an HTML page from `initconnect::getpagehtml()` (legacy UI entrypoint). +- **Response**: + - `200 OK` HTML. + +**Example** + +```bash +curl -X GET http://localhost:8080/ +``` + +### `POST /` +- **Auth**: none +- **Behavior**: + - Legacy API-key based initializer, now deprecated. +- **Response**: + - `410 Gone` with `{ "error": "deprecated_endpoint_use_jwt_auth" }`. + +**Example** + +```bash +curl -X POST http://localhost:8080/ -d 'deprecated' +``` + +### `GET /getkey/{database}&apikey={apikey}` +- **Auth**: none +- **Behavior**: + - Legacy API-key issuance, now deprecated in favor of JWT. +- **Response**: + - `410 Gone` with `{ "error": "deprecated_endpoint_use_jwt_auth" }`. + +**Example** + +```bash +curl -X GET "http://localhost:8080/getkey/my_schema&apikey=ignored" +``` + +--- + +## Logical Database (Schema) Management + +### `POST /createdatabase/{database}&apikey={apikey}` +- **Auth**: `admin` role required +- **Path Params**: + - `database`: logical database name (Postgres schema) + - `apikey`: ignored +- **Behavior**: + - Executes `CREATE SCHEMA IF NOT EXISTS `. +- **Response**: + - `200 OK` with a base64-encoded string of `"ok"` (for legacy compatibility). + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/createdatabase/my_schema&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" +``` + +--- + +## Table DDL + +### `POST /createtable/{database}&table={table}&gps={gps}&apikey={apikey}` +- **Auth**: `admin` role required +- **Path Params**: + - `database`: schema name + - `table`: table name + - `gps`: `true`/`false` – whether to also create a `{table}_GPS` attachment table + - `apikey`: ignored +- **Body (JSON)**: + - `columns`: stringified JSON list of `{ "name:" }` + - `types`: stringified JSON list of `{ "type:" }` +- **Behavior**: + - Creates `database.table` with an `INTERNAL_PRIMARY_KEY SERIAL PRIMARY KEY` and given columns. + - Adds defaults for certain types (e.g. `VARCHAR` → `DEFAULT ''`, `INT` → `DEFAULT 0`). + - If `gps=true`, also creates `database.table_GPS` with GPS/attachment columns. +- **Response**: + - `200 OK` with `"Table Created"` on success. + - `200 OK` with `"Invalid column name: ..."` if validation fails. + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/createtable/my_schema&table=my_table&gps=false&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "columns": "[{\"name:id\"},{\"name:name\"}]", + "types": "[{\"type:INT\"},{\"type:VARCHAR(255)\"}]" + }' +``` + +### `POST /droptable/{database}&table={table}&apikey={apikey}` +- **Auth**: `admin` role required +- **Path Params**: + - `database`: schema name + - `table`: table name + - `apikey`: ignored +- **Body (JSON)**: + - A single boolean field, e.g. `{ "backup": true }`. +- **Behavior**: + - If `backup=true`, generates and executes a backup statement. + - Drops `database.table` via `DROP TABLE`. +- **Response**: + - `200 OK` with `"Table Dropped"`. + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/droptable/my_schema&table=my_table&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"backup": false}' +``` + +--- + +## Insert / Update / Delete + +### `POST /insert/{database}&table={table}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: table name + - `api`: ignored +- **Body (JSON)**: + - Array of row objects, e.g. `[ { "col1": 1, "col2": "x" }, ... ]` +- **Behavior**: + - Validates that keys match table columns (excluding internal columns). + - Builds a multi-row `INSERT` with all values sent as quoted string literals (Postgres casts them). +- **Response**: + - `200 OK` with `"Insert Successful"` on success. + - `200 OK` with `"Invalid Data"` if column names/lengths dont match. + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/insert/my_schema&table=my_table&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]' +``` + +### `POST /updaterecord/{database}&table={table}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: table name + - `api`: ignored +- **Body (JSON)**: + - Array of objects including `INTERNAL_PRIMARY_KEY` and any fields to change, e.g.: + - `[ { "INTERNAL_PRIMARY_KEY": 1, "name": "new" }, ... ]` +- **Behavior**: + - For each object, builds an `UPDATE` for that primary key. + - All values are stringified and quoted; Postgres casts appropriately. +- **Response**: + - `200 OK` with `"Update Successful"`. + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/updaterecord/my_schema&table=my_table&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '[{"INTERNAL_PRIMARY_KEY":1,"name":"Alice Updated"}]' +``` + +### `POST /deleterecord/{database}&table={table}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: table name + - `api`: ignored +- **Body (JSON)**: + - Object whose values are the `INTERNAL_PRIMARY_KEY` values to delete, e.g. `{ "id1": 1, "id2": 2 }`. +- **Behavior**: + - Builds `DELETE FROM database.table WHERE INTERNAL_PRIMARY_KEY IN (...)`. +- **Response**: + - `200 OK` with `"Status: 200 Record Deleted"`. + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/deleterecord/my_schema&table=my_table&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"id1":1,"id2":2}' +``` + +--- + +## Attachments (GPS side table) + +### `POST /insertattachment/{database}&table={table}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: base table name; side table is `{table}_GPS` + - `api`: ignored +- **Body (JSON)**: + - Expected order: first key is filename, second key is attachment data. + - Example: `{ "filename": "file1.txt", "attachment": "" }`. +- **Behavior**: + - Decodes base64 data, hex-encodes bytes, inserts into `database.table_GPS (X_COORD, ATTACHMENT)`. +- **Response**: + - `200 OK` with `"Status: 200 Record Inserted"`. + +**Example** + +```bash +ATTACH_BASE64=$(printf 'hello world' | base64) + +curl -X POST \ + "http://localhost:8080/insertattachment/my_schema&table=my_table&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"filename\":\"file1.txt\",\"attachment\":\"${ATTACH_BASE64}\"}" +``` + +### `GET /retrieveattachment/{database}&table={table}&id={id}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: base table name + - `id`: `INTERNAL_PRIMARY_KEY` in `{table}_GPS` + - `api`: ignored +- **Behavior**: + - Selects the `Attachment` column from `database.table_GPS` for the given id. + - Returns the raw bytes base64-encoded as `result`. +- **Response**: + - `200 OK` with `{ "result": "" }`. + +**Example** + +```bash +curl -X GET \ + "http://localhost:8080/retrieveattachment/my_schema&table=my_table&id=1&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" +``` + +--- + +## Querying Data + +### `GET /query/{database}&table={table}&select={select}&where={where}&expand={expand}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: table name + - `select`: comma-separated list of columns or `*` + - `where`: SQL where clause (URL-encoded), e.g. `INTERNAL_PRIMARY_KEY=1` + - `expand`: `true` or `false` – whether to expand all columns + - `api`: ignored +- **Behavior**: + - Runs per-column SELECTs (casting to text) and builds a row-wise JSON object. +- **Response**: + - `200 OK` with JSON object keyed by row index (`"0"`, `"1"`, ...). + +**Example** + +```bash +curl -X GET \ + "http://localhost:8080/query/my_schema&table=my_table&select=*&where=INTERNAL_PRIMARY_KEY%3D1&expand=false&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" +``` + +### `GET /queryall/{database}&table={table}&depth={depth}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: root table name + - `depth`: recursion depth (integer) + - `api`: ignored +- **Behavior**: + - Reads relationships from `"Relationships".relationships` where `table` is a parent. + - Recursively walks child tables up to `depth`, nesting related records. +- **Response**: + - `200 OK` with nested JSON of parents and children (and deeper descendants if `depth > 1`). + +**Example** + +```bash +curl -X GET \ + "http://localhost:8080/queryall/my_schema&table=parents&depth=1&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" +``` + +### `GET /queryrelationship/{database}&relationship={relationship}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: target schema + - `relationship`: logical relationship name + - `api`: ignored +- **Behavior**: + - Loads a single relationship definition from `"Relationships".relationships`. + - Queries the parent table, then nests matching child rows using the stored `where_clause`. +- **Response**: + - `200 OK` with JSON of parent rows including nested child data under the child column name. + +**Example** + +```bash +curl -X GET \ + "http://localhost:8080/queryrelationship/my_schema&relationship=parents_children&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" +``` + +### `GET /querytableschema/{database}&table={table}&apikey={api}` +- **Auth**: user must be allowed to access `database` +- **Path Params**: + - `database`: schema name + - `table`: table name + - `api`: ignored +- **Behavior**: + - Queries `information_schema.columns` and `information_schema.key_column_usage`. + - Builds JSON for each column with type and constraint info. +- **Response**: + - `200 OK` with objects like `{ "column_name", "column_type", "constraint_name" }` keyed by index. + +**Example** + +```bash +curl -X GET \ + "http://localhost:8080/querytableschema/my_schema&table=my_table&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" +``` + +### `GET /querydatabase/{database}&expand={expand}&apikey={api}` +- **Auth**: `admin` role required +- **Path Params**: + - `database`: schema name + - `expand`: `true` or `false` + - `api`: ignored +- **Behavior**: + - If `expand=false`, returns table names and schema. + - If `expand=true`, for each table returns column names, types, and constraints. +- **Response**: + - `200 OK` with JSON description of tables and, optionally, their schemas. + +**Example** + +```bash +curl -X GET \ + "http://localhost:8080/querydatabase/my_schema&expand=false&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" +``` + +--- + +## Relationships Metadata + +### `POST /relationship/{database}&apikey={api}` +- **Auth**: `admin` role required +- **Path Params**: + - `database`: schema whose tables are being related + - `api`: ignored +- **Body (JSON)**: + - Keys: + - `table1`, `table1col`, `table2`, `table2col`, `ondelete`, `onupdate` +- **Behavior**: + - Builds an `ALTER TABLE . ADD FOREIGN KEY ...` statement. + - Executes it via Diesel. +- **Response**: + - `200 OK` with `"Status: 200 Relationship Created"` on success. + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/relationship/my_schema&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "table1": "parents", + "table1col":"id", + "table2": "children", + "table2col":"parent_id", + "ondelete": "CASCADE", + "onupdate": "CASCADE" + }' +``` + +### `POST /relateparent/{database}&parent_table={parent_table}&child_table={child_table}&relationship_name={relationship_name}&apikey={api}` +- **Auth**: `admin` role required +- **Path Params**: + - `database`: schema name + - `parent_table`: parent table + - `child_table`: child table + - `relationship_name`: unique logical name + - `api`: ignored +- **Body (JSON)**: + - Typically `{ "where": "=" }`, after parsing. +- **Behavior**: + - Ensures `relationship_name` is unique in `"Relationships".relationships`. + - Inserts a row describing the relationship (target DB, tables, where clause, name). +- **Response**: + - `200 OK` with `"Status: 200 Relationship Created"` on success. + - `200 OK` with `"Status: 400 Relationship Name Already Exists"` if duplicate. + +**Example** + +```bash +curl -X POST \ + "http://localhost:8080/relateparent/my_schema&parent_table=parents&child_table=children&relationship_name=parents_children&apikey=ignored" \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"where":"parents.INTERNAL_PRIMARY_KEY=children.parent_id"}' +``` diff --git a/README.md b/README.md index 4a14771..2d68a87 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,171 @@ If you prefer to run the Rust binary directly on your host while still using the The server will again be available on http://localhost:8080/. +## JWT Authentication & Authorization + +The application now uses JSON Web Tokens (JWT) and a Postgres-backed users table instead of API keys for most database actions. + +### Secrets + +- The signing key is read from the `JWT_SECRET` environment variable. +- In Docker, this is set for the `app` service in [docker-compose.yml](docker-compose.yml). +- For local development, export it before running: + + ```bash + export JWT_SECRET=change-me-in-prod + ``` + +### User Registration + +- Endpoint: `POST /auth/register` +- Body (JSON): + + ```json + { + "email": "user@example.com", + "password": "at_least_8_chars", + "allowed_schemas": ["my_database_schema"] + } + ``` + +- Behavior: + - The **first** user ever registered becomes an `admin`. + - All subsequent users are regular `user` accounts. + - `allowed_schemas` is optional; if omitted or empty, the user is allowed to access **all** schemas. + +### Login and Tokens + +- Endpoint: `POST /auth/login` +- Body (JSON): + + ```json + { + "email": "user@example.com", + "password": "at_least_8_chars" + } + ``` + +- On success, you receive: + + ```json + { + "status": "ok", + "token": "" + } + ``` + +- Use this token in the `Authorization` header for all protected endpoints: + + ```http + Authorization: Bearer + ``` + +### Admin Endpoints + +The first registered user (role `admin`) can manage other users: + +- List users: + - `GET /admin/users` + - Requires an `Authorization: Bearer ` header. +- Update which schemas a user can access: + - `POST /admin/users/schemas` + - Body (JSON): + + ```json + { + "email": "user@example.com", + "allowed_schemas": ["schema1", "schema2"] + } + ``` + +## Auth Behavior for Database Actions + +Most existing routes still keep their original URL shape (including `&apikey=...` in the path), but **the API key is now ignored**. Authorization is based solely on the JWT token and the user’s role/schemas. + +### Per-schema user actions + +These endpoints require a valid JWT and that the user is allowed to access the `{database}` schema (either explicitly via `allowed_schemas` or implicitly when the list is empty): + +- Insert records: + - `POST /insert/{database}&table={table}&apikey={ignored}` +- Insert attachments: + - `POST /insertattachment/{database}&table={table}&apikey={ignored}` +- Update records: + - `POST /updaterecord/{database}&table={table}&apikey={ignored}` +- Delete records: + - `POST /deleterecord/{database}&table={table}&apikey={ignored}` +- Query table data: + - `GET /query/{database}&table={table}&select={select}&where={where}&expand={expand}&apikey={ignored}` +- Query table schema: + - `GET /querytableschema/{database}&table={table}&apikey={ignored}` +- Query relationships and nested data: + - `GET /queryrelationship/{database}&relationship={relationship}&apikey={ignored}` + - `GET /queryall/{database}&table={table}&depth={depth}&apikey={ignored}` +- Retrieve attachments: + - `GET /retrieveattachment/{database}&table={table}&id={id}&apikey={ignored}` + +All of the above must be called with: + +```http +Authorization: Bearer +``` + +If the token is missing/invalid, or the user is not allowed to access `{database}`, the server responds with HTTP 403. + +### Admin-only actions + +These operations require a JWT for a user whose role is `admin`: + +- Create tables: + - `POST /createtable/{database}&table={table}&gps={gps}&apikey={ignored}` +- Drop tables: + - `POST /droptable/{database}&table={table}&apikey={ignored}` +- Create relationships: + - `POST /relationship/{database}&apikey={ignored}` + - `POST /relateparent/{database}&parent_table={parent_table}&child_table={child_table}&relationship_name={relationship_name}&apikey={ignored}` +- Introspect database schema: + - `GET /querydatabase/{database}&expand={expand}&apikey={ignored}` +- Create a new logical database/schema: + - `POST /createdatabase/{database}&apikey={ignored}` + +Again, the `apikey` path segment is ignored; only the JWT and the user’s role matter. + +## Example Frontend Flow (Auth Perspective) + +From a frontend or API client, a typical flow to insert/query data is: + +1. **Register or obtain an account** + - `POST /auth/register` with email/password (first user becomes admin). + - An admin can later set `allowed_schemas` for other users via `/admin/users/schemas`. + +2. **Login to get a token** + - `POST /auth/login` with email/password. + - Store the returned `token` on the client (e.g., in memory or secure storage). + +3. **Call database endpoints with the token** + - Example: insert into table `my_table` in schema `my_schema`: + + ```http + POST /insert/my_schema&table=my_table&apikey=ignored + Authorization: Bearer + Content-Type: application/json + + [ + { "col1": "value1", "col2": 123 } + ] + ``` + + - Example: query that same table: + + ```http + GET /query/my_schema&table=my_table&select=*&where=1=1&expand=false&apikey=ignored + Authorization: Bearer + ``` + +4. **Rely on roles/schemas for enforcement** + - If the JWT belongs to a user who does **not** have access to `my_schema`, the request will be rejected with 403. + - Admins (role `admin`) can call the admin-only endpoints to inspect or manage schemas, tables, and relationships. + ## Architecture & Migration Notes - The project was originally built against MariaDB/MySQL using the `mysql` crate. diff --git a/docker-compose.yml b/docker-compose.yml index 1e5f629..4839fff 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,9 +6,9 @@ services: container_name: hosted_database restart: unless-stopped environment: - - POSTGRES_USER=api_user - - POSTGRES_PASSWORD=secret - - POSTGRES_DB=api_main + - POSTGRES_USER=${POSTGRES_USER} + - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} + - POSTGRES_DB=${POSTGRES_DB} ports: - "5432:5432" volumes: @@ -22,5 +22,6 @@ services: depends_on: - db environment: - - DATABASE_URL=postgres://api_user:secret@db:5432/api_main - - PWD=secret + - DATABASE_URL=${DATABASE_URL} + - JWT_SECRET=${JWT_SECRET} + - PWD=${PASSWORD} diff --git a/sql-scripts/initapi.sql b/sql-scripts/initapi.sql index 1ce7f60..d1bc718 100644 --- a/sql-scripts/initapi.sql +++ b/sql-scripts/initapi.sql @@ -26,3 +26,14 @@ CREATE TABLE IF NOT EXISTS "Relationships".relationships ( relationship VARCHAR(100) UNIQUE ); +-- Schema and table for user accounts (JWT auth) +CREATE SCHEMA IF NOT EXISTS "Auth"; + +CREATE TABLE IF NOT EXISTS "Auth".users ( + id SERIAL PRIMARY KEY, + email VARCHAR(255) UNIQUE NOT NULL, + password_hash VARCHAR(255) NOT NULL, + role VARCHAR(50) NOT NULL DEFAULT 'user', + allowed_schemas TEXT NOT NULL DEFAULT '' +); + diff --git a/src/auth.rs b/src/auth.rs new file mode 100644 index 0000000..9dbc225 --- /dev/null +++ b/src/auth.rs @@ -0,0 +1,379 @@ +use actix_web::{dev::Payload, error::ErrorUnauthorized, web, Error, FromRequest, HttpRequest, HttpResponse, Responder}; +use diesel::prelude::*; +use diesel::sql_types::{Int4, Text}; +use diesel::QueryableByName; +use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation}; +use serde::{Deserialize, Serialize}; +use std::future::{ready, Ready}; + +use crate::dbconnect; +use crate::PooledConn; + +#[derive(Clone)] +pub struct AppState { + pub jwt_secret: String, +} + +impl AppState { + pub fn from_env() -> Self { + let secret = std::env::var("JWT_SECRET").unwrap_or_else(|_| "dev-secret-change-me".to_string()); + AppState { jwt_secret: secret } + } +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Claims { + pub sub: i32, + pub exp: usize, + pub role: String, + pub schemas: Vec, +} + +#[derive(Debug, Clone)] +pub struct Authenticated { + pub user_id: i32, + pub role: String, + pub schemas: Vec, +} + +impl Authenticated { + pub fn can_access_schema(&self, schema: &str) -> bool { + if self.schemas.is_empty() { + return true; + } + self.schemas.iter().any(|s| s == schema) + } +} + +impl FromRequest for Authenticated { + type Error = Error; + type Future = Ready>; + + fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { + let header = req + .headers() + .get("Authorization") + .and_then(|h| h.to_str().ok()) + .unwrap_or("") + .to_string(); + + if !header.starts_with("Bearer ") { + return ready(Err(ErrorUnauthorized("Missing or invalid Authorization header"))); + } + + let token = header.trim_start_matches("Bearer "); + + let state = match req.app_data::>() { + Some(data) => data.clone(), + None => { + return ready(Err(ErrorUnauthorized("Auth state not configured"))); + } + }; + + let decoding_key = DecodingKey::from_secret(state.jwt_secret.as_bytes()); + let validation = Validation::new(Algorithm::HS256); + + match decode::(token, &decoding_key, &validation) { + Ok(data) => { + let claims = data.claims; + let auth = Authenticated { + user_id: claims.sub, + role: claims.role, + schemas: claims.schemas, + }; + ready(Ok(auth)) + } + Err(_) => ready(Err(ErrorUnauthorized("Invalid token"))), + } + } +} + +#[derive(Debug, Deserialize)] +pub struct RegisterRequest { + pub email: String, + pub password: String, + pub allowed_schemas: Option>, +} + +#[derive(Debug, Deserialize)] +pub struct LoginRequest { + pub email: String, + pub password: String, +} + +#[derive(QueryableByName)] +struct UserRow { + #[diesel(sql_type = Int4)] + id: i32, + #[diesel(sql_type = Text)] + email: String, + #[diesel(sql_type = Text)] + password_hash: String, + #[diesel(sql_type = Text)] + role: String, + #[diesel(sql_type = Text)] + allowed_schemas: String, +} + +#[derive(QueryableByName)] +struct UserCount { + #[diesel(sql_type = Int4)] + count: i32, +} + +fn get_connection() -> PooledConn { + dbconnect::internalqueryconn() +} + +fn hash_password(password: &str) -> Result { + use argon2::password_hash::{rand_core::OsRng, PasswordHasher, SaltString}; + use argon2::{Algorithm, Argon2, Params, Version}; + + let salt = SaltString::generate(&mut OsRng); + let argon2 = Argon2::new(Algorithm::Argon2id, Version::V0x13, Params::default()); + let hash = argon2.hash_password(password.as_bytes(), &salt)?.to_string(); + Ok(hash) +} + +fn verify_password(password: &str, password_hash: &str) -> bool { + use argon2::password_hash::{PasswordHash, PasswordVerifier}; + use argon2::Argon2; + + let parsed_hash = PasswordHash::new(password_hash); + if parsed_hash.is_err() { + return false; + } + let parsed_hash = parsed_hash.unwrap(); + Argon2::default() + .verify_password(password.as_bytes(), &parsed_hash) + .is_ok() +} + +fn create_token(user: &UserRow, schemas: Vec, secret: &str) -> Result { + use chrono::Utc; + + let exp = (Utc::now().timestamp() + 3600) as usize; // 1 hour expiry + let claims = Claims { + sub: user.id, + exp, + role: user.role.clone(), + schemas, + }; + + let header = Header::new(Algorithm::HS256); + encode(&header, &claims, &EncodingKey::from_secret(secret.as_bytes())) +} + +pub async fn register( + body: web::Json, +) -> impl Responder { + let body = body.into_inner(); + + if body.email.trim().is_empty() || body.password.len() < 8 { + return HttpResponse::BadRequest() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"Invalid email or password too short\"}"); + } + + let schemas_vec = body.allowed_schemas.unwrap_or_else(|| vec![]); + let allowed_schemas = if schemas_vec.is_empty() { + String::new() + } else { + schemas_vec.join(",") + }; + + let password_hash = match hash_password(&body.password) { + Ok(h) => h, + Err(_) => { + return HttpResponse::InternalServerError() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"Failed to hash password\"}"); + } + }; + + let mut conn = get_connection(); + + // Determine role: first registered user becomes admin, others are regular users. + let count_row = diesel::sql_query("SELECT COUNT(*)::INT as count FROM \"Auth\".users") + .get_result::(&mut conn) + .unwrap_or(UserCount { count: 0 }); + + let role = if count_row.count == 0 { + "admin".to_string() + } else { + "user".to_string() + }; + + let query = + "INSERT INTO \"Auth\".users (email, password_hash, role, allowed_schemas) VALUES ($1, $2, $3, $4)"; + + let result = diesel::sql_query(query) + .bind::(&body.email) + .bind::(&password_hash) + .bind::(&role) + .bind::(&allowed_schemas) + .execute(&mut conn); + + match result { + Ok(_) => HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body("{\"status\":\"ok\"}"), + Err(e) => { + let msg = format!("{{\"error\":\"{}\"}}", e.to_string()); + HttpResponse::InternalServerError() + .content_type("application/json; charset=utf-8") + .body(msg) + } + } +} + +pub async fn login( + state: web::Data, + body: web::Json, +) -> impl Responder { + let body = body.into_inner(); + + let mut conn = get_connection(); + + let query = + "SELECT id, email, password_hash, role, allowed_schemas FROM \"Auth\".users WHERE email = $1"; + + let result: Result = diesel::sql_query(query) + .bind::(&body.email) + .get_result(&mut conn); + + let user = match result { + Ok(u) => u, + Err(_) => { + return HttpResponse::Unauthorized() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"Invalid credentials\"}"); + } + }; + + if !verify_password(&body.password, &user.password_hash) { + return HttpResponse::Unauthorized() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"Invalid credentials\"}"); + } + + let schemas: Vec = if user.allowed_schemas.trim().is_empty() { + Vec::new() + } else { + user.allowed_schemas + .split(',') + .map(|s| s.trim().to_string()) + .collect() + }; + + let token = match create_token(&user, schemas, &state.jwt_secret) { + Ok(t) => t, + Err(_) => { + return HttpResponse::InternalServerError() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"Failed to create token\"}"); + } + }; + + let resp = serde_json::json!({ + "status": "ok", + "token": token, + }); + + HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body(resp.to_string()) +} + +#[derive(Serialize)] +struct PublicUser { + id: i32, + email: String, + role: String, + allowed_schemas: String, +} + +#[derive(Deserialize)] +pub struct UpdateUserSchemasRequest { + pub email: String, + pub allowed_schemas: Vec, +} + +pub async fn list_users(auth: Authenticated) -> impl Responder { + if auth.role != "admin" { + return HttpResponse::Forbidden() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"Admin role required\"}"); + } + + let mut conn = get_connection(); + + let query = "SELECT id, email, role, allowed_schemas FROM \"Auth\".users"; + + let result: Result, _> = diesel::sql_query(query).load(&mut conn); + + match result { + Ok(rows) => { + let users: Vec = rows + .into_iter() + .map(|u| PublicUser { + id: u.id, + email: u.email, + role: u.role, + allowed_schemas: u.allowed_schemas, + }) + .collect(); + + let resp = serde_json::to_string(&users).unwrap_or_else(|_| "[]".to_string()); + HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body(resp) + } + Err(e) => { + let msg = format!("{{\"error\":\"{}\"}}", e.to_string()); + HttpResponse::InternalServerError() + .content_type("application/json; charset=utf-8") + .body(msg) + } + } +} + +pub async fn update_user_schemas( + auth: Authenticated, + body: web::Json, +) -> impl Responder { + if auth.role != "admin" { + return HttpResponse::Forbidden() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"Admin role required\"}"); + } + + let body = body.into_inner(); + let allowed_schemas = if body.allowed_schemas.is_empty() { + String::new() + } else { + body.allowed_schemas.join(",") + }; + + let mut conn = get_connection(); + + let query = + "UPDATE \"Auth\".users SET allowed_schemas = $1 WHERE email = $2"; + + let result = diesel::sql_query(query) + .bind::(&allowed_schemas) + .bind::(&body.email) + .execute(&mut conn); + + match result { + Ok(_) => HttpResponse::Ok() + .content_type("application/json; charset=utf-8") + .body("{\"status\":\"ok\"}"), + Err(e) => { + let msg = format!("{{\"error\":\"{}\"}}", e.to_string()); + HttpResponse::InternalServerError() + .content_type("application/json; charset=utf-8") + .body(msg) + } + } +} diff --git a/src/connkey.rs b/src/connkey.rs index 12596e0..dcaed19 100644 --- a/src/connkey.rs +++ b/src/connkey.rs @@ -23,7 +23,9 @@ impl ApiKey { } } pub fn populatekey(&mut self, database: String) { - let salt = std::env::args().nth(3).expect("no salt provided"); + // Legacy API-key generation no longer relies on a CLI-provided salt. + // Use a fixed salt to avoid panics when no CLI args are present. + let salt = std::env::var("APIKEY_SALT").unwrap_or_else(|_| "legacy-salt".to_string()); let mut apikey = String::new(); let mut ctx = Context::new(&SHA256); ctx.update(database.as_bytes()); diff --git a/src/createdatabase.rs b/src/createdatabase.rs index 2100e34..cedd43f 100644 --- a/src/createdatabase.rs +++ b/src/createdatabase.rs @@ -1,15 +1,9 @@ -use crate::connkey; -use crate::connkey::ApiKey; use crate::dbconnect; use diesel::prelude::*; use diesel::sql_query; //create a logical database (schema) in Postgres and register an API key pub fn create_database(database_name: &str) { let mut conn = dbconnect::internalqueryconn(); - let mut key = ApiKey::new(); - key.populatekey(database_name.to_string()); - - let hash = connkey::random_password(); let mut query = String::from("CREATE SCHEMA IF NOT EXISTS "); query.push_str(database_name); query.push_str(";"); @@ -17,14 +11,11 @@ pub fn create_database(database_name: &str) { sql_query(query) .execute(&mut conn) .expect("Failed to create schema"); - - let _ = connkey::insert_apikey(database_name.to_string(), hash); } pub fn create_databaseweb(database: &str) -> String { let mut conn = dbconnect::internalqueryconn(); let dbname = String::from(database); - let hash = connkey::random_password(); let mut query = String::from("CREATE SCHEMA IF NOT EXISTS "); query.push_str(&dbname); query.push_str(";"); @@ -33,6 +24,7 @@ pub fn create_databaseweb(database: &str) -> String { .execute(&mut conn) .expect("Failed to create schema"); - let apikey = connkey::insert_apikey(dbname.to_string(), hash); - apikey.unwrap() + // Legacy behavior returned an API key; JWT auth no longer needs this. + // Return a simple success marker for compatibility with existing callers. + String::from("ok") } diff --git a/src/insertrecords.rs b/src/insertrecords.rs index 7974ac1..84b014c 100644 --- a/src/insertrecords.rs +++ b/src/insertrecords.rs @@ -1,6 +1,5 @@ use crate::dbconnect; use crate::pushdata::gettablecol; -use crate::querytable; use serde::{Deserialize, Serialize}; use serde_json::Result; use diesel::prelude::*; @@ -23,14 +22,14 @@ impl TableDef { pub fn populate(&mut self, table_name: &str, database: &str) { let mut conn = dbconnect::internalqueryconn(); - let typesstmt = querytable::grab_columntypes(table_name, database).unwrap(); - println!("{}", typesstmt); - let types = querytable::exec_map(&mut conn, &typesstmt).unwrap(); + // For Postgres we no longer depend on MySQL's INFORMATION_SCHEMA.COLUMN_TYPE; + // we only need the column names and will send values as string literals, + // letting Postgres cast as needed. let fields = gettablecol::get_table_col(&mut conn, table_name, database).unwrap(); println!("Fields: {:?}", fields); self.table_name = String::from(table_name); self.table_fields = fields; - self.table_types = types; + self.table_types = Vec::new(); } pub fn compare_fields(&mut self, data: &Vec>) -> bool { //default to false @@ -74,77 +73,28 @@ impl TableDef { stmt.push_str(") VALUES ("); for data in date.iter() { for i in 0..data.len() { - let valuedata = data[i].1.replace("\"", ""); + let mut valuedata = data[i].1.replace("\"", ""); + // Escape single quotes for SQL literal + valuedata = valuedata.replace("'", "''"); - let typestring = - &self.compare_types(&data[i].0, &self.table_fields, &self.table_types); - - match typestring.as_str() { - "int(11)" => { - stmt.push_str(&valuedata); - } - "varchar(255)" => { - stmt.push_str("'"); - stmt.push_str(&valuedata); - stmt.push_str("'"); - } - "int(100)" => { - stmt.push_str(&valuedata); - } - "varchar(100)" => { - stmt.push_str("'"); - stmt.push_str(&valuedata); - stmt.push_str("'"); - } - "text" => { - stmt.push_str("'"); - stmt.push_str(&valuedata); - stmt.push_str("'"); - } - "datetime" => { - stmt.push_str("'"); - stmt.push_str(&valuedata); - stmt.push_str("'"); - } - "boolean" => { - stmt.push_str(&valuedata); - } - "tinyint(1)" => { - stmt.push_str(&valuedata); - } - "date" => { - stmt.push_str("'"); - stmt.push_str(&valuedata); - stmt.push_str("'"); - } - _ => { - stmt.push_str("NULL"); - } - } + // For Postgres we send everything as a quoted string literal and + // let the engine cast to the target column type. + stmt.push_str("'"); + stmt.push_str(&valuedata); + stmt.push_str("'"); if i != data.len() - 1 { stmt.push_str(", "); } } - //if data != &date[date.len() - 1] { stmt.push_str("), ("); - //} } - //stmt.push_str(")"); + // remove the trailing `, (` stmt.pop(); stmt.pop(); stmt.pop(); stmt } - fn compare_types(&self, column: &str, fields: &Vec, types: &Vec) -> String { - let colstring = String::from(column); - for i in 0..fields.len() { - if fields[i] == colstring { - return types[i].clone(); - } - } - String::from("NULL") - } } pub fn insert_attachment( database: &str, diff --git a/src/main.rs b/src/main.rs index dc8273a..c8cdf0d 100755 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,7 @@ pub mod querytable; pub mod relationships; pub mod tablecreate; pub mod update; +pub mod auth; // Global database connection type, backed by Diesel Postgres pub type PooledConn = PgConnection; @@ -47,6 +48,7 @@ async fn main() { // secretkey.clone(), // )) App::new() + .app_data(web::Data::new(auth::AppState::from_env())) //session cookie //.app_data(TempFileConfig::default().directory("./tmp")) //force users to start at / before going to /main @@ -55,6 +57,13 @@ async fn main() { .route(web::get().to(getinitializeconnect)) .route(web::post().to(postinitializeconnect)), ) + .route("/auth/register", web::post().to(auth::register)) + .route("/auth/login", web::post().to(auth::login)) + .route("/admin/users", web::get().to(auth::list_users)) + .route( + "/admin/users/schemas", + web::post().to(auth::update_user_schemas), + ) .route("/health", web::get().to(health)) //.route("/main", web::get().to(index)) //.route("/auth", web::post().to(auth)) @@ -152,23 +161,12 @@ async fn main() { .unwrap(); } async fn postinitializeconnect(form: web::Form) -> impl Responder { - let valid = connkey::search_apikey_admin(&form.apikey).unwrap(); - if valid == true { - //let _=initconnect::postdatabaseconnection(form.into_inner()); - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("{\"success\":\"true\"}") - } else { - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("{\"error\":\"Invalid API Key\"}") - } - //let _=initconnect::postdatabaseconnection(form.into_inner()); - //post to appdata from here - - // HttpResponse::Ok() - // .content_type("text/html; charset=utf-8") - // .body(include_str!("page.html")) + // Legacy endpoint: API-key based initialization is deprecated. + // Kept only to avoid breaking old clients; always returns an error. + let _ = form; // suppress unused warning + HttpResponse::Gone() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"deprecated_endpoint_use_jwt_auth\"}") } async fn getinitializeconnect() -> impl Responder { let html = initconnect::getpagehtml(); @@ -262,39 +260,51 @@ async fn health() -> impl Responder { // .body(include_str!("pages/methodsuccess.html")) //} async fn createrelationshipweb( + auth: auth::Authenticated, info: web::Path<(String, String)>, body: web::Json, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.1); - if valid.unwrap() == true { - let body = body.into_inner(); - let mut data = Vec::new(); - for (key, value) in body.as_object().unwrap().iter() { - let parsed = createrelationship::parse_json(value.to_string()); - println!("{:?}", parsed); - data.push((key.to_string(), parsed)); - } - let relationship = createrelationship::createrelationshipfromweb(&info.0, data); - let _ = createrelationship::commitrelationshipfromweb(relationship); - - HttpResponse::Ok() + let database = &info.0; + if auth.role != "admin" { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Status: 200 Relationship Created") - } else { - HttpResponse::Ok() + .body("Admin role required"); + } + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Status: 400 Invalid API Key") + .body("Forbidden for this database"); } + + let body = body.into_inner(); + let mut data = Vec::new(); + for (key, value) in body.as_object().unwrap().iter() { + let parsed = createrelationship::parse_json(value.to_string()); + println!("{:?}", parsed); + data.push((key.to_string(), parsed)); + } + let relationship = createrelationship::createrelationshipfromweb(&info.0, data); + let _ = createrelationship::commitrelationshipfromweb(relationship); + + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body("Status: 200 Relationship Created") } async fn createrelationshipparentweb( + auth: auth::Authenticated, info: web::Path<(String, String, String, String, String)>, body: web::Json, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.4); - if valid.unwrap() == false { - return HttpResponse::Ok() + let database = &info.0; + if auth.role != "admin" { + return HttpResponse::Forbidden() + .content_type("text/json; charset=utf-8") + .body("Admin role required"); + } + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Status: 400 Invalid API Key"); + .body("Forbidden for this database"); } let body = body.into_inner(); @@ -329,67 +339,67 @@ async fn createrelationshipparentweb( .body("Status: 200 Relationship Created") } async fn deleterecord( + auth: auth::Authenticated, info: web::Path<(String, String, String)>, body: web::Json, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.2); - if valid.unwrap() == true { - let mut conn = dbconnect::internalqueryconnapikey(); - let body = body.into_inner(); - let mut data = Vec::new(); - for (key, value) in body.as_object().unwrap().iter() { - data.push((key.to_string(), value.to_string())); - } - let database = &info.0; - let table = &info.1; - //let parsed_json=tablecreate::parse_json(data); - let statement = delete::deleterecord(&database, &table, data); - let _ = delete::exec_statement(&mut conn, &statement.unwrap()); - - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Status: 200 Record Deleted") - } else { - HttpResponse::Ok() + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Status: 400 Invalid API Key") + .body("Forbidden for this database"); + } + + let mut conn = dbconnect::internalqueryconn(); + let body = body.into_inner(); + let mut data = Vec::new(); + for (key, value) in body.as_object().unwrap().iter() { + data.push((key.to_string(), value.to_string())); } + let table = &info.1; + let statement = delete::deleterecord(&database, &table, data); + let _ = delete::exec_statement(&mut conn, &statement.unwrap()); + + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body("Status: 200 Record Deleted") } async fn getkey(info: web::Path<(String, String)>) -> impl Responder { - let valid = connkey::search_apikey_admin(&info.1); - if valid.unwrap() == true { - let stmt = connkey::generate_apikey(&info.0).unwrap(); - let key = connkey::execute_apikey(&stmt); - let retrnvalue = key.unwrap(); - let respnse = "{\"Status\": \"200\", \"APIKey\": \"".to_owned() + &retrnvalue + "\"}"; - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body(respnse) - } else { - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Status: 400 Invalid API Key") - } + // Legacy endpoint: API-key issuance is deprecated in favor of JWT auth. + let _ = info; // suppress unused warning + HttpResponse::Gone() + .content_type("application/json; charset=utf-8") + .body("{\"error\":\"deprecated_endpoint_use_jwt_auth\"}") } async fn createtableweb( + auth: auth::Authenticated, info: web::Path<(String, String, String, String)>, body: web::Json, ) -> impl Responder { println!("{:?}", &info.3); println!("{:?}", &info.0); - let valid = connkey::search_apikey(&info.0, &info.3); - if valid.unwrap() == true { - let mut conn = dbconnect::internalqueryconnapikey(); - let body = body.into_inner(); - let mut data = Vec::new(); - for (key, value) in body.as_object().unwrap().iter() { - data.push((key.to_string(), value.to_string())); - } - println!("{:?}", data); - let database = &info.0; - let table = &info.1; - let gps = &info.2; + let database = &info.0; + if auth.role != "admin" { + return HttpResponse::Forbidden() + .content_type("text/json; charset=utf-8") + .body("Admin role required"); + } + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() + .content_type("text/json; charset=utf-8") + .body("Forbidden for this database"); + } + + let mut conn = dbconnect::internalqueryconn(); + let body = body.into_inner(); + let mut data = Vec::new(); + for (key, value) in body.as_object().unwrap().iter() { + data.push((key.to_string(), value.to_string())); + } + println!("{:?}", data); + let table = &info.1; + let gps = &info.2; //convert gps to bool let gps = gps.parse::().unwrap(); @@ -425,25 +435,27 @@ async fn createtableweb( // &parsed_json.1, //); - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Table Created") - } else { - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Invalid API Key") - } + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body("Table Created") } async fn droptableweb( + auth: auth::Authenticated, info: web::Path<(String, String, String)>, body: web::Json, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.2); - if valid.unwrap() == false { - return HttpResponse::Ok() + let database = &info.0; + if auth.role != "admin" { + return HttpResponse::Forbidden() + .content_type("text/json; charset=utf-8") + .body("Admin role required"); + } + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Invalid API Key"); + .body("Forbidden for this database"); } + let mut conn = dbconnect::internalqueryconn(); let body = body.into_inner(); //let mut data=Vec::new(); @@ -468,14 +480,17 @@ async fn droptableweb( .content_type("text/json; charset=utf-8") .body("Table Dropped") } -async fn retrieveattachment(info: web::Path<(String, String, String, String)>) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.3); - if valid.unwrap() == false { - return HttpResponse::Ok() +async fn retrieveattachment( + auth: auth::Authenticated, + info: web::Path<(String, String, String, String)>, +) -> impl Responder { + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Invalid API Key"); + .body("Forbidden for this database"); } - let database = &info.0; + println!("{:?}", &info.1); let table = &info.1; println!("{:?}", &info.2); @@ -502,14 +517,15 @@ async fn retrieveattachment(info: web::Path<(String, String, String, String)>) - //grab attachment to put in s3 bucket async fn dbinsertattachment( + auth: auth::Authenticated, info: web::Path<(String, String, String)>, body: web::Json, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.2); - if !valid.unwrap() { - return HttpResponse::Ok() + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Status: 400 Invalid API Key"); + .body("Forbidden for this database"); } //decode json let body = body.into_inner(); @@ -547,48 +563,46 @@ async fn dbinsertattachment( } async fn dbinsert( + auth: auth::Authenticated, info: web::Path<(String, String, String)>, body: web::Json>, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.2); - if valid.unwrap() == true { - let body = body.into_inner(); - let mut storagevec: Vec> = Vec::new(); - for record in body.iter() { - let mut data = Vec::new(); - for (key, value) in record.as_object().unwrap().iter() { - data.push((key.to_string(), value.to_string())); - } - storagevec.push(data); + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() + .content_type("text/json; charset=utf-8") + .body("Forbidden for this database"); + } + + let body = body.into_inner(); + let mut storagevec: Vec> = Vec::new(); + for record in body.iter() { + let mut data = Vec::new(); + for (key, value) in record.as_object().unwrap().iter() { + data.push((key.to_string(), value.to_string())); } - println!("{:?}", storagevec); + storagevec.push(data); + } + println!("{:?}", storagevec); - let database = &info.0; - let table = &info.1; - //let apikey=&info.2; + let table = &info.1; - let mut newtable = insertrecords::TableDef::new(); - newtable.populate(&table, &database); - let valid = newtable.compare_fields(&storagevec); + let mut newtable = insertrecords::TableDef::new(); + newtable.populate(&table, &database); + let valid = newtable.compare_fields(&storagevec); - if valid { - let stmt = newtable.insert(&storagevec, &table, &database); - let _ = insertrecords::exec_insert(stmt); - //println!("{:?}", stmt); - } else { - return HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Invalid Data"); - } - - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Insert Successful") + if valid { + let stmt = newtable.insert(&storagevec, &table, &database); + let _ = insertrecords::exec_insert(stmt); } else { - HttpResponse::Ok() + return HttpResponse::Ok() .content_type("text/json; charset=utf-8") - .body("Invalid API Key") + .body("Invalid Data"); } + + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body("Insert Successful") } //async fn dbinsert_gps( // info: web::Path<(String, String, String)>, @@ -636,39 +650,38 @@ async fn dbinsert( // } //} async fn dbupdaterecord( + auth: auth::Authenticated, info: web::Path<(String, String, String)>, body: web::Json>, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.2); - if valid.unwrap() == true { - let mut conn = dbconnect::internalqueryconnapikey(); - let body = body.into_inner(); - let mut storagevec: Vec> = Vec::new(); - //let mut data = Vec::new(); - for record in body.iter() { - let mut data = Vec::new(); - for (key, value) in record.as_object().unwrap().iter() { - let strkey = key.as_str(); - let strvalue = value.as_str().unwrap(); - data.push((strkey.to_string(), strvalue.to_string())); - } - storagevec.push(data); - } - let database = &info.0; - let table = &info.1; - let statement = update::updaterecord(database, table, storagevec); - for statement in statement.iter() { - let _ = update::executeupdaterecord(&mut conn, &statement); - } - - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Update Successful") - } else { - HttpResponse::Ok() + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Invalid API Key") + .body("Forbidden for this database"); + } + + let mut conn = dbconnect::internalqueryconn(); + let body = body.into_inner(); + let mut storagevec: Vec> = Vec::new(); + for record in body.iter() { + let mut data = Vec::new(); + for (key, value) in record.as_object().unwrap().iter() { + // Store all values as strings (numbers, bools, etc. will be + // stringified) and let the SQL builder quote/cast appropriately. + data.push((key.to_string(), value.to_string())); + } + storagevec.push(data); + } + let table = &info.1; + let statement = update::updaterecord(database, table, storagevec); + for statement in statement.iter() { + let _ = update::executeupdaterecord(&mut conn, &statement); } + + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body("Update Successful") } //async fn createnewdb(form: web::Form) -> impl Responder { @@ -677,23 +690,25 @@ async fn dbupdaterecord( // .content_type("text/html; charset=utf-8") // .body(include_str!("pages/methodsuccess.html")) //} -async fn createnewdbweb(info: web::Path<(String, String)>) -> impl Responder { - let valid = connkey::search_apikey_admin(&info.1); - if valid.unwrap() == true { - let database_name = &info.0; - //let apikey=&info.1; - let key = createdatabase::create_databaseweb(database_name); - let encoded = BASE64.encode(key.as_bytes()); - let response = serde_json::json!(encoded); - - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body(response.to_string()) - } else { - HttpResponse::Ok() +async fn createnewdbweb( + auth: auth::Authenticated, + info: web::Path<(String, String)>, +) -> impl Responder { + if auth.role != "admin" { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Err 500: Not a valid API Key") + .body("Admin role required"); } + + // Legacy admin API key in the path is ignored in favor of JWT-based admin checks. + let database_name = &info.0; + let key = createdatabase::create_databaseweb(database_name); + let encoded = BASE64.encode(key.as_bytes()); + let response = serde_json::json!(encoded); + + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body(response.to_string()) } //async fn createtable(MultipartForm(form): MultipartForm) -> impl Responder { @@ -782,15 +797,18 @@ async fn createnewdbweb(info: web::Path<(String, String)>) -> impl Responder { // .content_type("text/json; charset=utf-8") // .body("Success 200: Query Executed") //} -async fn queryall(info: web::Path<(String, String, String, String)>) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.3); - if valid.unwrap() == false { - return HttpResponse::Ok() +async fn queryall( + auth: auth::Authenticated, + info: web::Path<(String, String, String, String)>, +) -> impl Responder { + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Err 400: Not a valid API Key"); + .body("Forbidden for this database"); } + let mut connection = dbconnect::internalqueryconn(); - let database = &info.0; let table = &info.1; let depth = &info.2; let depth: i32 = depth.parse().unwrap(); @@ -809,15 +827,18 @@ async fn queryall(info: web::Path<(String, String, String, String)>) -> impl Res .content_type("text/json; charset=utf-8") .body(json.to_string()) } -async fn queryrelationship(info: web::Path<(String, String, String)>) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.2); - if valid.unwrap() == false { - return HttpResponse::Ok() +async fn queryrelationship( + auth: auth::Authenticated, + info: web::Path<(String, String, String)>, +) -> impl Responder { + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Err 400: Not a valid API Key"); + .body("Forbidden for this database"); } + let mut connection = dbconnect::internalqueryconn(); - let database = &info.0; let relationship = &info.1; let relationshipvec: Vec = @@ -864,76 +885,81 @@ async fn queryrelationship(info: web::Path<(String, String, String)>) -> impl Re .body(json.to_string()) } async fn querytojson( + auth: auth::Authenticated, info: web::Path<(String, String, String, String, String, String)>, ) -> impl Responder { - let valid = connkey::search_apikey(&info.0, &info.5); - if valid.unwrap() == true { - let mut connection = dbconnect::internalqueryconn(); - - let database = &info.0; - let tablename = &info.1; - let select = &info.2; - let whereclause = &info.3; - let expanded = &info.4; - let expbool = expanded.parse::().unwrap(); - //select is comma separated list of columns - //separate select into vector - let selectvec: Vec<&str> = select.split(',').collect(); - let select2 = selectvec.clone(); - - for i in 0..selectvec.len() { - println!("{}", selectvec[i]); - } + let database = &info.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() + .content_type("text/json; charset=utf-8") + .body("Forbidden for this database"); + } - //let apikey=&info.4; + let mut connection = dbconnect::internalqueryconn(); - let queryresult = querytable::query_tables( - &tablename, - &mut connection, - &whereclause, - &database, - selectvec, - expbool, - ); - let json = querytable::build_json( - queryresult, - &database, - &tablename, - &mut connection, - select2, - expbool, - ); + let tablename = &info.1; + let select = &info.2; + let whereclause = &info.3; + let expanded = &info.4; + let expbool = expanded.parse::().unwrap(); + //select is comma separated list of columns + //separate select into vector + let selectvec: Vec<&str> = select.split(',').collect(); + let select2 = selectvec.clone(); - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body(json.to_string()) - } else { - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Invalid API Key") + for i in 0..selectvec.len() { + println!("{}", selectvec[i]); } + + let queryresult = querytable::query_tables( + &tablename, + &mut connection, + &whereclause, + &database, + selectvec, + expbool, + ); + let json = querytable::build_json( + queryresult, + &database, + &tablename, + &mut connection, + select2, + expbool, + ); + + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body(json.to_string()) } -async fn querytableschema(body: web::Path<(String, String, String)>) -> impl Responder { - let valid = connkey::search_apikey(&body.0, &body.2); - if valid.unwrap() == true { - let mut connection = dbconnect::internalqueryconn(); +async fn querytableschema( + auth: auth::Authenticated, + body: web::Path<(String, String, String)>, +) -> impl Responder { + let database = &body.0; + if !auth.can_access_schema(database) { + return HttpResponse::Forbidden() + .content_type("text/json; charset=utf-8") + .body("Forbidden for this database"); + } - let database = &body.0; - let tablename = &body.1; - let mut select = Vec::new(); - select.push("*"); - let columnnamestmt = querytable::grab_columnnames_schema(tablename, database); - let column = querytable::exec_map(&mut connection, &columnnamestmt.unwrap()); - let columntypestmt = querytable::grab_columntypes_schema(tablename, database); - let columntype = querytable::exec_map(&mut connection, &columntypestmt.unwrap()); - let constraintstmt = querytable::query_constraints(tablename, database); - let constraint = querytable::exec_map_tuple(&mut connection, &constraintstmt.unwrap()); - - let json = querytable::query_table_schema( - column.unwrap(), - columntype.unwrap(), - constraint.unwrap(), - ); + let mut connection = dbconnect::internalqueryconn(); + + let tablename = &body.1; + let mut select = Vec::new(); + select.push("*"); + let columnnamestmt = querytable::grab_columnnames_schema(tablename, database); + let column = querytable::exec_map(&mut connection, &columnnamestmt.unwrap()); + let columntypestmt = querytable::grab_columntypes_schema(tablename, database); + let columntype = querytable::exec_map(&mut connection, &columntypestmt.unwrap()); + let constraintstmt = querytable::query_constraints(tablename, database); + let constraint = querytable::exec_map_tuple(&mut connection, &constraintstmt.unwrap()); + + let json = querytable::query_table_schema( + column.unwrap(), + columntype.unwrap(), + constraint.unwrap(), + ); //let queryresult = querytable::query_table_schema( // &database, // &tablename, @@ -941,21 +967,23 @@ async fn querytableschema(body: web::Path<(String, String, String)>) -> impl Res //let json = // querytable::build_jsonschema(queryresult, &database, &tablename, &mut connection); - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body(json.to_string()) - } else { - HttpResponse::Ok() + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body(json.to_string()) +} +async fn querydatabase( + auth: auth::Authenticated, + body: web::Path<(String, String, String)>, +) -> impl Responder { + if auth.role != "admin" { + return HttpResponse::Forbidden() .content_type("text/json; charset=utf-8") - .body("Invalid API Key") + .body("Admin role required"); } -} -async fn querydatabase(body: web::Path<(String, String, String)>) -> impl Responder { - let valid = connkey::search_apikey_admin(&body.2); - if valid.unwrap() == true { - let mut connection = dbconnect::internalqueryconn(); - let database = &body.0; + let mut connection = dbconnect::internalqueryconn(); + + let database = &body.0; //expand will be true or false let expand = &body.1; //turn into bool @@ -996,14 +1024,9 @@ async fn querydatabase(body: web::Path<(String, String, String)>) -> impl Respon println!("Storage: {:?}", storage); _json = querytable::query_database_schema(storage, database); } - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body(_json.to_string()) - } else { - HttpResponse::Ok() - .content_type("text/json; charset=utf-8") - .body("Invalid API Key") - } + HttpResponse::Ok() + .content_type("text/json; charset=utf-8") + .body(_json.to_string()) } //async fn getcreate(form: web::Form) -> impl Responder { // let mut connection = dbconnect::database_connection(&form.database.to_string()); diff --git a/src/pushdata/gettablecol.rs b/src/pushdata/gettablecol.rs index 78750ff..05adf75 100644 --- a/src/pushdata/gettablecol.rs +++ b/src/pushdata/gettablecol.rs @@ -10,17 +10,19 @@ pub fn get_table_col( database_name: &str, ) -> std::result::Result, Box> { let mut querystring: String = - String::from("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='"); + String::from("SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA='"); querystring.push_str(database_name.to_string().as_str()); querystring.push_str("' AND TABLE_NAME='"); //testcsv' AND TABLE_NAME='"); querystring.push_str(table_name.to_string().as_str()); querystring.push_str("'"); - querystring.push_str(" and COLUMN_NAME != 'INTERNAL_PRIMARY_KEY'"); - querystring.push_str(" and COLUMN_NAME != 'GPS_ID'"); - querystring.push_str(" and COLUMN_NAME != 'X_COORD'"); - querystring.push_str(" and COLUMN_NAME != 'Y_COORD'"); - querystring.push_str(" and COLUMN_NAME != 'Attachment'"); + // In Postgres, unquoted identifiers are stored lowercased in information_schema, + // so filter on the lowercased names of our internal/system columns. + querystring.push_str(" and COLUMN_NAME != 'internal_primary_key'"); + querystring.push_str(" and COLUMN_NAME != 'gps_id'"); + querystring.push_str(" and COLUMN_NAME != 'x_coord'"); + querystring.push_str(" and COLUMN_NAME != 'y_coord'"); + querystring.push_str(" and COLUMN_NAME != 'attachment'"); #[derive(QueryableByName)] struct ColName { #[diesel(sql_type = Text)] diff --git a/src/querytable.rs b/src/querytable.rs index b576dda..c707a5b 100644 --- a/src/querytable.rs +++ b/src/querytable.rs @@ -38,12 +38,11 @@ pub fn exec_map( #[derive(QueryableByName)] struct SingleString { #[diesel(sql_type = Text)] - #[allow(non_snake_case)] - COLUMN_NAME: String, + column_name: String, } let rows: Vec = sql_query(query).load(conn)?; - Ok(rows.into_iter().map(|r| r.COLUMN_NAME).collect()) + Ok(rows.into_iter().map(|r| r.column_name).collect()) } pub fn exec_map_tuple( @@ -53,34 +52,35 @@ pub fn exec_map_tuple( #[derive(QueryableByName)] struct TwoStrings { #[diesel(sql_type = Text)] - #[allow(non_snake_case)] - COLUMN_NAME: String, + column_name: String, #[diesel(sql_type = Text)] - #[allow(non_snake_case)] - CONSTRAINT_NAME: String, + constraint_name: String, } let rows: Vec = sql_query(query).load(conn)?; Ok(rows .into_iter() - .map(|r| (r.COLUMN_NAME, r.CONSTRAINT_NAME)) + .map(|r| (r.column_name, r.constraint_name)) .collect()) } pub fn grab_columntypes( table: &str, database: &str, ) -> std::result::Result> { - let mut query = - String::from("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '"); + // Postgres: use data_type and alias it to column_name so exec_map can + // deserialize it with the SingleString { column_name } struct. + let mut query = String::from( + "SELECT data_type AS column_name FROM information_schema.columns WHERE table_schema = '", + ); query.push_str(database); query.push_str("' AND TABLE_NAME = '"); query.push_str(table); query.push_str("'"); - query.push_str("And COLUMN_NAME != 'INTERNAL_PRIMARY_KEY'"); - query.push_str("And COLUMN_NAME != 'GPS_ID'"); - query.push_str("And COLUMN_NAME != 'X_COORD'"); - query.push_str("And COLUMN_NAME != 'Y_COORD'"); - query.push_str("And COLUMN_NAME != 'Attachment'"); + query.push_str(" AND column_name != 'internal_primary_key'"); + query.push_str(" AND column_name != 'gps_id'"); + query.push_str(" AND column_name != 'x_coord'"); + query.push_str(" AND column_name != 'y_coord'"); + query.push_str(" AND column_name != 'attachment'"); Ok(query) } @@ -89,8 +89,9 @@ pub fn grab_all_columntypes( table: &str, database: &str, ) -> std::result::Result> { - let mut query = - String::from("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '"); + let mut query = String::from( + "SELECT data_type AS column_name FROM information_schema.columns WHERE table_schema = '", + ); query.push_str(database); query.push_str("' AND TABLE_NAME = '"); query.push_str(table); @@ -102,8 +103,9 @@ pub fn grab_columntypes_schema( table: &str, database: &str, ) -> std::result::Result> { - let mut query = - String::from("SELECT COLUMN_TYPE FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '"); + let mut query = String::from( + "SELECT data_type AS column_name FROM information_schema.columns WHERE table_schema = '", + ); query.push_str(database); query.push_str("' AND TABLE_NAME = '"); query.push_str(table); @@ -392,12 +394,11 @@ pub fn exec_grab_tablenames( #[derive(QueryableByName)] struct SingleStringRow { #[diesel(sql_type = Text)] - #[allow(non_snake_case)] - TABLE_NAME: String, + table_name: String, } let rows: Vec = sql_query(query).load(conn)?; - Ok(rows.into_iter().map(|r| r.TABLE_NAME).collect()) + Ok(rows.into_iter().map(|r| r.table_name).collect()) } pub fn json_table_names(queryresult: Vec, database: &str) -> serde_json::Value { diff --git a/src/tablecreate.rs b/src/tablecreate.rs index 0c2f63a..86cd8f8 100644 --- a/src/tablecreate.rs +++ b/src/tablecreate.rs @@ -49,23 +49,28 @@ pub fn create_table_web( query.push_str(" ("); query.push_str("INTERNAL_PRIMARY_KEY SERIAL PRIMARY KEY, "); for i in 0..column_names.len() { - let valid = validate_unprotected_term(column_names[i].0.as_str()); + // validate the actual column name (the value part) + let valid = validate_unprotected_term(column_names[i].1.as_str()); if valid.0 == false { - println!("Invalid column name: {}", column_names[i].0); + println!("Invalid column name: {}", column_names[i].1); let mut error = String::from("Invalid column name: "); - error.push_str(column_names[i].0.as_str()); + error.push_str(column_names[i].1.as_str()); return error; } - query.push_str(column_names[i].1.as_str()); + let col_name = column_names[i].1.as_str(); + let col_type = column_types[i].1.as_str(); + + query.push_str(col_name); query.push_str(" "); - query.push_str(column_types[i].1.as_str()); + query.push_str(col_type); //grab first 7 characters of column type // - if column_types[i].1.get(0..7) == Some("VARCHAR") { - query.push_str(" DEFAULT \"\""); + if col_type.get(0..7) == Some("VARCHAR") { + // Use single quotes for empty string default in Postgres + query.push_str(" DEFAULT ''"); } - if column_types[i].1.get(0..3) == Some("INT") { + if col_type.get(0..3) == Some("INT") { query.push_str(" DEFAULT 0"); } @@ -114,8 +119,12 @@ pub fn create_table_web_gps(database: &str, table_name: &str) -> String { pub fn parse_json(json: Vec<(String, String)>) -> (Vec<(String, String)>, Vec<(String, String)>) { let mut columnstr = json[0].1.clone(); let mut datatypestr = json[1].1.clone(); + // remove wrapping quotes and backslashes from the JSON-encoded arrays + // first drop any plain double quotes that weren't escaped columnstr = columnstr.replace("\"", ""); datatypestr = datatypestr.replace("\"", ""); + columnstr = columnstr.replace("\\\"", ""); + datatypestr = datatypestr.replace("\\\"", ""); columnstr = columnstr.replace("[", ""); datatypestr = datatypestr.replace("[", ""); columnstr = columnstr.replace("]", ""); @@ -134,6 +143,9 @@ pub fn parse_json(json: Vec<(String, String)>) -> (Vec<(String, String)>, Vec<(S datatypestr = datatypestr.replace("{", ""); columnstr = columnstr.replace("}", ""); datatypestr = datatypestr.replace("}", ""); + // remove any backslashes left from JSON escaping + columnstr = columnstr.replace("\\", ""); + datatypestr = datatypestr.replace("\\", ""); let column = columnstr.split(","); let datatype = datatypestr.split(","); diff --git a/src/update.rs b/src/update.rs index e14edd0..405d4ef 100644 --- a/src/update.rs +++ b/src/update.rs @@ -9,21 +9,46 @@ pub fn updaterecord(database: &str, table: &str, date: Vec stmt.push_str("."); stmt.push_str(table); stmt.push_str(" SET "); - for i in 1..data.len() { - stmt.push_str(&data[i].0); - stmt.push_str("= \""); + // Find the primary key entry (INTERNAL_PRIMARY_KEY), case-insensitive. + let pk_index_opt = data + .iter() + .position(|(k, _)| k.eq_ignore_ascii_case("INTERNAL_PRIMARY_KEY")); - stmt.push_str(&data[i].1); - stmt.push_str("\""); - if i != data.len() - 1 { + let pk_index = match pk_index_opt { + Some(idx) => idx, + None => { + // No primary key provided; skip this record. + continue; + } + }; + + // Build SET clause for all non-PK fields. + let mut first = true; + for (i, (col, val)) in data.iter().enumerate() { + if i == pk_index { + continue; + } + if !first { stmt.push_str(", "); } + first = false; + + // Clean and quote value as a SQL string literal. + let mut valuedata = val.replace("\"", ""); + valuedata = valuedata.replace("'", "''"); + + stmt.push_str(col); + stmt.push_str(" = '"); + stmt.push_str(&valuedata); + stmt.push_str("'"); } - stmt.push_str(" WHERE "); - stmt.push_str("INTERNAL_PRIMARY_KEY"); - stmt.push_str("="); - stmt.push_str(&data[0].1); + // WHERE clause on primary key value. + let mut pk_value = data[pk_index].1.replace("\"", ""); + pk_value = pk_value.replace("'", "''"); + + stmt.push_str(" WHERE INTERNAL_PRIMARY_KEY = "); + stmt.push_str(&pk_value); stmts.push(stmt); } diff --git a/tests/api_flow.rs b/tests/api_flow.rs new file mode 100644 index 0000000..00440e9 --- /dev/null +++ b/tests/api_flow.rs @@ -0,0 +1,172 @@ +use actix_web::{test, web, App}; + +// Import the same modules main.rs uses +use dbwebconnect::auth; +use dbwebconnect::createdatabase; +use dbwebconnect::createrecord; +use dbwebconnect::createrelationship; +use dbwebconnect::dbconnect; +use dbwebconnect::delete; +use dbwebconnect::getfields; +use dbwebconnect::initconnect; +use dbwebconnect::insertrecords; +use dbwebconnect::pushdata; +use dbwebconnect::querytable; +use dbwebconnect::relationships; +use dbwebconnect::tablecreate; +use dbwebconnect::update; + +// NOTE: These are integration-style tests that expect: +// - A running Postgres instance reachable via DATABASE_URL +// - The initapi.sql migrations applied (so Auth, Relationships schemas exist) +// You can run them with `cargo test` once those prerequisites are satisfied. + +fn app_factory() -> App> { + App::new() + .app_data(web::Data::new(auth::AppState::from_env())) + .service( + web::resource("/") + .route(web::get().to(crate::getinitializeconnect)) + .route(web::post().to(crate::postinitializeconnect)), + ) + .route("/auth/register", web::post().to(auth::register)) + .route("/auth/login", web::post().to(auth::login)) + .route("/admin/users", web::get().to(auth::list_users)) + .route( + "/admin/users/schemas", + web::post().to(auth::update_user_schemas), + ) + .route("/health", web::get().to(crate::health)) + .route("/getkey/{database}&apikey={apikey}", web::get().to(crate::getkey)) + .route( + "/createtable/{database}&table={table}&gps={gps}&apikey={apikey}", + web::post().to(crate::createtableweb), + ) + .route( + "/droptable/{database}&table={table}&apikey={apikey}", + web::post().to(crate::droptableweb), + ) + .route( + "/createdatabase/{database}&apikey={apikey}", + web::post().to(crate::createnewdbweb), + ) + .route( + "/query/{database}&table={table}&select={select}&where={where}&expand={expand}&apikey={api}", + web::get().to(crate::querytojson), + ) + .route( + "/querytableschema/{database}&table={table}&apikey={api}", + web::get().to(crate::querytableschema), + ) + .route( + "/querydatabase/{database}&expand={expand}&apikey={api}", + web::get().to(crate::querydatabase), + ) + .route( + "/queryrelationship/{database}&relationship={relationship}&apikey={api}", + web::get().to(crate::queryrelationship), + ) + .route( + "/queryall/{database}&table={table}&depth={depth}&apikey={api}", + web::get().to(crate::queryall), + ) + .route( + "/insert/{database}&table={table}&apikey={api}", + web::post().to(crate::dbinsert), + ) + .route( + "/insertattachment/{database}&table={table}&apikey={api}", + web::post().to(crate::dbinsertattachment), + ) + .route( + "/retrieveattachment/{database}&table={table}&id={id}&apikey={api}", + web::get().to(crate::retrieveattachment), + ) + .route( + "/updaterecord/{database}&table={table}&apikey={api}", + web::post().to(crate::dbupdaterecord), + ) + .route( + "/relationship/{database}&apikey={api}", + web::post().to(crate::createrelationshipweb), + ) + .route( + "relateparent/{database}&parent_table={parent_table}&child_table={child_table}&relationship_name={relationship_name}&apikey={api}", + web::post().to(crate::createrelationshipparentweb), + ) + .route( + "/deleterecord/{database}&table={table}&apikey={api}", + web::post().to(crate::deleterecord), + ) +} + +#[actix_web::test] +async fn health_smoke() { + let app = test::init_service(app_factory()).await; + let req = test::TestRequest::get().uri("/health").to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); +} + +// High-level flow: register + login + simple schema lifecycle +#[actix_web::test] +async fn auth_and_schema_lifecycle_smoke() { + let app = test::init_service(app_factory()).await; + + // 1) Register first user (becomes admin) + let register_body = serde_json::json!({ + "email": "admin@test.local", + "password": "Password123!" + }); + let req = test::TestRequest::post() + .uri("/auth/register") + .set_json(®ister_body) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + + // 2) Login to get JWT + let login_body = serde_json::json!({ + "email": "admin@test.local", + "password": "Password123!" + }); + let req = test::TestRequest::post() + .uri("/auth/login") + .set_json(&login_body) + .to_request(); + let resp = test::call_and_read_body(&app, req).await; + let json: serde_json::Value = serde_json::from_slice(&resp).unwrap(); + let token = json["token"].as_str().unwrap().to_string(); + + // 3) Create logical schema + let req = test::TestRequest::post() + .uri("/createdatabase/test_schema&apikey=ignored") + .insert_header(("Authorization", format!("Bearer {}", token))) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); + + // 4) Create simple table in that schema + let body = serde_json::json!({ + "columns": "[{\"name:id\"},{\"name:name\"}]", + "types": "[{\"type:INT\"},{\"type:VARCHAR(255)\"}]" + }); + let req = test::TestRequest::post() + .uri("/createtable/test_schema&table=test_table&gps=false&apikey=ignored") + .insert_header(("Authorization", format!("Bearer {}", token))) + .set_json(&body) + .to_request(); + let resp = test::call_service(&app, req).await; + assert!(resp.status().is_success()); +} + +// Skeletons for additional scenarios; fill out as needed. +// - attachment_roundtrip +// - relationship_roundtrip +// - queryall_depth_control From 0b9ba4e1d75bc6b74fcea8df650c6b5b48afe729 Mon Sep 17 00:00:00 2001 From: Kyle Baumgarten Date: Sun, 25 Jan 2026 23:20:17 -0500 Subject: [PATCH 3/3] fix docker compose --- docker-compose.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4839fff..fcbec19 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -12,7 +12,7 @@ services: ports: - "5432:5432" volumes: - - ./data:/var/lib/postgresql/data + - db_data:/var/lib/postgresql/data - ./sql-scripts:/docker-entrypoint-initdb.d app: @@ -24,4 +24,5 @@ services: environment: - DATABASE_URL=${DATABASE_URL} - JWT_SECRET=${JWT_SECRET} - - PWD=${PASSWORD} +volumes: + db_data: