diff --git a/Cargo.lock b/Cargo.lock index 7976442..fac2a80 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,6 +39,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" + [[package]] name = "android-tzdata" version = "0.1.1" @@ -136,6 +142,15 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic" version = "0.5.3" @@ -190,6 +205,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bcrypt" version = "0.15.1" @@ -220,6 +241,9 @@ name = "bitflags" version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +dependencies = [ + "serde", +] [[package]] name = "bitvec" @@ -297,12 +321,6 @@ version = "1.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" -[[package]] -name = "case" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6c0e7b807d60291f42f33f58480c0bfafe28ed08286446f45e463728cf9c1c" - [[package]] name = "cc" version = "1.1.21" @@ -328,6 +346,7 @@ dependencies = [ "iana-time-zone", "js-sys", "num-traits", + "serde", "wasm-bindgen", "windows-targets 0.52.6", ] @@ -348,6 +367,21 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "convert_case" version = "0.4.0" @@ -365,6 +399,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -380,6 +424,36 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crossbeam-queue" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" + [[package]] name = "crypto-common" version = "0.1.6" @@ -431,6 +505,17 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + [[package]] name = "deranged" version = "0.3.11" @@ -504,15 +589,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "subtle", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +dependencies = [ + "serde", +] [[package]] name = "encoding_rs" @@ -529,7 +624,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21cdad81446a7f7dc43f6a77409efeb9733d2fa65553efef6018ef257c959b73" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", "syn 1.0.109", @@ -574,6 +669,28 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "event-listener" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + [[package]] name = "fastrand" version = "2.1.1" @@ -594,12 +711,38 @@ dependencies = [ "version_check", ] +[[package]] +name = "flume" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[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.2.1" @@ -623,7 +766,6 @@ checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" dependencies = [ "futures-channel", "futures-core", - "futures-executor", "futures-io", "futures-sink", "futures-task", @@ -657,6 +799,17 @@ dependencies = [ "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" version = "0.3.30" @@ -774,6 +927,19 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +dependencies = [ + "ahash", + "allocator-api2", +] + +[[package]] +name = "hashlink" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +dependencies = [ + "hashbrown", +] [[package]] name = "heck" @@ -781,6 +947,12 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" @@ -799,6 +971,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.12.1" @@ -808,6 +989,15 @@ dependencies = [ "digest", ] +[[package]] +name = "home" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +dependencies = [ + "windows-sys 0.52.0", +] + [[package]] name = "hostname" version = "0.3.1" @@ -1025,6 +1215,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libc" @@ -1032,6 +1225,23 @@ version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -1127,6 +1337,12 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.8.0" @@ -1148,33 +1364,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "mongod" -version = "0.2.2" -source = "git+https://git.hydrar.de/jmarya/mongod#58150002e60802d7b00fe0537e4a2e8c4e389a4a" -dependencies = [ - "chrono", - "futures", - "mongod_derive", - "mongodb", - "regex", - "serde", - "serde_json", - "tokio", - "uuid", -] - -[[package]] -name = "mongod_derive" -version = "0.1.0" -source = "git+https://git.hydrar.de/jmarya/mongod#58150002e60802d7b00fe0537e4a2e8c4e389a4a" -dependencies = [ - "case", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "mongodb" version = "2.8.2" @@ -1241,6 +1430,33 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1251,12 +1467,49 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + [[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-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1264,6 +1517,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1291,12 +1545,62 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "openssl" +version = "0.10.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" +dependencies = [ + "bitflags 2.6.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.77", +] + +[[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.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f9e8deee91df40a943c71b917e5874b951d32a802526c85721ce3b776c929d6" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "overload" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.3" @@ -1320,6 +1624,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + [[package]] name = "pbkdf2" version = "0.11.0" @@ -1352,6 +1662,15 @@ dependencies = [ "syn 2.0.77", ] +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1370,6 +1689,33 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "powerfmt" version = "0.2.0" @@ -1655,6 +2001,26 @@ dependencies = [ "uncased", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1754,6 +2120,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9aaafd5a2b6e3d657ff009d82fbd630b6bd54dd4eb06f21693925cdf80f9b8b" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -1776,6 +2151,29 @@ dependencies = [ "untrusted", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.6.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "0.9.0" @@ -1848,6 +2246,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_with" version = "1.14.0" @@ -1881,6 +2291,17 @@ dependencies = [ "digest", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha2" version = "0.10.8" @@ -1916,6 +2337,16 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core", +] + [[package]] name = "slab" version = "0.4.9" @@ -1930,6 +2361,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "socket2" @@ -1956,6 +2390,232 @@ name = "spin" version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "sqlformat" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bba3a93db0cc4f7bdece8bb09e77e2e785c20bfebf79eb8340ed80708048790" +dependencies = [ + "nom", + "unicode_categories", +] + +[[package]] +name = "sqlx" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +dependencies = [ + "atoi", + "byteorder", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-channel", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown", + "hashlink", + "hex", + "indexmap", + "log", + "memchr", + "native-tls", + "once_cell", + "paste", + "percent-encoding", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlformat", + "thiserror", + "tokio", + "tokio-stream", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.77", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.77", + "tempfile", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand", + "rsa", + "serde", + "sha1", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.6.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand", + "serde", + "serde_json", + "sha2", + "smallvec", + "sqlx-core", + "stringprep", + "thiserror", + "tracing", + "uuid", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "tracing", + "url", + "uuid", +] [[package]] name = "stable-pattern" @@ -2029,13 +2689,13 @@ dependencies = [ "data-encoding", "env_logger", "log", - "mongod", "mongodb", "rand", "rocket", "rocket_cors", "serde", "serde_json", + "sqlx", "uuid", "walkdir", ] @@ -2250,6 +2910,7 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ + "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -2444,6 +3105,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +[[package]] +name = "unicode_categories" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" + [[package]] name = "untrusted" version = "0.9.0" @@ -2483,6 +3150,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version_check" version = "0.9.5" @@ -2514,6 +3187,12 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.93" @@ -2575,6 +3254,16 @@ version = "0.25.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +[[package]] +name = "whoami" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" +dependencies = [ + "redox_syscall", + "wasite", +] + [[package]] name = "widestring" version = "1.1.0" diff --git a/Cargo.toml b/Cargo.toml index caf4a90..cb0cbac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,12 +9,12 @@ rocket = { version = "0.5.1", features = ["json"] } rocket_cors = "0.6.0" serde = { version = "1.0.203", features = ["derive"] } serde_json = "1.0.117" -uuid = { version = "1.9.1", features = ["v4"] } walkdir = "2.5.0" -mongod = { git = "https://git.hydrar.de/jmarya/mongod", features = ["cache"] } bcrypt = "0.15.1" data-encoding = "2.6.0" rand = "0.8.5" env_logger = "0.11.5" log = "0.4.22" -chrono = "0.4.38" +chrono = { version = "0.4.38", features = ["serde"] } +uuid = { version = "1.8.0", features = ["v4", "serde"] } +sqlx = { version = "0.8", features = ["postgres", "runtime-tokio-native-tls", "derive", "uuid", "chrono", "json"] } \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index fe4c5a6..dc8e996 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,20 +5,19 @@ services: ports: - "8080:8080" depends_on: - - mongodb + - postgres volumes: - ./data:/data # Runtime data (optional) - ./media:/media # Audio files environment: - - "DB_URI=mongodb://user:pass@mongodb:27017" - - "DB=synthwrld" + - "DATABASE_URL=postgres://user:pass@postgres/synthwave" - mongodb: - image: mongo:latest - ports: - - "27017:27017" - environment: - MONGO_INITDB_ROOT_USERNAME: user - MONGO_INITDB_ROOT_PASSWORD: pass + postgres: + image: timescale/timescaledb:latest-pg16 + restart: always volumes: - - ./db:/data/db + - ./db:/var/lib/postgresql/data/ + environment: + - POSTGRES_USER=user + - POSTGRES_PASSWORD=pass + - POSTGRES_DB=synthwave diff --git a/migrations/0000_init.sql b/migrations/0000_init.sql new file mode 100644 index 0000000..4b5f4a7 --- /dev/null +++ b/migrations/0000_init.sql @@ -0,0 +1,49 @@ + +CREATE TABLE IF NOT EXISTS user ( + username varchar(255) NOT NULL PRIMARY KEY, + password text NOT NULL, + user_role text NOT NULL DEFAULT 'Regular' CHECK (user_role IN ('regular', 'admin')) +); + +CREATE TABLE IF NOT EXISTS user_session ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + token text NOT NULL, + user varchar(255) NOT NULL, + FOREIGN KEY(user) REFERENCES user(username) +); + +CREATE TABLE IF NOT EXISTS artist ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + name text NOT NULL +); + +CREATE TABLE IF NOT EXISTS album ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + title text NOT NULL, + artist UUID + FOREIGN KEY(artist) REFERENCES artist(id) +); + +CREATE TABLE IF NOT EXISTS track ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + path text NOT NULL, + title text, + date_added timestampz NOT NULL DEFAULT current_timestamp, + album UUID, + artist UUID, + meta jsonb, + FOREIGN KEY(album) REFERENCES album(id), + FOREIGN KEY(artist) REFERENCES artist(id) +); + +CREATE TABLE IF NOT EXISTS events ( + id UUID NOT NULL PRIMARY KEY DEFAULT gen_random_uuid(), + time timestampz NOT NULL DEFAULT current_timestamp, + kind text CHECK (kind IN ('play', 'played', 'stop')), + user VARCHAR(255) NOT NULL, + track UUID NOT NULL, + FOREIGN KEY(user) REFERENCES user(username), + FOREIGN KEY(track) REFERENCES track(id) +); + +SELECT create_hypertable('events', by_range('time')); diff --git a/src/library/album.rs b/src/library/album.rs index 430ee24..48d5e82 100644 --- a/src/library/album.rs +++ b/src/library/album.rs @@ -1,41 +1,33 @@ -use mongod::{ - assert_reference_of, - derive::{Model, Referencable}, - Model, Referencable, Reference, Validate, -}; use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use serde_json::json; +use sqlx::FromRow; -use crate::library::artist::Artist; -use mongod::ToAPI; +use crate::{get_pg, library::artist::Artist}; use super::track::Track; -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Album { - pub _id: String, + pub id: uuid::Uuid, pub title: String, - pub artist_id: Option, + pub artist: Option, } impl Album { pub async fn create(title: &str, artist: Option<&str>) -> Self { - let a = Self { - _id: uuid::Uuid::new_v4().to_string(), - title: title.to_string(), - artist_id: if let Some(artist) = artist { - Some(Reference::new_raw(artist)) - } else { - None - }, - }; - a.insert().await.unwrap(); - a + sqlx::query_as("INSERT INTO album (title, artist) VALUES ($1, $2) RETURNING *") + .bind(title) + .bind(artist) + .fetch_one(get_pg!()) + .await + .unwrap() } - pub async fn get_tracks_of_album(album: &str) -> Vec { - Track::find(doc! { "album_id": album}, None, None) + pub async fn get_tracks_of_album(album: &uuid::Uuid) -> Vec { + sqlx::query_as("SELECT * FROM track WHERE album = $1") + .bind(album) + .fetch_all(get_pg!()) .await .unwrap() } @@ -44,7 +36,7 @@ impl Album { /// /// This method first retrieves the list of tracks in the album, then looks for a file named "cover.{ext}" where {ext} is one of png, jpg, jpeg, avif. The first existing cover file found will be returned. pub async fn get_cover(&self) -> Option { - let track_path = Self::get_tracks_of_album(&format!("album::{}", self._id)) + let track_path = Self::get_tracks_of_album(&self.id) .await .first()? .path @@ -63,27 +55,17 @@ impl Album { } } -impl ToAPI for Album { +impl Album { async fn api(&self) -> serde_json::Value { json!({ - "id": &self._id, + "id": &self.id, "title": &self.title, - "artist": self.artist_id.as_ref().map(mongod::Reference::id), + "artist": self.artist, "cover_url": if self.get_cover().await.is_some() { - Some(format!("/album/{}/cover", self._id)) + Some(format!("/album/{}/cover", self.id)) } else { None } }) } } - -impl Validate for Album { - async fn validate(&self) -> Result<(), String> { - if let Some(artist_id) = &self.artist_id { - assert_reference_of!(artist_id, Artist); - } - - Ok(()) - } -} diff --git a/src/library/artist.rs b/src/library/artist.rs index 557b29c..b6e47bb 100644 --- a/src/library/artist.rs +++ b/src/library/artist.rs @@ -1,35 +1,31 @@ -use mongod::{ - derive::{Model, Referencable}, - reference_of, Model, Referencable, Validate, -}; -use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use serde_json::json; +use sqlx::FromRow; -use mongod::ToAPI; +use crate::get_pg; use super::track::Track; -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Artist { - pub _id: String, + pub id: uuid::Uuid, pub name: String, } impl Artist { pub async fn create(name: &str) -> Self { - let a = Artist { - _id: uuid::Uuid::new_v4().to_string(), - name: name.to_string(), - }; - a.insert().await.unwrap(); - a + sqlx::query_as("INSERT INTO artist (name) VALUES ($1) RETURNING id, name") + .bind(name) + .fetch_one(get_pg!()) + .await + .unwrap() } /// Gets the image of an artist or `None` if it can't be found. /// /// This function gets a track from the artist. It then expects the folder structure to be `Artist/Album/Track.ext` and searches for an image file named `artist` in the artist folder. - pub async fn get_image_of(id: &str) -> Option { + pub async fn get_image_of(id: &uuid::Uuid) -> Option { + // todo : fix let track_path = Track::find_one(doc! { "artist_id": reference_of!(Artist, id)}) .await? .path; @@ -49,22 +45,16 @@ impl Artist { } } -impl ToAPI for Artist { +impl Artist { async fn api(&self) -> serde_json::Value { json!({ - "id": &self._id, + "id": &self.id, "name": &self.name, - "image": if Artist::get_image_of(self.id()).await.is_some() { - Some(format!("/artist/{}/image", self.id())) + "image": if Artist::get_image_of(&self.id).await.is_some() { + Some(format!("/artist/{}/image", self.id)) } else { None } }) } } - -impl Validate for Artist { - async fn validate(&self) -> Result<(), String> { - Ok(()) - } -} diff --git a/src/library/event.rs b/src/library/event.rs index f06e038..3a92d2b 100644 --- a/src/library/event.rs +++ b/src/library/event.rs @@ -1,25 +1,19 @@ -use mongod::{ - assert_reference_of, - derive::{Model, Referencable}, - Model, Referencable, Reference, Validate, -}; -use mongodb::bson::doc; use serde::{Deserialize, Serialize}; +use sqlx::prelude::FromRow; -use crate::library::user::User; +use crate::{get_pg, library::user::User}; -use super::track::Track; - -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Event { - pub _id: String, + pub id: uuid::Uuid, + pub timestamp: chrono::DateTime, pub kind: EventKind, - pub user: Reference, - pub track: Reference, - pub timestamp: i64, + pub user: String, + pub track: uuid::Uuid, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)] +#[sqlx(type_name = "event_kind", rename_all = "lowercase")] pub enum EventKind { Play, Played, @@ -27,36 +21,21 @@ pub enum EventKind { } impl Event { - pub async fn create(kind: EventKind, user: &User, track: Reference) -> Self { - let event = Self { - _id: uuid::Uuid::new_v4().to_string(), - kind: kind, - user: user.reference(), - track, - timestamp: chrono::Utc::now().timestamp(), - }; - - event.insert().await.unwrap(); - - event + pub async fn create(kind: EventKind, user: &User, track: uuid::Uuid) -> Self { + sqlx::query_as("INSERT INTO events (kind, user, track) VALUES ($1, $2, $3) RETURNING *") + .bind(kind) + .bind(&user.username) + .bind(track) + .fetch_one(get_pg!()) + .await + .unwrap() } pub async fn get_latest_events_of(u: &User) -> Vec { - Self::find( - doc! { "user": u.reference() }, - Some(300), - Some(doc! { "timestamp": -1 }), - ) - .await - .unwrap() - } -} - -impl Validate for Event { - async fn validate(&self) -> Result<(), String> { - assert_reference_of!(self.user, User); - assert_reference_of!(self.track, Track); - - Ok(()) + sqlx::query_as("SELECT * FROM events WHERE user = $1 ORDER BY time DESC LIMIT 300") + .bind(&u.username) + .fetch_all(get_pg!()) + .await + .unwrap() } } diff --git a/src/library/track.rs b/src/library/track.rs index 4d1cd25..1946c88 100644 --- a/src/library/track.rs +++ b/src/library/track.rs @@ -1,61 +1,50 @@ -use std::collections::HashSet; - -use mongod::{ - assert_reference_of, - derive::{Model, Referencable}, - Model, Referencable, Reference, Validate, -}; -use mongodb::bson::doc; use serde::{Deserialize, Serialize}; use serde_json::json; +use sqlx::prelude::FromRow; +use std::collections::HashSet; -use crate::library::{album::Album, artist::Artist}; -use mongod::ToAPI; +use crate::{ + get_pg, + library::{album::Album, artist::Artist}, +}; use super::{event::Event, metadata::AudioMetadata, user::User}; -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Track { - pub _id: String, + pub id: uuid::Uuid, pub path: String, pub title: String, - pub date_added: i64, - pub album_id: Option, - pub artist_id: Option, - pub meta: Option, + pub date_added: chrono::DateTime, + pub album: Option, + pub artist: Option, + pub meta: Option, } impl Track { pub async fn create(data: &serde_json::Map) { - let mut t = Self { - _id: uuid::Uuid::new_v4().to_string(), - path: data.get("path").unwrap().as_str().unwrap().to_string(), - title: data.get("title").unwrap().as_str().unwrap().to_string(), - date_added: chrono::Utc::now().timestamp(), - album_id: None, - artist_id: None, - meta: data.get("meta").map(|x| AudioMetadata(x.clone())), - }; - t.insert().await.unwrap(); - t.update(&serde_json::to_value(data).unwrap()) - .await - .unwrap(); + sqlx::query("INSERT INTO track (path, title, meta) VALUES ($1, $2, $4)") + .bind(data.get("path").unwrap().as_str().unwrap().to_string()) + .bind(data.get("title").unwrap().as_str().unwrap().to_string()) + .bind(data.get("meta")) + .fetch(get_pg!()); } pub async fn get_latest_of_user(u: &User) -> Vec { let latest_events = Event::get_latest_events_of(u).await; let mut ids = HashSet::new(); - let mut tracks = vec![]; for event in latest_events { - let track: Track = event.track.get().await; - if !ids.contains(&track._id) { - ids.insert(track._id.clone()); - tracks.push(track); + if !ids.contains(&event.track) { + ids.insert(event.track.clone()); } } - tracks + sqlx::query_as("SELECT * FROM track WHERE id = ANY($1)") + .bind(ids.into_iter().collect::>()) + .fetch_all(get_pg!()) + .await + .unwrap() } /// Transcode audio to OPUS with `bitrate` @@ -100,6 +89,7 @@ impl Track { /// Find tracks with no album or artist pub async fn get_orphans() -> Vec { + // todo : fix Self::find( doc! { "artist_id": None::, @@ -113,9 +103,10 @@ impl Track { } } -impl ToAPI for Track { +impl Track { async fn api(&self) -> serde_json::Value { - let (cover, album_title, album_id) = if let Some(album_ref) = &self.album_id { + // todo : fix + let (cover, album_title, album_id) = if let Some(album_ref) = &self.album { let album = album_ref.get::().await; (album.get_cover().await.is_some(), album.title, album._id) @@ -123,7 +114,7 @@ impl ToAPI for Track { (false, String::new(), String::new()) }; - let artist_title = if let Some(artist_ref) = &self.artist_id { + let artist_title = if let Some(artist_ref) = &self.artist { artist_ref .get_partial::(json!({"name": 1})) .await @@ -133,33 +124,19 @@ impl ToAPI for Track { }; json!({ - "id": self._id, + "id": self.id, "title": self.title, - "track_number": self.meta.as_ref().map(super::metadata::AudioMetadata::track_number), + "track_number": self.meta.as_ref().map(|x| AudioMetadata(*x).track_number()), "meta": serde_json::to_value(&self.meta).unwrap(), - "album_id": self.album_id.as_ref().map(|x| x.id().to_string()), + "album_id": self.album, "album": album_title, "cover": if cover { Some(format!("/album/{album_id}/cover")) } else { None }, - "artist_id": self.artist_id.as_ref().map(|x| x.id().to_string()), + "artist_id": self.artist, "artist": artist_title }) } } - -impl Validate for Track { - async fn validate(&self) -> Result<(), String> { - if let Some(artist_id) = &self.artist_id { - assert_reference_of!(artist_id, Artist); - } - - if let Some(album_id) = &self.artist_id { - assert_reference_of!(album_id, Artist); - } - - Ok(()) - } -} diff --git a/src/library/user.rs b/src/library/user.rs index a3ee36f..c350a8c 100644 --- a/src/library/user.rs +++ b/src/library/user.rs @@ -1,15 +1,10 @@ use data_encoding::HEXUPPER; -use mongod::{ - assert_reference_of, - derive::{Model, Referencable}, - Model, Referencable, Reference, Validate, -}; -use mongodb::bson::doc; use rand::RngCore; use serde::{Deserialize, Serialize}; use serde_json::json; +use sqlx::FromRow; -use mongod::ToAPI; +use crate::get_pg; fn gen_token(token_length: usize) -> String { let mut token_bytes = vec![0u8; token_length]; @@ -19,65 +14,66 @@ fn gen_token(token_length: usize) -> String { HEXUPPER.encode(&token_bytes) } -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct User { - pub _id: String, pub username: String, pub password: String, - pub role: UserRole, + pub user_role: UserRole, } -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[derive(Debug, Clone, Serialize, Deserialize, sqlx::Type)] +#[sqlx(type_name = "user_role", rename_all = "lowercase")] pub enum UserRole { Regular, Admin, } -impl Validate for User { - async fn validate(&self) -> Result<(), String> { - Ok(()) - } -} - impl User { - pub async fn create(username: &str, password: &str, role: UserRole) -> Option { - if Self::find_one_partial(doc! { "username": username }, json!({})) + pub async fn find(username: &str) -> Option { + sqlx::query_as("SELECT * FROM user WHERE username = $1") + .bind(username) + .fetch_optional(get_pg!()) .await - .is_some() - { + .unwrap() + } + + pub async fn create(username: &str, password: &str, role: UserRole) -> Option { + if Self::find(username).await.is_some() { return None; } let u = Self { - _id: uuid::Uuid::new_v4().to_string(), username: username.to_string(), password: bcrypt::hash(password, bcrypt::DEFAULT_COST).unwrap(), - role, + user_role: role, }; - u.insert().await.ok()?; + sqlx::query("INSERT INTO user (username, password, user_role) VALUES ($1, $2, $3)") + .bind(&u.username) + .bind(&u.password) + .bind(&u.user_role) + .fetch(get_pg!()); Some(u) } pub async fn login(username: &str, password: &str) -> Option<(Session, UserRole)> { - let u = Self::find_one(doc! { "username": username }).await?; + let u = Self::find(username).await?; if !u.verify_pw(password) { return None; } - Some((u.session().await, u.role)) + Some((u.session().await, u.user_role)) } /// Change the password of a `User` pub async fn passwd(self, old: &str, new: &str) -> Result<(), ()> { if self.verify_pw(old) { - self.change() - .password(bcrypt::hash(new, bcrypt::DEFAULT_COST).unwrap()) - .update() - .await - .map_err(|_| ())?; + sqlx::query("UPDATE user SET password = $1 WHERE username = $2;") + .bind(bcrypt::hash(new, bcrypt::DEFAULT_COST).unwrap()) + .bind(&self.username) + .fetch(get_pg!()); return Ok(()); } @@ -86,15 +82,14 @@ impl User { } pub async fn session(&self) -> Session { - let s = Session { - _id: uuid::Uuid::new_v4().to_string(), - token: gen_token(60), - user: self.reference(), - }; - - s.insert().await.unwrap(); - - s + sqlx::query_as( + "INSERT INTO user_session (token, user) VALUES ($1, $2) RETURNING id, token, user", + ) + .bind(gen_token(64)) + .bind(&self.username) + .fetch_one(get_pg!()) + .await + .unwrap() } pub const fn is_admin(&self) -> bool { @@ -106,26 +101,18 @@ impl User { } } -impl ToAPI for User { +impl User { async fn api(&self) -> serde_json::Value { json!({ "username": self.username, - "role": self.role + "role": self.user_role }) } } -#[derive(Debug, Clone, Serialize, Deserialize, Model, Referencable)] +#[derive(Debug, Clone, Serialize, Deserialize, FromRow)] pub struct Session { - pub _id: String, + pub id: uuid::Uuid, pub token: String, - pub user: Reference, -} - -impl Validate for Session { - async fn validate(&self) -> Result<(), String> { - assert_reference_of!(self.user, User); - - Ok(()) - } + pub user: String, } diff --git a/src/main.rs b/src/main.rs index 4fabf1c..6bfe0e8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,11 +5,30 @@ mod library; mod route; use library::user::{User, UserRole}; -use mongod::Model; use mongodb::bson::doc; use rocket::routes; +use rocket::tokio::sync::OnceCell; use rocket::{http::Method, launch}; +pub static PG: OnceCell = OnceCell::const_new(); + +#[macro_export] +macro_rules! get_pg { + () => { + if let Some(client) = $crate::PG.get() { + client + } else { + let client = sqlx::postgres::PgPoolOptions::new() + .max_connections(5) + .connect(&std::env::var("DATABASE_URL").unwrap()) + .await + .unwrap(); + $crate::PG.set(client).unwrap(); + $crate::PG.get().unwrap() + } + }; +} + #[launch] async fn rocket() -> _ { env_logger::init(); @@ -27,16 +46,17 @@ async fn rocket() -> _ { .to_cors() .expect("error creating CORS options"); + let pg = get_pg!(); + + sqlx::migrate!("./migrations").run(pg).await.unwrap(); + let lib = Libary::new("./media".into()); let cache = cache::RouteCache::new(); lib.rescan(&cache).await; // create initial admin user - if User::find(doc! { "username": "admin" }, None, None) - .await - .is_none() - { + if User::find("admin").await.is_none() { User::create("admin", "admin", UserRole::Admin).await; }