diff --git a/.sqlx/query-d08992cf2c132fedbed21b94d545e154fa2a7a2a2bf79fd033341d1bb5a6c0f2.json b/.sqlx/query-d08992cf2c132fedbed21b94d545e154fa2a7a2a2bf79fd033341d1bb5a6c0f2.json deleted file mode 100644 index f47d543..0000000 --- a/.sqlx/query-d08992cf2c132fedbed21b94d545e154fa2a7a2a2bf79fd033341d1bb5a6c0f2.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "SELECT * FROM users WHERE name = $1", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "id", - "type_info": "Int8" - }, - { - "ordinal": 1, - "name": "name", - "type_info": "Text" - }, - { - "ordinal": 2, - "name": "password", - "type_info": "Text" - }, - { - "ordinal": 3, - "name": "is_admin", - "type_info": "Bool" - } - ], - "parameters": { - "Left": [ - "Text" - ] - }, - "nullable": [ - false, - false, - false, - false - ] - }, - "hash": "d08992cf2c132fedbed21b94d545e154fa2a7a2a2bf79fd033341d1bb5a6c0f2" -} diff --git a/Cargo.lock b/Cargo.lock index ca299f1..58ace3c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,9 +40,9 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.10.2" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "209b47e8954a928e1d72e86eca7000ebb6655fe1436d33eefc2201cad027e237" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" dependencies = [ "aead", "aes", @@ -52,6 +52,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.3" @@ -66,13 +77,19 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.1.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -109,6 +126,81 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + +[[package]] +name = "async-attributes" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3203e79f4dd9bdda415ed03cf14dae5a2bf775c683a00f94e9cd1faf0f596e5" +dependencies = [ + "quote", + "syn 1.0.109", +] + +[[package]] +name = "async-channel" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81953c529336010edd6d8e358f886d9581267795c61b19475b71314bffa46d35" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + [[package]] name = "async-compression" version = "0.4.3" @@ -123,6 +215,120 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-executor" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1da3ae8dabd9c00f453a329dfe1fb28da3c0a72e2478cdcd93171740c20499" +dependencies = [ + "async-lock", + "async-task", + "concurrent-queue", + "fastrand 2.0.1", + "futures-lite", + "slab", +] + +[[package]] +name = "async-global-executor" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b6f5d7df27bd294849f8eec66ecfc63d11814df7a4f5d74168a2394467b776" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-lock", + "blocking", + "futures-lite", + "once_cell", + "tokio", +] + +[[package]] +name = "async-io" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +dependencies = [ + "async-lock", + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-lite", + "log", + "parking", + "polling", + "rustix 0.37.24", + "slab", + "socket2 0.4.9", + "waker-fn", +] + +[[package]] +name = "async-lock" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-std" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62565bb4402e926b29953c785397c6dc0391b7b446e45008b0049eb43cec6f5d" +dependencies = [ + "async-attributes", + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "once_cell", + "pin-project-lite", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-stream" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +dependencies = [ + "async-stream-impl", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-stream-impl" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "async-task" +version = "4.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9441c6b2fe128a7c2bf680a44c34d0df31ce09e5b7e401fcca3faa483dbc921" + [[package]] name = "async-trait" version = "0.1.73" @@ -143,6 +349,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.1.0" @@ -176,6 +388,17 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +[[package]] +name = "bigdecimal" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6773ddc0eafc0e509fb60e48dff7f450f8e674a0686ae8605e8d9901bd5eefa" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -191,6 +414,18 @@ dependencies = [ "serde", ] +[[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.10.4" @@ -201,10 +436,71 @@ dependencies = [ ] [[package]] -name = "brotli" -version = "3.3.4" +name = "blocking" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a0b1dbcc8ae29329621f8d4f0d835787c1c38bb1401979b49d13b0b305ff68" +checksum = "8c36a4d0d48574b3dd360b4b7d95cc651d2b6557b6402848a27d4b228a473e2a" +dependencies = [ + "async-channel", + "async-lock", + "async-task", + "fastrand 2.0.1", + "futures-io", + "futures-lite", + "piper", + "tracing", +] + +[[package]] +name = "borsh" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" +dependencies = [ + "borsh-derive", + "hashbrown 0.12.3", +] + +[[package]] +name = "borsh-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0754613691538d51f329cce9af41d7b7ca150bc973056f1156611489475f54f7" +dependencies = [ + "borsh-derive-internal", + "borsh-schema-derive-internal", + "proc-macro-crate 0.1.5", + "proc-macro2", + "syn 1.0.109", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afb438156919598d2c7bad7e1c0adf3d26ed3840dbc010db1a882a65583ca2fb" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "634205cc43f74a1b9046ef87c4540ebda95696ec0f315024860cad7c5b0f5ccd" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "brotli" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516074a47ef4bce09577a3b379392300159ce5b1ba2e501ff1c819950066100f" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -213,9 +509,9 @@ dependencies = [ [[package]] name = "brotli-decompressor" -version = "2.3.4" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +checksum = "da74e2b81409b1b743f8f0c62cc6254afefb8b8e50bbfe3735550f7aeefa3448" dependencies = [ "alloc-no-stdlib", "alloc-stdlib", @@ -227,6 +523,28 @@ version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +[[package]] +name = "bytecheck" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6372023ac861f6e6dc89c8344a8f398fb42aaba2b5dbc649ca0c0e9dbcb627" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7ec4c6f261935ad534c0c22dbef2201b45918860eb1c574b972bd213a76af61" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -263,6 +581,7 @@ dependencies = [ "android-tzdata", "iana-time-zone", "num-traits", + "serde", "windows-targets", ] @@ -276,6 +595,46 @@ dependencies = [ "inout", ] +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + [[package]] name = "color-eyre" version = "0.6.2" @@ -303,12 +662,33 @@ dependencies = [ "tracing-error", ] +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "concurrent-queue" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f057a694a54f12365049b0958a1685bb52d567f5593b355fbf685838e873d400" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "const-oid" version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28c122c3980598d243d63d9a704629a2d748d101f278052ff068be5a4423ab6f" +[[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.17.0" @@ -405,6 +785,41 @@ dependencies = [ "cipher", ] +[[package]] +name = "darling" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.38", +] + +[[package]] +name = "darling_macro" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.38", +] + [[package]] name = "der" version = "0.7.8" @@ -425,6 +840,30 @@ dependencies = [ "serde", ] +[[package]] +name = "derivative" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive_more" +version = "0.99.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "rustc_version", + "syn 1.0.109", +] + [[package]] name = "digest" version = "0.10.7" @@ -452,6 +891,24 @@ dependencies = [ "serde", ] +[[package]] +name = "encoding_rs" +version = "0.8.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "entities" +version = "0.1.0" +dependencies = [ + "sea-orm", + "serde", + "time", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -495,6 +952,15 @@ dependencies = [ "once_cell", ] +[[package]] +name = "fastrand" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" +dependencies = [ + "instant", +] + [[package]] name = "fastrand" version = "2.0.1" @@ -543,6 +1009,26 @@ dependencies = [ "percent-encoding", ] +[[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-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -587,6 +1073,21 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fff74096e71ed47f8e023204cfd0aa1289cd54ae5430a9523be060cdb849964" +[[package]] +name = "futures-lite" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" +dependencies = [ + "fastrand 1.9.0", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.28" @@ -616,6 +1117,7 @@ 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", @@ -664,6 +1166,24 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "glob" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" + +[[package]] +name = "gloo-timers" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b995a66bb87bebce9a0f4a95aed01daca4872c050bfcb21653361c03bc35e5c" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", +] + [[package]] name = "h2" version = "0.3.21" @@ -688,6 +1208,9 @@ 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 = "hashbrown" @@ -695,7 +1218,7 @@ version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" dependencies = [ - "ahash", + "ahash 0.8.3", "allocator-api2", ] @@ -861,6 +1384,12 @@ 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.4.0" @@ -897,6 +1426,17 @@ dependencies = [ "hashbrown 0.14.1", ] +[[package]] +name = "inherent" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce243b1bfa62ffc028f1cc3b6034ec63d649f3031bc8a4fbbb004e1ac17d1f68" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "inout" version = "0.1.3" @@ -906,6 +1446,26 @@ 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", +] + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi", + "libc", + "windows-sys", +] + [[package]] name = "itertools" version = "0.11.0" @@ -930,6 +1490,15 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -964,9 +1533,15 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.4.9" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45786cec4d5e54a224b15cb9f06751883103a27c19c93eda09b0b4f5f08fefac" +checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" + +[[package]] +name = "linux-raw-sys" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da2479e8c062e40bf0066ffa0bc823de0a9368974af99c9f6df941d2c231e03f" [[package]] name = "lock_api" @@ -983,6 +1558,9 @@ name = "log" version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +dependencies = [ + "value-bag", +] [[package]] name = "matchers" @@ -1015,6 +1593,14 @@ version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +[[package]] +name = "migration" +version = "0.1.0" +dependencies = [ + "async-std", + "sea-orm-migration", +] + [[package]] name = "mime" version = "0.3.17" @@ -1057,6 +1643,25 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "multer" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01acbdc23469fd8fe07ab135923371d5f5a422fbf9c522158677c8eb15bc51c2" +dependencies = [ + "bytes", + "encoding_rs", + "futures-util", + "http", + "httparse", + "log", + "memchr", + "mime", + "spin 0.9.8", + "tokio", + "version_check", +] + [[package]] name = "nom" version = "7.1.3" @@ -1077,6 +1682,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-bigint-dig" version = "0.8.4" @@ -1156,6 +1772,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "ordered-float" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a54938017eacd63036332b4ae5c8a49fc8c0c1d6d629893057e4f13609edd06" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ouroboros" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2ba07320d39dfea882faa70554b4bd342a5f273ed59ba7c1c6b4c840492c954" +dependencies = [ + "aliasable", + "ouroboros_macro", + "static_assertions", +] + +[[package]] +name = "ouroboros_macro" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec4c6225c69b4ca778c0aea097321a64c421cf4577b331c61b229267edabb6f8" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "overload" version = "0.1.1" @@ -1168,6 +1817,12 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "parking" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52c774a4c39359c1d1c52e43f73dd91a75a614652c825408eec30c95a9b2067" + [[package]] name = "parking_lot" version = "0.12.1" @@ -1224,6 +1879,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +dependencies = [ + "atomic-waker", + "fastrand 2.0.1", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -1270,6 +1936,7 @@ dependencies = [ "hyper", "mime", "mime_guess", + "multer", "parking_lot", "percent-encoding", "pin-project-lite", @@ -1282,10 +1949,13 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "serde_yaml", "smallvec", + "tempfile", "thiserror", "time", "tokio", + "tokio-stream", "tokio-util", "tracing", ] @@ -1296,12 +1966,87 @@ version = "1.3.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2550a0bce7273b278894ef3ccc5a6869e7031b6870042f3cc6826ed9faa980a6" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.38", ] +[[package]] +name = "poem-ext" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdbd601810df4590c4f9bdf8f4c567ad9979d296bb3ebd75190a45fb8f22a21" +dependencies = [ + "itertools", + "paste", + "poem", + "poem-openapi", + "sea-orm", + "serde", + "tokio-shield", + "tracing", +] + +[[package]] +name = "poem-openapi" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62659dcc7ca09a525881300646f3b28e319889072e83cd16a2865ba024a185e" +dependencies = [ + "base64", + "bytes", + "derive_more", + "futures-util", + "indexmap 2.0.2", + "mime", + "num-traits", + "poem", + "poem-openapi-derive", + "quick-xml", + "regex", + "serde", + "serde_json", + "serde_urlencoded", + "serde_yaml", + "thiserror", + "tokio", +] + +[[package]] +name = "poem-openapi-derive" +version = "3.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e90bf699e87e95b8303f9b59684cf3b9a8fff840872b13cbe0680aa4330d5226" +dependencies = [ + "darling", + "http", + "indexmap 2.0.2", + "mime", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "regex", + "syn 2.0.38", + "thiserror", +] + +[[package]] +name = "polling" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" +dependencies = [ + "autocfg", + "bitflags 1.3.2", + "cfg-if", + "concurrent-queue", + "libc", + "log", + "pin-project-lite", + "windows-sys", +] + [[package]] name = "polyval" version = "0.6.1" @@ -1330,6 +2075,15 @@ dependencies = [ "indexmap 1.9.3", ] +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + [[package]] name = "proc-macro-crate" version = "1.3.1" @@ -1341,14 +2095,58 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.68" +name = "proc-macro-error" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "quick-xml" version = "0.30.0" @@ -1368,6 +2166,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1404,12 +2208,15 @@ version = "0.1.0" dependencies = [ "cfg-if", "color-eyre", + "entities", "md5", + "migration", "poem", + "poem-ext", "quick-xml", + "sea-orm", "serde", "serde_json", - "sqlx", "time", "tokio", "tracing", @@ -1471,6 +2278,15 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "rend" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2571463863a6bd50c32f94402933f03457a3fbaf697a707c5be741e459f08fd" +dependencies = [ + "bytecheck", +] + [[package]] name = "rfc7239" version = "0.1.0" @@ -1480,6 +2296,49 @@ dependencies = [ "uncased", ] +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin 0.5.2", + "untrusted", + "web-sys", + "winapi", +] + +[[package]] +name = "rkyv" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0200c8230b013893c0b2d6213d6ec64ed2b9be2e0e016682b7224ff82cff5c58" +dependencies = [ + "bitvec", + "bytecheck", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e06b915b5c230a17d7a736d1e2e63ee753c256a8614ef3f5147b13a4f5541d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "rsa" version = "0.9.2" @@ -1502,6 +2361,22 @@ dependencies = [ "zeroize", ] +[[package]] +name = "rust_decimal" +version = "1.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c4216490d5a413bc6d10fa4742bd7d4955941d062c0ef873141d6b0e7b30fd" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand", + "rkyv", + "serde", + "serde_json", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1509,18 +2384,71 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] -name = "rustix" -version = "0.38.17" +name = "rustc_version" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "0.37.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4279d76516df406a8bd37e7dff53fd37d1a093f997a3c34a5c21658c126db06d" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.3.8", + "windows-sys", +] + +[[package]] +name = "rustix" +version = "0.38.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a74ee2d7c2581cd139b42447d7d9389b889bdaad3a73f1ebb16f2a3237bb19c" dependencies = [ "bitflags 2.4.0", "errno", "libc", - "linux-raw-sys", + "linux-raw-sys 0.4.10", "windows-sys", ] +[[package]] +name = "rustls" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" +dependencies = [ + "ring", + "rustls-webpki", + "sct", +] + +[[package]] +name = "rustls-pemfile" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d3987094b1d07b653b7dfdc3f70ce9a1da9c51ac18c1b06b662e4f9a0e9f4b2" +dependencies = [ + "base64", +] + +[[package]] +name = "rustls-webpki" +version = "0.101.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c7d5dece342910d9ba34d259310cae3e0154b873b35408b787b59bce53d34fe" +dependencies = [ + "ring", + "untrusted", +] + [[package]] name = "ryu" version = "1.0.15" @@ -1533,6 +2461,187 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sct" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d53dcdb7c9f8158937a7981b48accfd39a43af418591a5d008c7b22b5e1b7ca4" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sea-bae" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd3534a9978d0aa7edd2808dc1f8f31c4d0ecd31ddf71d997b3c98e9f3c9114" +dependencies = [ + "heck", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "sea-orm" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da5b2d70c255bc5cbe1d49f69c3c8eadae0fbbaeb18ee978edbf2f75775cb94d" +dependencies = [ + "async-stream", + "async-trait", + "bigdecimal", + "chrono", + "futures", + "log", + "ouroboros", + "rust_decimal", + "sea-orm-macros", + "sea-query", + "sea-query-binder", + "serde", + "serde_json", + "sqlx", + "strum", + "thiserror", + "time", + "tracing", + "url", + "uuid", +] + +[[package]] +name = "sea-orm-cli" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bef60732e6016c5643350c87f43a697e8c074e41e4e2a9d961c056cb1310915" +dependencies = [ + "chrono", + "clap", + "dotenvy", + "glob", + "regex", + "sea-schema", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "sea-orm-macros" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c8d455fad40194fb9774fdc4810c0f2700ff0dc0e93bd5ce9d641cc3f5dd75" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "sea-bae", + "syn 2.0.38", + "unicode-ident", +] + +[[package]] +name = "sea-orm-migration" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e53b6ddaf6dbb84e5dfc3fb78634ed0a4d6d64e7479500ab2585db239747031" +dependencies = [ + "async-trait", + "clap", + "dotenvy", + "futures", + "sea-orm", + "sea-orm-cli", + "sea-schema", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "sea-query" +version = "0.30.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb3e6bba153bb198646c8762c48414942a38db27d142e44735a133cabddcc820" +dependencies = [ + "bigdecimal", + "chrono", + "derivative", + "inherent", + "ordered-float", + "rust_decimal", + "sea-query-derive", + "serde_json", + "time", + "uuid", +] + +[[package]] +name = "sea-query-binder" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36bbb68df92e820e4d5aeb17b4acd5cc8b5d18b2c36a4dd6f4626aabfa7ab1b9" +dependencies = [ + "bigdecimal", + "chrono", + "rust_decimal", + "sea-query", + "serde_json", + "sqlx", + "time", + "uuid", +] + +[[package]] +name = "sea-query-derive" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd78f2e0ee8e537e9195d1049b752e0433e2cac125426bccb7b5c3e508096117" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", + "thiserror", +] + +[[package]] +name = "sea-schema" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cd9561232bd1b82ea748b581f15909d11de0db6563ddcf28c5d908aee8282f1" +dependencies = [ + "futures", + "sea-query", + "sea-schema-derive", +] + +[[package]] +name = "sea-schema-derive" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f686050f76bffc4f635cda8aea6df5548666b830b52387e8bc7de11056d11e" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "semver" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" + [[package]] name = "serde" version = "1.0.188" @@ -1576,6 +2685,19 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_yaml" +version = "0.9.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" +dependencies = [ + "indexmap 2.0.2", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "sha1" version = "0.10.6" @@ -1626,6 +2748,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "simdutf8" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f27f6278552951f1f2b8cf9da965d10969b2efdea95a6ec47987ab46edfe263a" + [[package]] name = "slab" version = "0.4.9" @@ -1716,10 +2844,12 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8d6753e460c998bbd4cd8c6f0ed9a64346fcca0723d6e75e52fdc351c5d2169d" dependencies = [ - "ahash", + "ahash 0.8.3", "atoi", + "bigdecimal", "byteorder", "bytes", + "chrono", "crc", "crossbeam-queue", "dotenvy", @@ -1738,6 +2868,9 @@ dependencies = [ "once_cell", "paste", "percent-encoding", + "rust_decimal", + "rustls", + "rustls-pemfile", "serde", "serde_json", "sha2", @@ -1749,6 +2882,8 @@ dependencies = [ "tokio-stream", "tracing", "url", + "uuid", + "webpki-roots", ] [[package]] @@ -1798,9 +2933,11 @@ checksum = "864b869fdf56263f4c95c45483191ea0af340f9f3e3e7b4d57a61c7c87a970db" dependencies = [ "atoi", "base64", + "bigdecimal", "bitflags 2.4.0", "byteorder", "bytes", + "chrono", "crc", "digest", "dotenvy", @@ -1821,6 +2958,7 @@ dependencies = [ "percent-encoding", "rand", "rsa", + "rust_decimal", "serde", "sha1", "sha2", @@ -1830,6 +2968,7 @@ dependencies = [ "thiserror", "time", "tracing", + "uuid", "whoami", ] @@ -1841,8 +2980,10 @@ checksum = "eb7ae0e6a97fb3ba33b23ac2671a5ce6e3cabe003f451abd5a56e7951d975624" dependencies = [ "atoi", "base64", + "bigdecimal", "bitflags 2.4.0", "byteorder", + "chrono", "crc", "dotenvy", "etcetera", @@ -1858,8 +2999,10 @@ dependencies = [ "log", "md-5", "memchr", + "num-bigint", "once_cell", "rand", + "rust_decimal", "serde", "serde_json", "sha1", @@ -1870,6 +3013,7 @@ dependencies = [ "thiserror", "time", "tracing", + "uuid", "whoami", ] @@ -1880,6 +3024,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d59dc83cf45d89c555a577694534fcd1b55c545a816c816ce51f20bbe56a4f3f" dependencies = [ "atoi", + "chrono", "flume", "futures-channel", "futures-core", @@ -1894,8 +3039,15 @@ dependencies = [ "time", "tracing", "url", + "uuid", ] +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + [[package]] name = "stringprep" version = "0.1.4" @@ -1907,6 +3059,18 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + [[package]] name = "subtle" version = "2.5.0" @@ -1935,6 +3099,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.8.0" @@ -1942,9 +3112,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", - "fastrand", + "fastrand 2.0.1", "redox_syscall", - "rustix", + "rustix 0.38.18", "windows-sys", ] @@ -2023,9 +3193,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.32.0" +version = "1.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ed6077ed6cd6c74735e21f37eb16dc3935f96878b1fe961074089cc80893f9" +checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" dependencies = [ "backtrace", "bytes", @@ -2051,6 +3221,16 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "tokio-shield" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3777cb82f12ea1f163052c757c39899d2cf11bc73b045ff513de6699db4b7751" +dependencies = [ + "futures", + "tokio", +] + [[package]] name = "tokio-stream" version = "0.1.14" @@ -2076,6 +3256,15 @@ dependencies = [ "tracing", ] +[[package]] +name = "toml" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +dependencies = [ + "serde", +] + [[package]] name = "toml_datetime" version = "0.6.3" @@ -2247,6 +3436,18 @@ dependencies = [ "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" + +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + [[package]] name = "url" version = "2.4.1" @@ -2268,12 +3469,33 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "uuid" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d" +dependencies = [ + "serde", +] + [[package]] name = "valuable" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +[[package]] +name = "value-bag" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d92ccd67fb88503048c01b59152a04effd0782d035a83a6d256ce6085f08f4a3" + [[package]] name = "vcpkg" version = "0.2.15" @@ -2286,6 +3508,12 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +[[package]] +name = "waker-fn" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" + [[package]] name = "want" version = "0.3.1" @@ -2326,6 +3554,18 @@ dependencies = [ "wasm-bindgen-shared", ] +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02dbc21516f9f1f04f187958890d7e6026df8d16540b7ad9492bc34a67cea03" +dependencies = [ + "cfg-if", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-bindgen-macro" version = "0.2.87" @@ -2355,6 +3595,25 @@ version = "0.2.87" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" +[[package]] +name = "web-sys" +version = "0.3.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b85cbef8c220a6abc02aefd892dfc0fc23afb1c6a426316ec33253a3877249b" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b291546d5d9d1eab74f069c77749f2cb8504a12caa20f0f2de93ddbf6f411888" +dependencies = [ + "rustls-webpki", +] + [[package]] name = "whoami" version = "1.4.1" @@ -2467,6 +3726,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Cargo.toml b/Cargo.toml index 100b6ec..f5297dc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,38 +1,20 @@ -[package] -name = "rave" -version = "0.1.0" -edition = "2021" -publish = ["crates-io"] +[workspace] +members = ["app", "entities", "migration"] +resolver = "2" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -cfg-if = "1.0.0" -color-eyre = "0.6.2" -md5 = "0.7.0" -poem = { version = "1.3.58", features = [ - "compression", - "cookie", - "session", - "static-files", - "xml", +[workspace.dependencies] +entities = { path = "entities" } +migration = { path = "migration" } +sea-orm = { version = "0.12", features = [ + "sqlx-postgres", + "runtime-tokio-rustls", + "with-time", + "postgres-array", ] } -quick-xml = { version = "0.30.0", features = ["serialize"] } -serde = { version = "1.0.188", features = ["derive"] } -serde_json = "1.0.107" -sqlx = { version = "0.7.2", features = ["time", "postgres", "runtime-tokio"] } time = { version = "0.3.29", features = [ "serde-human-readable", "macros", "parsing", ] } -tokio = { version = "1.32.0", features = ["full"] } tracing = { version = "0.1.37", features = ["async-await"] } -tracing-subscriber = { version = "0.3.17", features = [ - "env-filter", - "tracing", - "parking_lot", - "time", -] } -url = { version = "2.4.1", features = ["serde"] } -url-escape = "0.1.1" +serde = { version = "1.0.188", features = ["derive"] } diff --git a/app/Cargo.toml b/app/Cargo.toml new file mode 100644 index 0000000..0b4a9ae --- /dev/null +++ b/app/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "rave" +version = "0.1.0" +edition = "2021" +publish = ["forge-lys-ee"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +cfg-if = "1.0.0" +color-eyre = "0.6.2" +md5 = "0.7.0" +poem = { version = "1.3.58", features = [ + "compression", + "cookie", + "session", + "static-files", + "xml", +] } +poem-ext = "0.9.4" +quick-xml = { version = "0.30.0", features = ["serialize"] } +serde = { workspace = true } +serde_json = "1.0.107" +time = { workspace = true } +tokio = { version = "1.32.0", features = ["full"] } +tracing = { workspace = true } +tracing-subscriber = { version = "0.3.17", features = [ + "env-filter", + "tracing", + "parking_lot", + "time", +] } +url = { version = "2.4.1", features = ["serde"] } +url-escape = "0.1.1" +sea-orm = { workspace = true } +entities = { workspace = true } +migration = { workspace = true } diff --git a/src/authentication.rs b/app/src/authentication.rs similarity index 100% rename from src/authentication.rs rename to app/src/authentication.rs diff --git a/src/authentication/de.rs b/app/src/authentication/de.rs similarity index 100% rename from src/authentication/de.rs rename to app/src/authentication/de.rs diff --git a/src/main.rs b/app/src/main.rs similarity index 86% rename from src/main.rs rename to app/src/main.rs index 0ca9a96..da47fd0 100644 --- a/src/main.rs +++ b/app/src/main.rs @@ -5,13 +5,15 @@ use std::time::Duration; use color_eyre::Result; +use migration::{Migrator, MigratorTrait}; use poem::{ listener::TcpListener, middleware, web::{CompressionAlgo, CompressionLevel}, Endpoint, EndpointExt, Route, }; -use sqlx::PgPool; +use poem_ext::db::DbTransactionMiddleware; +use sea_orm::{ConnectOptions, Database, DatabaseConnection}; use tracing::info; use tracing_subscriber::{fmt, EnvFilter}; @@ -20,7 +22,6 @@ mod random_types; mod rest; mod subsonic; mod ui; -mod user; mod utils; const LISTEN: &str = "0.0.0.0:1234"; @@ -32,11 +33,10 @@ async fn main() -> Result<()> { let route = create_route(); - let pool = create_pool().await; + let dbc = create_pool().await?; + Migrator::up(&dbc, None).await?; - sqlx::migrate!().run(&pool).await?; - - let route = route.with(utils::middleware::DbConnectionMiddleware::new(pool)); + let route = route.with(DbTransactionMiddleware::new(dbc)); let server = create_server(); @@ -51,12 +51,16 @@ async fn main() -> Result<()> { Ok(()) } -async fn create_pool() -> PgPool { + +async fn create_pool() -> Result { let url = std::env::var("DATABASE_URL").expect("DATABASE_URL not set"); - PgPool::connect(&url) - .await - .expect("Failed to connect to database") + let mut opt = ConnectOptions::new(url); + opt.max_connections(100) + .min_connections(5) + .sqlx_logging(true); + + Ok(Database::connect(opt).await?) } fn create_route() -> Box> { diff --git a/src/random_types.rs b/app/src/random_types.rs similarity index 100% rename from src/random_types.rs rename to app/src/random_types.rs diff --git a/src/random_types/sort_type.rs b/app/src/random_types/sort_type.rs similarity index 100% rename from src/random_types/sort_type.rs rename to app/src/random_types/sort_type.rs diff --git a/app/src/rest/get_album.rs b/app/src/rest/get_album.rs new file mode 100644 index 0000000..986e746 --- /dev/null +++ b/app/src/rest/get_album.rs @@ -0,0 +1,30 @@ +use crate::{ + authentication::Authentication, + subsonic::SubsonicResponse, + utils::{self}, +}; + +use poem::web::{Data, Query}; +use poem_ext::db::DbTxn; +use serde::Deserialize; + +#[poem::handler] +pub async fn get_album( + Data(txn): Data<&DbTxn>, + auth: Authentication, + Query(params): Query, +) -> SubsonicResponse { + let u = utils::verify_user(txn.clone(), auth).await; + + match u { + Ok(_) => {} + Err(e) => return e, + } + + todo!("get_album not implemented"); +} + +#[derive(Debug, Clone, Deserialize)] +pub struct GetAlbumParams { + pub id: i32, +} diff --git a/app/src/rest/get_album_list.rs b/app/src/rest/get_album_list.rs new file mode 100644 index 0000000..b53cdda --- /dev/null +++ b/app/src/rest/get_album_list.rs @@ -0,0 +1,263 @@ +#![allow(clippy::unused_async)] // todo: remove + +use entities::{album, prelude::Album}; +use poem::web::{Data, Query}; +use poem_ext::db::DbTxn; +use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect}; +use serde::Deserialize; + +use crate::{ + authentication::Authentication, + random_types::SortType, + subsonic::{Error, SubsonicResponse}, + utils::{self}, +}; + +macro_rules! error_or { + ($thing:ident) => { + match $thing { + Ok(a) => Ok(a), + Err(e) => Err(Error::Generic(Some(e.to_string()))), + } + }; +} + +#[poem::handler] +pub async fn get_album_list( + Data(txn): Data<&DbTxn>, + auth: Authentication, + Query(params): Query, +) -> SubsonicResponse { + let txn = txn.clone(); + let u = utils::verify_user(txn.clone(), auth).await; + + match u { + Ok(_) => {} + Err(e) => return e, + } + + let params = match params.verify() { + Ok(p) => p, + Err(e) => return e, + }; + + let album_list = match params.r#type { + SortType::Random => get_album_list_random(txn, params).await, + SortType::Newest => get_album_list_newest(txn, params).await, + SortType::Highest => get_album_list_highest(txn, params).await, + SortType::Frequent => get_album_list_frequent(txn, params).await, + SortType::Recent => get_album_list_recent(txn, params).await, + SortType::AlphabeticalByName => get_album_list_alphabetical_by_name(txn, params).await, + SortType::AlphabeticalByArtist => get_album_list_alphabetical_by_artist(txn, params).await, + SortType::Starred => get_album_list_starred(txn, params).await, + SortType::ByYear => get_album_list_by_year(txn, params).await, + SortType::ByGenre => get_album_list_by_genre(txn, params).await, + }; + + match album_list { + Ok(a) => SubsonicResponse::new_album_list(a), + Err(e) => SubsonicResponse::new_error(e), + } +} + +#[allow(unused_variables)] +async fn get_album_list_random( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + Err(Error::Generic(Some( + "Sorting by random not implemented".to_string(), + ))) +} + +#[allow(unused_variables)] +async fn get_album_list_newest( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + let albums = Album::find() + .order_by_desc(album::Column::Created) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) +} + +#[allow(unused_variables)] +async fn get_album_list_highest( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + Err(Error::Generic(Some( + "Sorting by highest rating not implemented".to_string(), + ))) +} + +#[allow(unused_variables)] +async fn get_album_list_frequent( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + let albums = Album::find() + .order_by_desc(album::Column::PlayCount) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) +} + +#[allow(unused_variables)] +async fn get_album_list_recent( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + Err(Error::Generic(Some( + "Sorting by recently played not implemented".to_string(), + ))) +} + +#[allow(unused_variables)] +async fn get_album_list_alphabetical_by_name( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + let albums = Album::find() + .order_by_desc(album::Column::Name) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) +} + +#[allow(unused_variables)] +async fn get_album_list_alphabetical_by_artist( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + let albums = Album::find() + .order_by_desc(album::Column::Artist) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) +} + +#[allow(unused_variables)] +async fn get_album_list_starred( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + let albums = Album::find() + .filter(album::Column::Starred.is_not_null()) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) +} + +async fn get_album_list_by_year( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + let from_year = params.from_year; + let to_year = params.to_year; + + let (Some(from_year), Some(to_year)) = (from_year, to_year) else { + return Err(Error::RequiredParameterMissing(Some( + "Missing required parameter: fromYear or toYear".to_string(), + ))); + }; + + let albums = Album::find() + .filter(album::Column::Year.is_not_null()) + .filter(album::Column::Year.between(from_year, to_year)) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) +} + +#[allow(unused_variables)] +async fn get_album_list_by_genre( + conn: DbTxn, + params: GetAlbumListParams, +) -> Result, Error> { + let genre = params.genre; + + let Some(genre) = genre else { + return Err(Error::RequiredParameterMissing(Some( + "Missing required parameter: genre".to_string(), + ))); + }; + + let albums = Album::find() + .filter(album::Column::Genre.is_not_null()) + .filter(album::Column::Genre.eq(genre)) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) +} + +#[derive(Debug, Clone, Deserialize)] +pub struct GetAlbumListParams { + #[serde(rename = "type")] + pub r#type: SortType, + #[serde(default = "default_size")] + pub size: u64, + #[serde(default)] + pub offset: u64, + #[serde(default)] + pub from_year: Option, + #[serde(default)] + pub to_year: Option, + #[serde(default)] + pub genre: Option, + #[serde(default)] + pub music_folder_id: Option, +} + +impl GetAlbumListParams { + #[allow(clippy::result_large_err)] + pub fn verify(self) -> Result { + if self.r#type == SortType::ByYear { + if self.from_year.is_none() || self.to_year.is_none() { + return Err(SubsonicResponse::new_error( + Error::RequiredParameterMissing(Some( + "Missing required parameter: fromYear or toYear".to_string(), + )), + )); + } + } else if self.r#type == SortType::ByGenre && self.genre.is_none() { + return Err(SubsonicResponse::new_error( + Error::RequiredParameterMissing(Some( + "Missing required parameter: genre".to_string(), + )), + )); + } else if self.size > 500 || self.size < 1 { + return Err(SubsonicResponse::new_error(Error::Generic(Some( + "size must be between 1 and 500".to_string(), + )))); + } + + Ok(self) + } +} + +const fn default_size() -> u64 { + 10 +} diff --git a/src/rest/get_license.rs b/app/src/rest/get_license.rs similarity index 59% rename from src/rest/get_license.rs rename to app/src/rest/get_license.rs index 4f9cfa9..86ae684 100644 --- a/src/rest/get_license.rs +++ b/app/src/rest/get_license.rs @@ -1,14 +1,15 @@ use poem::web::Data; +use poem_ext::db::DbTxn; use crate::{ authentication::Authentication, subsonic::SubsonicResponse, - utils::{self, middleware::DbConn}, + utils::{self}, }; #[poem::handler] -pub async fn get_license(Data(conn): Data<&DbConn>, auth: Authentication) -> SubsonicResponse { - let u = utils::verify_user(conn.clone(), auth).await; +pub async fn get_license(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { + let u = utils::verify_user(txn.clone(), auth).await; match u { Ok(_) => {} diff --git a/src/rest/get_music_folders.rs b/app/src/rest/get_music_folders.rs similarity index 69% rename from src/rest/get_music_folders.rs rename to app/src/rest/get_music_folders.rs index dc8352c..eb9febf 100644 --- a/src/rest/get_music_folders.rs +++ b/app/src/rest/get_music_folders.rs @@ -1,17 +1,15 @@ use poem::web::Data; +use poem_ext::db::DbTxn; use crate::{ authentication::Authentication, subsonic::{MusicFolder, SubsonicResponse}, - utils::{self, middleware::DbConn}, + utils::{self}, }; #[poem::handler] -pub async fn get_music_folders( - Data(conn): Data<&DbConn>, - auth: Authentication, -) -> SubsonicResponse { - let u = utils::verify_user(conn.clone(), auth).await; +pub async fn get_music_folders(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { + let u = utils::verify_user(txn.clone(), auth).await; match u { Ok(_) => {} diff --git a/src/rest/mod.rs b/app/src/rest/mod.rs similarity index 85% rename from src/rest/mod.rs rename to app/src/rest/mod.rs index 88b933c..f004787 100644 --- a/src/rest/mod.rs +++ b/app/src/rest/mod.rs @@ -8,8 +8,6 @@ mod get_music_folders; mod ping; // rest/getAlbumList mod get_album_list; -// rest/getAlbumList2 -mod get_album_list2; // rest/getAlbum mod get_album; // rest/stream @@ -21,7 +19,7 @@ pub fn build() -> Box> { .at("/getLicense", get_license::get_license) .at("/getMusicFolders", get_music_folders::get_music_folders) .at("/getAlbumList", get_album_list::get_album_list) - .at("/getAlbumList2", get_album_list2::get_album_list2) + .at("/getAlbumList2", get_album_list::get_album_list) .at("/getAlbum", get_album::get_album) .at("/stream", stream::stream) .boxed() diff --git a/src/rest/ping.rs b/app/src/rest/ping.rs similarity index 53% rename from src/rest/ping.rs rename to app/src/rest/ping.rs index c0031b4..820a73e 100644 --- a/src/rest/ping.rs +++ b/app/src/rest/ping.rs @@ -1,14 +1,15 @@ use poem::web::Data; +use poem_ext::db::DbTxn; use crate::{ authentication::Authentication, subsonic::SubsonicResponse, - utils::{self, middleware::DbConn}, + utils::{self}, }; #[poem::handler] -pub async fn ping(Data(conn): Data<&DbConn>, auth: Authentication) -> SubsonicResponse { - let u = utils::verify_user(conn.clone(), auth).await; +pub async fn ping(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { + let u = utils::verify_user(txn.clone(), auth).await; match u { Ok(_) => SubsonicResponse::new_empty(), diff --git a/src/rest/stream.rs b/app/src/rest/stream.rs similarity index 84% rename from src/rest/stream.rs rename to app/src/rest/stream.rs index 800182e..7bdd74e 100644 --- a/src/rest/stream.rs +++ b/app/src/rest/stream.rs @@ -3,22 +3,23 @@ use poem::{ web::{Data, Query}, IntoResponse, Response, }; +use poem_ext::db::DbTxn; use serde::Deserialize; use crate::{ authentication::Authentication, - utils::{self, middleware::DbConn}, + utils::{self}, }; -const SONG: &[u8] = include_bytes!("../../../data.mp3"); +const SONG: &[u8] = include_bytes!("../../../../data.mp3"); #[poem::handler] pub async fn stream( - Data(conn): Data<&DbConn>, + Data(txn): Data<&DbTxn>, auth: Authentication, Query(_params): Query, ) -> Response { - let u = utils::verify_user(conn.clone(), auth).await; + let u = utils::verify_user(txn.clone(), auth).await; match u { Ok(_) => {} diff --git a/src/subsonic.rs b/app/src/subsonic.rs similarity index 93% rename from src/subsonic.rs rename to app/src/subsonic.rs index 2458832..6d9fc4f 100644 --- a/src/subsonic.rs +++ b/app/src/subsonic.rs @@ -2,8 +2,9 @@ use std::fmt::Display; +use entities::album; use poem::{http::StatusCode, IntoResponse, Response}; -use serde::{ser::SerializeStruct, Serialize, Serializer}; +use serde::{ser::SerializeStruct, Serialize}; use time::OffsetDateTime; use crate::authentication::VersionTriple; @@ -42,16 +43,16 @@ impl SubsonicResponse { Self::new(SubResponseType::MusicFolders { music_folders }) } - pub fn new_album_list(albums: Vec) -> Self { + pub fn new_album_list(albums: Vec) -> Self { Self::new(SubResponseType::AlbumList { albums }) } - pub fn new_album_list2(albums: Vec) -> Self { + pub fn new_album_list2(albums: Vec) -> Self { Self::new(SubResponseType::AlbumList2 { albums }) } - pub fn new_album(album: AlbumId3) -> Self { - Self::new(SubResponseType::Album(album)) + pub fn new_album(album: album::Model, songs: Vec) -> Self { + Self::new(SubResponseType::Album { album, songs }) } pub fn new_empty() -> Self { @@ -85,23 +86,29 @@ pub enum SubResponseType { #[serde(rename = "albumList")] AlbumList { #[serde(rename = "album")] - albums: Vec, + albums: Vec, }, #[serde(rename = "albumList2")] AlbumList2 { #[serde(rename = "album")] - albums: Vec, + albums: Vec, }, #[serde(rename = "album")] - Album(AlbumId3), + Album { + #[serde(flatten)] + album: album::Model, + #[serde(flatten)] + songs: Vec, + }, Empty, } #[derive(Debug, Clone, Serialize, Default)] +#[serde(default)] pub struct AlbumId3 { - #[serde(rename = "@id", serialize_with = "album_id")] - pub id: i32, - #[serde(rename = "@parent")] + #[serde(rename = "@id", serialize_with = "crate::utils::album_id")] + pub id: i64, + #[serde(rename = "@name")] pub name: String, #[serde(rename = "@artist", skip_serializing_if = "Option::is_none")] pub artist: Option, @@ -123,15 +130,8 @@ pub struct AlbumId3 { pub year: Option, #[serde(rename = "@genre", skip_serializing_if = "Option::is_none")] pub genre: Option, - #[serde(rename = "song", skip_serializing_if = "Vec::is_empty")] - pub songs: Vec, -} - -#[allow(clippy::trivially_copy_pass_by_ref)] -fn album_id(id: &i32, s: S) -> Result { - let str = format!("al-{id}"); - - s.serialize_str(&str) + #[serde(rename = "@musicFolder", skip_serializing_if = "Option::is_none")] + pub folder_id: Option, } #[derive(Debug, Clone, Serialize, Default)] @@ -219,7 +219,7 @@ pub enum MediaType { #[derive(Debug, Clone, Serialize)] pub struct MusicFolder { #[serde(rename = "@id")] - pub id: i32, + pub id: i64, #[serde(rename = "@name")] pub name: String, } diff --git a/src/ui/mod.rs b/app/src/ui/mod.rs similarity index 100% rename from src/ui/mod.rs rename to app/src/ui/mod.rs diff --git a/app/src/utils.rs b/app/src/utils.rs new file mode 100644 index 0000000..fe8489c --- /dev/null +++ b/app/src/utils.rs @@ -0,0 +1,51 @@ +use entities::{prelude::User, user}; +use poem_ext::db::DbTxn; +use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +use serde::Serializer; +use tracing::error; + +use crate::{ + authentication::Authentication, + subsonic::{Error, SubsonicResponse}, +}; + +pub async fn verify_user( + conn: DbTxn, + auth: Authentication, +) -> Result { + let user = User::find() + .filter(user::Column::Name.eq(&auth.username)) + .one(&*conn) + .await; + + match user { + Ok(Some(u)) => { + let ours = md5::compute(format!("{}{}", u.password, auth.salt)); + let ours = format!("{ours:x}"); + + if ours == auth.token { + Ok(u) + } else { + Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword( + None, + ))) + } + } + Ok(None) => Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword( + None, + ))), + Err(e) => { + error!("Error getting user: {e}"); + Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword( + None, + ))) + } + } +} + +#[allow(clippy::trivially_copy_pass_by_ref)] +pub fn album_id(id: &i64, s: S) -> Result { + let str = format!("al-{id}"); + + s.serialize_str(&str) +} diff --git a/build.rs b/build.rs deleted file mode 100644 index 7609593..0000000 --- a/build.rs +++ /dev/null @@ -1,5 +0,0 @@ -// generated by `sqlx migrate build-script` -fn main() { - // trigger recompilation when a new migration is added - println!("cargo:rerun-if-changed=migrations"); -} \ No newline at end of file diff --git a/entities/Cargo.toml b/entities/Cargo.toml new file mode 100644 index 0000000..140c02a --- /dev/null +++ b/entities/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "entities" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +sea-orm = { workspace = true } +time = { workspace = true } +serde = { workspace = true } diff --git a/entities/src/album.rs b/entities/src/album.rs new file mode 100644 index 0000000..a555eb5 --- /dev/null +++ b/entities/src/album.rs @@ -0,0 +1,57 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "album")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + pub name: String, + pub artist: Option, + pub artist_id: Option, + pub cover_art: Option, + pub song_count: i32, + pub duration: i64, + pub play_count: i64, + pub created: DateTimeWithTimeZone, + pub starred: Option, + pub year: Option, + pub genre: Option, + pub music_folder_id: Option, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm( + belongs_to = "super::artist::Entity", + from = "Column::ArtistId", + to = "super::artist::Column::Id", + on_update = "NoAction", + on_delete = "SetNull" + )] + Artist, + #[sea_orm( + belongs_to = "super::music_folder::Entity", + from = "Column::MusicFolderId", + to = "super::music_folder::Column::Id", + on_update = "NoAction", + on_delete = "Cascade" + )] + MusicFolder, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Artist.def() + } +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::MusicFolder.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/entities/src/artist.rs b/entities/src/artist.rs new file mode 100644 index 0000000..8a1a689 --- /dev/null +++ b/entities/src/artist.rs @@ -0,0 +1,31 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "artist")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + #[sea_orm(unique)] + pub name: String, + pub cover_art: Option, + pub artist_image_url: Option, + pub album_count: i32, + pub starred: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::album::Entity")] + Album, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Album.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/entities/src/lib.rs b/entities/src/lib.rs new file mode 100644 index 0000000..986f8c1 --- /dev/null +++ b/entities/src/lib.rs @@ -0,0 +1,8 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 + +pub mod prelude; + +pub mod album; +pub mod artist; +pub mod music_folder; +pub mod user; diff --git a/entities/src/music_folder.rs b/entities/src/music_folder.rs new file mode 100644 index 0000000..2a13758 --- /dev/null +++ b/entities/src/music_folder.rs @@ -0,0 +1,27 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "music_folder")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + #[sea_orm(unique)] + pub name: String, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation { + #[sea_orm(has_many = "super::album::Entity")] + Album, +} + +impl Related for Entity { + fn to() -> RelationDef { + Relation::Album.def() + } +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/entities/src/prelude.rs b/entities/src/prelude.rs new file mode 100644 index 0000000..72c6250 --- /dev/null +++ b/entities/src/prelude.rs @@ -0,0 +1,6 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 + +pub use super::album::Entity as Album; +pub use super::artist::Entity as Artist; +pub use super::music_folder::Entity as MusicFolder; +pub use super::user::Entity as User; diff --git a/entities/src/user.rs b/entities/src/user.rs new file mode 100644 index 0000000..0f88856 --- /dev/null +++ b/entities/src/user.rs @@ -0,0 +1,20 @@ +//! `SeaORM` Entity. Generated by sea-orm-codegen 0.12.3 + +use sea_orm::entity::prelude::*; +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)] +#[sea_orm(table_name = "user")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + #[sea_orm(unique)] + pub name: String, + pub password: String, + pub is_admin: bool, +} + +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] +pub enum Relation {} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/migration/Cargo.toml b/migration/Cargo.toml new file mode 100644 index 0000000..2b237c9 --- /dev/null +++ b/migration/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "migration" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +name = "migration" +path = "src/lib.rs" + +[dependencies] +async-std = { version = "1", features = ["attributes", "tokio1"] } + +[dependencies.sea-orm-migration] +version = "0.12" +features = ["runtime-tokio-rustls", "sqlx-postgres"] diff --git a/migration/README.md b/migration/README.md new file mode 100644 index 0000000..3b438d8 --- /dev/null +++ b/migration/README.md @@ -0,0 +1,41 @@ +# Running Migrator CLI + +- Generate a new migration file + ```sh + cargo run -- generate MIGRATION_NAME + ``` +- Apply all pending migrations + ```sh + cargo run + ``` + ```sh + cargo run -- up + ``` +- Apply first 10 pending migrations + ```sh + cargo run -- up -n 10 + ``` +- Rollback last applied migrations + ```sh + cargo run -- down + ``` +- Rollback last 10 applied migrations + ```sh + cargo run -- down -n 10 + ``` +- Drop all tables from the database, then reapply all migrations + ```sh + cargo run -- fresh + ``` +- Rollback all applied migrations, then reapply all migrations + ```sh + cargo run -- refresh + ``` +- Rollback all applied migrations + ```sh + cargo run -- reset + ``` +- Check the status of all migrations + ```sh + cargo run -- status + ``` diff --git a/migration/src/lib.rs b/migration/src/lib.rs new file mode 100644 index 0000000..0b1424e --- /dev/null +++ b/migration/src/lib.rs @@ -0,0 +1,20 @@ +pub use sea_orm_migration::prelude::*; + +mod m20220101_000001_create_user; +mod m20231009_181004_create_music_folder; +mod m20231009_181104_create_artist; +mod m20231009_181346_create_album; + +pub struct Migrator; + +#[async_trait::async_trait] +impl MigratorTrait for Migrator { + fn migrations() -> Vec> { + vec![ + Box::new(m20220101_000001_create_user::Migration), + Box::new(m20231009_181004_create_music_folder::Migration), + Box::new(m20231009_181104_create_artist::Migration), + Box::new(m20231009_181346_create_album::Migration), + ] + } +} diff --git a/migration/src/m20220101_000001_create_user.rs b/migration/src/m20220101_000001_create_user.rs new file mode 100644 index 0000000..a1f1804 --- /dev/null +++ b/migration/src/m20220101_000001_create_user.rs @@ -0,0 +1,68 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(User::Table) + .if_not_exists() + .col( + ColumnDef::new(User::Id) + .big_integer() + .not_null() + .primary_key() + .auto_increment() + .unique_key(), + ) + .col(ColumnDef::new(User::Name).string().not_null().unique_key()) + .col(ColumnDef::new(User::Password).string().not_null()) + .col( + ColumnDef::new(User::IsAdmin) + .boolean() + .not_null() + .default(false), + ) + .to_owned(), + ) + .await?; + + manager + .create_index( + Index::create() + .table(User::Table) + .col(User::Name) + .to_owned(), + ) + .await?; + + let query = Query::insert() + .into_table(User::Table) + .columns([User::Name, User::Password, User::IsAdmin]) + .values_panic(["admin".into(), "admin".into(), true.into()]) + .to_owned(); + + manager.exec_stmt(query).await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(User::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +pub enum User { + Table, + Id, + Name, + Password, + IsAdmin, +} diff --git a/migration/src/m20231009_181004_create_music_folder.rs b/migration/src/m20231009_181004_create_music_folder.rs new file mode 100644 index 0000000..1321f5f --- /dev/null +++ b/migration/src/m20231009_181004_create_music_folder.rs @@ -0,0 +1,45 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(MusicFolder::Table) + .if_not_exists() + .col( + ColumnDef::new(MusicFolder::Id) + .big_integer() + .not_null() + .primary_key() + .auto_increment() + .unique_key(), + ) + .col( + ColumnDef::new(MusicFolder::Name) + .string() + .not_null() + .unique_key(), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(MusicFolder::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +pub enum MusicFolder { + Table, + Id, + Name, +} diff --git a/migration/src/m20231009_181104_create_artist.rs b/migration/src/m20231009_181104_create_artist.rs new file mode 100644 index 0000000..a8fc2ef --- /dev/null +++ b/migration/src/m20231009_181104_create_artist.rs @@ -0,0 +1,63 @@ +use sea_orm_migration::prelude::*; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Artist::Table) + .if_not_exists() + .col( + ColumnDef::new(Artist::Id) + .big_integer() + .not_null() + .primary_key() + .auto_increment() + .unique_key(), + ) + .col( + ColumnDef::new(Artist::Name) + .string() + .not_null() + .unique_key(), + ) + .col(ColumnDef::new(Artist::CoverArt).string().null()) + .col(ColumnDef::new(Artist::ArtistImageUrl).string().null()) + .col( + ColumnDef::new(Artist::AlbumCount) + .integer() + .not_null() + .default(0), + ) + .col( + ColumnDef::new(Artist::Starred) + .boolean() + .not_null() + .default(false), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Artist::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +pub enum Artist { + Table, + Id, + Name, + CoverArt, + ArtistImageUrl, + AlbumCount, + Starred, +} diff --git a/migration/src/m20231009_181346_create_album.rs b/migration/src/m20231009_181346_create_album.rs new file mode 100644 index 0000000..3e281c6 --- /dev/null +++ b/migration/src/m20231009_181346_create_album.rs @@ -0,0 +1,104 @@ +use sea_orm_migration::prelude::*; + +use crate::{ + m20231009_181004_create_music_folder::MusicFolder, m20231009_181104_create_artist::Artist, +}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(Album::Table) + .if_not_exists() + .col( + ColumnDef::new(Album::Id) + .big_integer() + .not_null() + .auto_increment() + .primary_key(), + ) + .col(ColumnDef::new(Album::Name).string().not_null()) + .col(ColumnDef::new(Album::Artist).string().null()) + .col(ColumnDef::new(Album::ArtistId).big_integer().null()) + .col(ColumnDef::new(Album::CoverArt).string().null()) + .col(ColumnDef::new(Album::SongCount).integer().not_null()) + .col(ColumnDef::new(Album::Duration).big_integer().not_null()) + .col( + ColumnDef::new(Album::PlayCount) + .big_integer() + .not_null() + .default(0), + ) + .col( + ColumnDef::new(Album::Created) + .timestamp_with_time_zone() + .not_null(), + ) + .col( + ColumnDef::new(Album::Starred) + .timestamp_with_time_zone() + .null(), + ) + .col(ColumnDef::new(Album::Year).integer().null()) + .col(ColumnDef::new(Album::Genre).string().null()) + .col(ColumnDef::new(Album::MusicFolderId).big_integer().null()) + .to_owned(), + ) + .await?; + + manager + .create_foreign_key( + ForeignKey::create() + .from_tbl(Album::Table) + .from_col(Album::MusicFolderId) + .to_tbl(MusicFolder::Table) + .to_col(MusicFolder::Id) + .on_delete(ForeignKeyAction::Cascade) + .to_owned(), + ) + .await?; + + manager + .create_foreign_key( + ForeignKey::create() + .from_tbl(Album::Table) + .from_col(Album::ArtistId) + .to_tbl(Artist::Table) + .to_col(Artist::Id) + .on_delete(ForeignKeyAction::SetNull) + .to_owned(), + ) + .await?; + + Ok(()) + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(Album::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +pub enum Album { + Table, + Id, + Name, + Artist, + ArtistId, + CoverArt, + SongCount, + Duration, + PlayCount, + Created, + Starred, + Year, + Genre, + MusicFolderId, +} diff --git a/migration/src/main.rs b/migration/src/main.rs new file mode 100644 index 0000000..c6b6e48 --- /dev/null +++ b/migration/src/main.rs @@ -0,0 +1,6 @@ +use sea_orm_migration::prelude::*; + +#[async_std::main] +async fn main() { + cli::run_cli(migration::Migrator).await; +} diff --git a/migrations/0001_create-user.sql b/migrations/0001_create-user.sql deleted file mode 100644 index 7768f6f..0000000 --- a/migrations/0001_create-user.sql +++ /dev/null @@ -1,9 +0,0 @@ --- Add migration script here -CREATE TABLE users ( - id BIGSERIAL NOT NULL PRIMARY KEY, - name TEXT NOT NULL UNIQUE, - password TEXT NOT NULL, - is_admin BOOLEAN NOT NULL DEFAULT FALSE -); -INSERT INTO users (id, name, password, is_admin) -VALUES (0, 'admin', 'admin', TRUE); \ No newline at end of file diff --git a/migrations/0002_create-albums.sql b/migrations/0002_create-albums.sql deleted file mode 100644 index b91f8ae..0000000 --- a/migrations/0002_create-albums.sql +++ /dev/null @@ -1,15 +0,0 @@ --- Add migration script here -CREATE TABLE IF NOT EXISTS album ( - id BIGSERIAL NOT NULL PRIMARY KEY, - name TEXT NOT NULL, - artist TEXT, - artist_id INTEGER, - cover_art TEXT, - song_count INTEGER, - duration INTEGER, - play_count INTEGER, - created timestamptz DEFAULT current_timestamp, - starred timestamptz DEFAULT current_timestamp, - year INTEGER, - genre TEXT -); \ No newline at end of file diff --git a/migrations/0003_create-tracks.sql b/migrations/0003_create-tracks.sql deleted file mode 100644 index 2b1bca5..0000000 --- a/migrations/0003_create-tracks.sql +++ /dev/null @@ -1,21 +0,0 @@ --- Add migration script here -CREATE TABLE IF NOT EXISTS tracks ( - id BIGSERIAL NOT NULL PRIMARY KEY, - parent INTEGER NOT NULL REFERENCES album(id), - is_dir BOOLEAN NOT NULL DEFAULT FALSE, - title TEXT NOT NULL, - album TEXT, - artist TEXT, - track INTEGER, - year INTEGER, - genre TEXT, - cover_art TEXT, - duration INTEGER, - path TEXT, - play_count INTEGER, - created timestamptz DEFAULT current_timestamp, - starred timestamptz, - album_id TEXT, - artist_id TEXT, - disc_number INTEGER -) \ No newline at end of file diff --git a/src/rest/get_album.rs b/src/rest/get_album.rs deleted file mode 100644 index 7c80765..0000000 --- a/src/rest/get_album.rs +++ /dev/null @@ -1,121 +0,0 @@ -use crate::{ - authentication::Authentication, - subsonic::{AlbumId3, Child, Error, MediaType, SubsonicResponse}, - utils::{self, middleware::DbConn}, -}; - -use poem::web::{Data, Query}; -use serde::Deserialize; - -#[poem::handler] -pub async fn get_album( - Data(conn): Data<&DbConn>, - auth: Authentication, - Query(params): Query, -) -> SubsonicResponse { - let u = utils::verify_user(conn.clone(), auth).await; - - match u { - Ok(_) => {} - Err(e) => return e, - } - - let mut count = 0; - let album = match params.id { - 11 => AlbumId3 { - id: 11, - name: "Example".to_string(), - artist: Some("Example".to_string()), - song_count: 5, - duration: 100, - songs: vec![ - Child { - id: "tr-111".to_string(), - title: "Example - 1".to_string(), - album: Some("Example".to_string()), - duration: Some(20), - content_type: Some("audio/mpeg".to_string()), - r#type: Some(MediaType::Music), - track: Some({ - count += 1; - count - }), - ..Default::default() - }, - Child { - id: "tr-112".to_string(), - title: "Example - 2".to_string(), - album: Some("Example".to_string()), - duration: Some(20), - content_type: Some("audio/mpeg".to_string()), - r#type: Some(MediaType::Music), - track: Some({ - count += 1; - count - }), - ..Default::default() - }, - Child { - id: "tr-113".to_string(), - title: "Example - 3".to_string(), - album: Some("Example".to_string()), - duration: Some(20), - content_type: Some("audio/mpeg".to_string()), - r#type: Some(MediaType::Music), - track: Some({ - count += 1; - count - }), - ..Default::default() - }, - Child { - id: "tr-114".to_string(), - title: "Example - 4".to_string(), - album: Some("Example".to_string()), - duration: Some(20), - content_type: Some("audio/mpeg".to_string()), - r#type: Some(MediaType::Music), - track: Some({ - count += 1; - count - }), - ..Default::default() - }, - Child { - id: "tr-115".to_string(), - title: "Example - 5".to_string(), - album: Some("Example".to_string()), - duration: Some(20), - content_type: Some("audio/mpeg".to_string()), - r#type: Some(MediaType::Music), - track: Some({ - count += 1; - count - }), - ..Default::default() - }, - ], - ..Default::default() - }, - 12 => AlbumId3 { - id: 12, - name: "Example 2".to_string(), - artist: Some("Example 2".to_string()), - song_count: 7, - duration: 200, - ..Default::default() - }, - _ => { - return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(Some( - "Album does not exist".to_string(), - ))) - } - }; - - SubsonicResponse::new_album(album) -} - -#[derive(Debug, Clone, Deserialize)] -pub struct GetAlbumParams { - pub id: i32, -} diff --git a/src/rest/get_album_list.rs b/src/rest/get_album_list.rs deleted file mode 100644 index c5f24e6..0000000 --- a/src/rest/get_album_list.rs +++ /dev/null @@ -1,98 +0,0 @@ -use poem::web::{Data, Query}; -use serde::Deserialize; - -use crate::{ - authentication::Authentication, - random_types::SortType, - subsonic::{Child, Error, SubsonicResponse}, - utils::{self, middleware::DbConn}, -}; - -#[poem::handler] -pub async fn get_album_list( - Data(conn): Data<&DbConn>, - auth: Authentication, - Query(params): Query, -) -> SubsonicResponse { - let u = utils::verify_user(conn.clone(), auth).await; - - match u { - Ok(_) => {} - Err(e) => return e, - } - - let _params = match params.verify() { - Ok(p) => p, - Err(e) => return e, - }; - - let album_list = vec![ - Child { - id: "al-11".to_string(), - parent: Some(1), - title: "Example".to_string(), - artist: Some("Example".to_string()), - is_dir: true, - ..Default::default() - }, - Child { - id: "al-12".to_string(), - parent: Some(1), - title: "Example 2".to_string(), - artist: Some("Example 2".to_string()), - is_dir: true, - ..Default::default() - }, - ]; - - SubsonicResponse::new_album_list(album_list) -} - -#[derive(Debug, Clone, Deserialize)] -pub struct GetAlbumListParams { - #[serde(rename = "type")] - pub r#type: SortType, - #[serde(default = "default_size")] - pub size: i32, - #[serde(default)] - pub offset: i32, - #[serde(default)] - pub from_year: Option, - #[serde(default)] - pub to_year: Option, - #[serde(default)] - pub genre: Option, - #[serde(default)] - pub music_folder_id: Option, -} - -impl GetAlbumListParams { - #[allow(clippy::result_large_err)] - pub fn verify(self) -> Result { - if self.r#type == SortType::ByYear { - if self.from_year.is_none() || self.to_year.is_none() { - return Err(SubsonicResponse::new_error( - Error::RequiredParameterMissing(Some( - "Missing required parameter: fromYear or toYear".to_string(), - )), - )); - } - } else if self.r#type == SortType::ByGenre && self.genre.is_none() { - return Err(SubsonicResponse::new_error( - Error::RequiredParameterMissing(Some( - "Missing required parameter: genre".to_string(), - )), - )); - } else if self.size > 500 || self.size < 1 { - return Err(SubsonicResponse::new_error(Error::Generic(Some( - "size must be between 1 and 500".to_string(), - )))); - } - - Ok(self) - } -} - -const fn default_size() -> i32 { - 10 -} diff --git a/src/rest/get_album_list2.rs b/src/rest/get_album_list2.rs deleted file mode 100644 index cb504ba..0000000 --- a/src/rest/get_album_list2.rs +++ /dev/null @@ -1,52 +0,0 @@ -use poem::web::{Data, Query}; - -use crate::{ - authentication::Authentication, - rest::get_album_list::GetAlbumListParams, - subsonic::{AlbumId3, SubsonicResponse}, - utils::{self, middleware::DbConn}, -}; - -#[poem::handler] -pub async fn get_album_list2( - Data(conn): Data<&DbConn>, - auth: Authentication, - Query(params): Query, -) -> SubsonicResponse { - let u = utils::verify_user(conn.clone(), auth).await; - - match u { - Ok(_) => {} - Err(e) => return e, - } - - let params = match params.verify() { - Ok(p) => p, - Err(e) => return e, - }; - - if params.offset > 0 { - return SubsonicResponse::new_album_list2(Vec::new()); - } - - let album_list = vec![ - AlbumId3 { - id: 11, - name: "Example".to_string(), - artist: Some("Example".to_string()), - song_count: 5, - duration: 100, - ..Default::default() - }, - AlbumId3 { - id: 12, - name: "Example 2".to_string(), - artist: Some("Example 2".to_string()), - song_count: 7, - duration: 200, - ..Default::default() - }, - ]; - - SubsonicResponse::new_album_list2(album_list) -} diff --git a/src/user.rs b/src/user.rs deleted file mode 100644 index be73f9a..0000000 --- a/src/user.rs +++ /dev/null @@ -1,30 +0,0 @@ -use color_eyre::Result; -use serde::{Deserialize, Serialize}; - -use crate::utils::middleware::DbConn; - -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct User { - pub id: i64, - pub name: String, - /// I hate this. It's stored in plaintext. Why? - password: String, - pub is_admin: bool, -} - -impl User { - pub fn verify(&self, hashed_password: &str, salt: &str) -> bool { - let ours = md5::compute(format!("{}{}", self.password, salt)); - let ours = format!("{ours:x}"); - - ours == hashed_password - } -} - -pub async fn get_user(conn: DbConn, name: &str) -> Result> { - let user = sqlx::query_as!(User, "SELECT * FROM users WHERE name = $1", name) - .fetch_optional(&*conn) - .await?; - - Ok(user) -} diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 828d6be..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,36 +0,0 @@ -use tracing::error; - -use crate::{ - authentication::Authentication, - subsonic::{Error, SubsonicResponse}, - user::{get_user, User}, -}; - -use self::middleware::DbConn; - -pub async fn verify_user(conn: DbConn, auth: Authentication) -> Result { - let user = get_user(conn, &auth.username).await; - - match user { - Ok(Some(u)) => { - if u.verify(&auth.token, &auth.salt) { - Ok(u) - } else { - Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword( - None, - ))) - } - } - Ok(None) => Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword( - None, - ))), - Err(e) => { - error!("Error getting user: {e}"); - Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword( - None, - ))) - } - } -} - -pub mod middleware; diff --git a/src/utils/middleware.rs b/src/utils/middleware.rs deleted file mode 100644 index d6e0c5b..0000000 --- a/src/utils/middleware.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::sync::Arc; - -use poem::{Endpoint, Middleware}; -use sqlx::PgPool; - -pub type DbConn = Arc; - -#[derive(Debug)] -pub struct DbConnectionMiddleware { - db: PgPool, -} - -impl DbConnectionMiddleware { - pub const fn new(db: PgPool) -> Self { - Self { db } - } -} - -impl Middleware for DbConnectionMiddleware { - type Output = DbConnectionMwEndpoint; - - fn transform(&self, ep: E) -> Self::Output { - DbConnectionMwEndpoint { - inner: ep, - db: self.db.clone(), - } - } -} - -#[derive(Debug)] -pub struct DbConnectionMwEndpoint { - inner: E, - db: PgPool, -} - -#[poem::async_trait] -impl Endpoint for DbConnectionMwEndpoint { - type Output = E::Output; - - async fn call(&self, mut req: poem::Request) -> Result { - let conn = Arc::new(self.db.clone()); - - req.extensions_mut().insert(conn); - - self.inner.call(req).await - } -}