From 71b7eca0b9082ff8b4317979770de00d3764585a Mon Sep 17 00:00:00 2001 From: Lyssieth Date: Tue, 10 Oct 2023 22:06:32 +0300 Subject: [PATCH] feat: more kejiggering, added Sentry support and tracing spans. (personally I use glitchtip :3) --- .dockerignore | 6 + .env.example | 2 + .gitignore | 3 +- Cargo.lock | 507 ++++++++++++++++++++++++- Justfile | 11 +- entities/src/album.rs | 2 +- entities/src/artist.rs | 3 - migration/src/m000004_create_artist.rs | 11 - migration/src/m000006_create_album.rs | 2 +- mount.sh | 23 ++ rave/Cargo.toml | 13 + rave/src/main.rs | 24 +- rave/src/random_types/sort_type.rs | 4 +- rave/src/rest/get_album.rs | 15 +- rave/src/rest/get_album_list.rs | 57 +-- rave/src/rest/get_license.rs | 2 + rave/src/rest/get_music_folders.rs | 2 + rave/src/rest/get_scan_status.rs | 5 +- rave/src/rest/ping.rs | 2 + rave/src/rest/start_scan.rs | 9 +- rave/src/rest/stream.rs | 2 + rave/src/scan.rs | 89 ++++- rave/src/utils.rs | 5 +- 23 files changed, 721 insertions(+), 78 deletions(-) create mode 100644 .dockerignore create mode 100644 .env.example create mode 100644 mount.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..1ce7311 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +/.* +/target +/docker-compose.yml +/Justfile +/mounth.sh +*.md diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..446dc91 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +export DATABASE_URL="postgres://localhost:5432/rave" # The database URL +export RAVE_SENTRY_DSN="" # An optional Sentry DSN to send errors to diff --git a/.gitignore b/.gitignore index 8a2ac5e..7c8d16e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /target users.db* -.en* +.env* +!.env.example .rave-dev-db diff --git a/Cargo.lock b/Cargo.lock index 491d351..588e54a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -599,6 +599,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.4" @@ -722,6 +732,16 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "serde", + "uuid", +] + [[package]] name = "der" version = "0.7.8" @@ -869,6 +889,18 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "finl_unicode" version = "1.2.0" @@ -902,6 +934,21 @@ 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.0" @@ -1196,6 +1243,17 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "hostname" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c731c3e10504cc8ed35cfe2f1db4c9274c3d35fa486e3b31df46f068ef3e867" +dependencies = [ + "libc", + "match_cfg", + "winapi", +] + [[package]] name = "http" version = "0.2.9" @@ -1254,6 +1312,33 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d78e1e73ec14cf7375674f74d7dde185c8206fd9dea6fb6295e8a98098aaa97" +dependencies = [ + "futures-util", + "http", + "hyper", + "rustls", + "tokio", + "tokio-rustls", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + [[package]] name = "iana-time-zone" version = "0.1.57" @@ -1371,6 +1456,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "ipnet" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6" + [[package]] name = "itertools" version = "0.11.0" @@ -1467,6 +1558,12 @@ dependencies = [ "value-bag", ] +[[package]] +name = "match_cfg" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4" + [[package]] name = "matchers" version = "0.1.0" @@ -1567,6 +1664,24 @@ dependencies = [ "version_check", ] +[[package]] +name = "native-tls" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + [[package]] name = "nom" version = "7.1.3" @@ -1678,6 +1793,50 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c" +dependencies = [ + "bitflags 2.4.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.38", +] + +[[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.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "3.9.1" @@ -1687,6 +1846,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "os_info" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006e42d5b888366f1880eda20371fedde764ed2213dc8496f49622fa0c99cd5e" +dependencies = [ + "log", + "serde", + "winapi", +] + [[package]] name = "ouroboros" version = "0.17.2" @@ -1773,6 +1943,26 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" +[[package]] +name = "pin-project" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fda4ed1c6c173e3fc7a83629421152e01d7b1f9b7f65fb301e490e8cfc656422" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + [[package]] name = "pin-project-lite" version = "0.2.13" @@ -1863,6 +2053,7 @@ dependencies = [ "tokio", "tokio-stream", "tokio-util", + "tower", "tracing", ] @@ -2089,6 +2280,8 @@ dependencies = [ "poem-ext", "quick-xml", "sea-orm", + "sentry", + "sentry-tracing", "serde", "serde_json", "time", @@ -2153,6 +2346,49 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" +[[package]] +name = "reqwest" +version = "0.11.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "hyper", + "hyper-rustls", + "hyper-tls", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "system-configuration", + "tokio", + "tokio-native-tls", + "tokio-rustls", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots 0.25.2", + "winreg", +] + [[package]] name = "rfc7239" version = "0.1.0" @@ -2247,6 +2483,7 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" dependencies = [ + "log", "ring", "rustls-webpki", "sct", @@ -2277,6 +2514,15 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +[[package]] +name = "schannel" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c3733bf4cf7ea0880754e19cb5a462007c4a8c1914bff372ccc95b464f1df88" +dependencies = [ + "windows-sys", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -2437,12 +2683,157 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "security-framework" +version = "2.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" +[[package]] +name = "sentry" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0097a48cd1999d983909f07cb03b15241c5af29e5e679379efac1c06296abecc" +dependencies = [ + "httpdate", + "native-tls", + "reqwest", + "rustls", + "sentry-backtrace", + "sentry-contexts", + "sentry-core", + "sentry-debug-images", + "sentry-panic", + "sentry-tower", + "sentry-tracing", + "tokio", + "ureq", + "webpki-roots 0.25.2", +] + +[[package]] +name = "sentry-backtrace" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a7b80fa1dd6830a348d38a8d3a9761179047757b7dca29aef82db0118b9670" +dependencies = [ + "backtrace", + "once_cell", + "regex", + "sentry-core", +] + +[[package]] +name = "sentry-contexts" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7615dc588930f1fd2e721774f25844ae93add2dbe2d3c2f995ce5049af898147" +dependencies = [ + "hostname", + "libc", + "os_info", + "rustc_version", + "sentry-core", + "uname", +] + +[[package]] +name = "sentry-core" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f51264e4013ed9b16558cce43917b983fa38170de2ca480349ceb57d71d6053" +dependencies = [ + "once_cell", + "rand", + "sentry-types", + "serde", + "serde_json", +] + +[[package]] +name = "sentry-debug-images" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fe6180fa564d40bb942c9f0084ffb5de691c7357ead6a2b7a3154fae9e401dd" +dependencies = [ + "findshlibs", + "once_cell", + "sentry-core", +] + +[[package]] +name = "sentry-panic" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323160213bba549f9737317b152af116af35c0410f4468772ee9b606d3d6e0fa" +dependencies = [ + "sentry-backtrace", + "sentry-core", +] + +[[package]] +name = "sentry-tower" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffe3ab7bf7f65c9f8ccd20aa136ce5b2140aa6d6a11339e823cd43a7d694a9e" +dependencies = [ + "sentry-core", + "tower-layer", + "tower-service", +] + +[[package]] +name = "sentry-tracing" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38033822128e73f7b6ca74c1631cef8868890c6cb4008a291cf73530f87b4eac" +dependencies = [ + "sentry-backtrace", + "sentry-core", + "tracing-core", + "tracing-subscriber", +] + +[[package]] +name = "sentry-types" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e663b3eb62ddfc023c9cf5432daf5f1a4f6acb1df4d78dd80b740b32dd1a740" +dependencies = [ + "debugid", + "hex", + "rand", + "serde", + "serde_json", + "thiserror", + "time", + "url", + "uuid", +] + [[package]] name = "serde" version = "1.0.188" @@ -2674,7 +3065,7 @@ dependencies = [ "tokio-stream", "tracing", "url", - "webpki-roots", + "webpki-roots 0.24.0", ] [[package]] @@ -2879,6 +3270,27 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "system-configuration" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "tempfile" version = "3.8.0" @@ -2997,6 +3409,26 @@ dependencies = [ "syn 2.0.38", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls", + "tokio", +] + [[package]] name = "tokio-shield" version = "0.1.1" @@ -3049,6 +3481,29 @@ dependencies = [ "winnow", ] +[[package]] +name = "tower" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +dependencies = [ + "futures-core", + "futures-util", + "pin-project", + "pin-project-lite", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-layer" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" + [[package]] name = "tower-service" version = "0.3.2" @@ -3166,6 +3621,15 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +[[package]] +name = "uname" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" +dependencies = [ + "libc", +] + [[package]] name = "uncased" version = "0.9.9" @@ -3239,6 +3703,22 @@ version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" +[[package]] +name = "ureq" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5ccd538d4a604753ebc2f17cd9946e89b77bf87f6a8e2309667c6f2e87855e3" +dependencies = [ + "base64", + "log", + "native-tls", + "once_cell", + "rustls", + "rustls-webpki", + "url", + "webpki-roots 0.25.2", +] + [[package]] name = "url" version = "2.4.1" @@ -3266,6 +3746,15 @@ 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" @@ -3396,6 +3885,12 @@ dependencies = [ "rustls-webpki", ] +[[package]] +name = "webpki-roots" +version = "0.25.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc" + [[package]] name = "whoami" version = "1.4.1" @@ -3508,6 +4003,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "winreg" +version = "0.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +dependencies = [ + "cfg-if", + "windows-sys", +] + [[package]] name = "zeroize" version = "1.6.0" diff --git a/Justfile b/Justfile index b38a666..6d7f77c 100644 --- a/Justfile +++ b/Justfile @@ -3,5 +3,12 @@ set dotenv-load := false build: docker buildx build --pull --platform linux/amd64 -t "forge.lys.ee/lyssieth/rave:amd64" --load . -run: build - docker run -it -v ./data:/storage lyssieth/rave:latest +mount: + bash ./mount.sh + +run: mount + RAVE_STORAGE_DIR=/tmp/media-for-rave cargo r + +update-db: + sea migrate fresh + sea generate entity -o ./entities/src --with-serde both --date-time-crate time --lib \ No newline at end of file diff --git a/entities/src/album.rs b/entities/src/album.rs index 053ca1a..5866c63 100644 --- a/entities/src/album.rs +++ b/entities/src/album.rs @@ -18,7 +18,7 @@ pub struct Model { pub starred: Option, pub year: Option, pub genre_ids: Option>, - pub played: TimeDateTimeWithTimeZone, + pub played: Option, pub user_rating: Option, pub artist_ids: Option>, pub original_release_date: Option, diff --git a/entities/src/artist.rs b/entities/src/artist.rs index 2c086a7..8c28ec5 100644 --- a/entities/src/artist.rs +++ b/entities/src/artist.rs @@ -13,9 +13,6 @@ pub struct Model { pub artist_image_url: Option, pub album_count: i32, pub starred: bool, - pub music_brainz_id: String, - pub sort_name: String, - pub roles: String, } #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] diff --git a/migration/src/m000004_create_artist.rs b/migration/src/m000004_create_artist.rs index afa9d16..415f43b 100644 --- a/migration/src/m000004_create_artist.rs +++ b/migration/src/m000004_create_artist.rs @@ -36,14 +36,6 @@ impl MigrationTrait for Migration { .not_null() .default(false), ) - .col( - ColumnDef::new(Artist::MusicBrainzId) - .string() - .not_null() - .default(""), - ) - .col(ColumnDef::new(Artist::SortName).string().not_null()) - .col(ColumnDef::new(Artist::Roles).string().not_null()) .to_owned(), ) .await?; @@ -79,7 +71,4 @@ pub enum Artist { ArtistImageUrl, AlbumCount, Starred, - MusicBrainzId, - SortName, - Roles, } diff --git a/migration/src/m000006_create_album.rs b/migration/src/m000006_create_album.rs index 93fef11..2010440 100644 --- a/migration/src/m000006_create_album.rs +++ b/migration/src/m000006_create_album.rs @@ -53,7 +53,7 @@ impl MigrationTrait for Migration { .col( ColumnDef::new(Album::Played) .timestamp_with_time_zone() - .not_null(), + .null(), ) .col(ColumnDef::new(Album::UserRating).tiny_integer().null()) .col( diff --git a/mount.sh b/mount.sh new file mode 100644 index 0000000..07285e1 --- /dev/null +++ b/mount.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +set -euo pipefail # exit on error + +# check for `fuse-overlayfs` executable +if ! command -v fuse-overlayfs &>/dev/null; then + echo "fuse-overlayfs could not be found" + exit 1 +fi + +# check if already mounted; im /tmp so it's ephemeral +if [ -d /tmp/media-for-rave ]; then exit 0; fi + +# mount the music directory; assumes a fstab entry like: +# /dev/sda1 /mnt/Media ext4 defaults 0 0 +# or equivalent; as long as it mounts to /mnt/Media +sudo mount /mnt/Media + +# create the overlayfs +mkdir -p /tmp/overlay /tmp/work /tmp/media-for-rave + +# mount the overlayfs +fuse-overlayfs -o lowerdir=/mnt/Media/Music -o upperdir=/tmp/overlay -o workdir=/tmp/work /tmp/media-for-rave diff --git a/rave/Cargo.toml b/rave/Cargo.toml index a8e95da..5606eea 100644 --- a/rave/Cargo.toml +++ b/rave/Cargo.toml @@ -17,6 +17,7 @@ poem = { version = "1.3.58", features = [ "session", "static-files", "xml", + "tower-compat", ] } poem-ext = "0.9.4" quick-xml = { version = "0.30.0", features = ["serialize"] } @@ -41,3 +42,15 @@ once_cell = { version = "1.18.0", features = ["parking_lot"] } futures-lite = "1.13.0" id3 = { version = "1.8.0", features = ["tokio"] } tracing-appender = "0.2.2" +sentry = { version = "0.31.7", default-features = false, features = [ + "backtrace", + "contexts", + "panic", + "transport", + "debug-images", + "reqwest", + "rustls", + "tower", + "tracing", +] } +sentry-tracing = { version = "0.31.7", features = ["backtrace"] } diff --git a/rave/src/main.rs b/rave/src/main.rs index 639818a..001953d 100644 --- a/rave/src/main.rs +++ b/rave/src/main.rs @@ -8,13 +8,14 @@ use color_eyre::Result; use migration::{Migrator, MigratorTrait}; use poem::{ listener::TcpListener, - middleware, + middleware::{self, TowerLayerCompatExt}, web::{CompressionAlgo, CompressionLevel}, - Endpoint, EndpointExt, Route, + Endpoint, EndpointExt, Request, Route, }; use poem_ext::db::DbTransactionMiddleware; use sea_orm::{ConnectOptions, Database, DatabaseConnection}; -use tracing::info; +use sentry::integrations::tower::NewSentryLayer; +use tracing::{debug, info}; use tracing_appender::non_blocking::WorkerGuard; use tracing_subscriber::{ fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, @@ -36,6 +37,21 @@ async fn main() -> Result<()> { color_eyre::install()?; let _guards = install_tracing().await?; + let _sentry = if let Ok(dsn) = std::env::var("RAVE_SENTRY_DSN") { + let guard = sentry::init(sentry::ClientOptions { + dsn: Some(dsn.parse()?), + release: sentry::release_name!(), + traces_sample_rate: 1.0, + ..Default::default() + }); + + debug!("Sentry initialized"); + + Some(guard) + } else { + None + }; + let route = create_route(); let dbc = create_pool().await?; @@ -72,6 +88,7 @@ fn create_route() -> Box> { Route::new() .nest("/", ui::build()) .nest("/rest", rest::build()) + .with(NewSentryLayer::::new_from_top().compat()) .with(middleware::CatchPanic::new()) .with( middleware::Compression::new() @@ -117,6 +134,7 @@ async fn install_tracing() -> Result<[WorkerGuard; 1]> { .with_writer(non_blocking) .with_filter(filter), ) + .with(sentry_tracing::layer()) .try_init()?; Ok([guard]) diff --git a/rave/src/random_types/sort_type.rs b/rave/src/random_types/sort_type.rs index f2f4754..45a4835 100644 --- a/rave/src/random_types/sort_type.rs +++ b/rave/src/random_types/sort_type.rs @@ -1,7 +1,7 @@ use std::str::FromStr; use serde::Deserialize; -use tracing::warn; +use tracing::error; use crate::subsonic::Error; @@ -45,7 +45,7 @@ impl FromStr for SortType { "bygenre" => Ok(Self::ByGenre), _ => { - warn!("got invalid type parameter {s}"); + error!("got invalid type parameter {s}"); Err(Error::Generic(Some( "type parameter is invalid".to_string(), ))) diff --git a/rave/src/rest/get_album.rs b/rave/src/rest/get_album.rs index 5c28570..d2a0526 100644 --- a/rave/src/rest/get_album.rs +++ b/rave/src/rest/get_album.rs @@ -9,9 +9,10 @@ use poem::web::{Data, Query}; use poem_ext::db::DbTxn; use sea_orm::{EntityTrait, ModelTrait}; use serde::Deserialize; -use tracing::warn; +use tracing::{error, instrument, warn}; #[poem::handler] +#[instrument(skip(txn, auth))] pub async fn get_album( Data(txn): Data<&DbTxn>, auth: Authentication, @@ -30,7 +31,10 @@ pub async fn get_album( Ok(Some(_)) => unreachable!("Some(album) covered by `let .. else`"), Ok(None) => return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(None)), Err(e) => { - warn!("Error getting album: {}", e); + error!( + error = &e as &dyn std::error::Error, + "Error getting album: {e}" + ); return SubsonicResponse::new_error(Error::Generic(None)); } } @@ -41,12 +45,15 @@ pub async fn get_album( let tracks = match tracks { Ok(t) => t, Err(e) => { - warn!("Error getting tracks: {}", e); + error!( + error = &e as &dyn std::error::Error, + "Error getting tracks: {e}" + ); return SubsonicResponse::new_error(Error::Generic(None)); } }; - todo!() + todo!("finish implementing get_album") // SubsonicResponse::new_album(album, tracks) } diff --git a/rave/src/rest/get_album_list.rs b/rave/src/rest/get_album_list.rs index e2ac06c..7240bf1 100644 --- a/rave/src/rest/get_album_list.rs +++ b/rave/src/rest/get_album_list.rs @@ -11,7 +11,7 @@ use poem::{ use poem_ext::db::DbTxn; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect}; use serde::Deserialize; -use tracing::debug; +use tracing::instrument; use crate::{ authentication::Authentication, @@ -30,6 +30,7 @@ macro_rules! error_or { } #[poem::handler] +#[instrument(skip(req, txn, auth))] pub async fn get_album_list( req: &Request, Data(txn): Data<&DbTxn>, @@ -66,7 +67,6 @@ pub async fn get_album_list( // match album_list { // Ok(a) => { - // debug!("uri path: {}", req.uri().path()); // if req.uri().path().contains("getAlbumList2") { // SubsonicResponse::new_album_list2(a) // } else { @@ -77,17 +77,17 @@ pub async fn get_album_list( // } } -#[allow(unused_variables)] +#[instrument(skip(_conn, _params))] async fn get_album_list_random( - conn: DbTxn, - params: GetAlbumListParams, + _conn: DbTxn, + _params: GetAlbumListParams, ) -> Result, Error> { Err(Error::Generic(Some( "Sorting by random not implemented".to_string(), ))) } -#[allow(unused_variables)] +#[instrument(skip(conn))] async fn get_album_list_newest( conn: DbTxn, params: GetAlbumListParams, @@ -102,17 +102,22 @@ async fn get_album_list_newest( error_or!(albums) } -#[allow(unused_variables)] +#[instrument(skip(conn))] async fn get_album_list_highest( conn: DbTxn, params: GetAlbumListParams, ) -> Result, Error> { - Err(Error::Generic(Some( - "Sorting by highest rating not implemented".to_string(), - ))) + let albums = Album::find() + .order_by_desc(album::Column::UserRating) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) } -#[allow(unused_variables)] +#[instrument(skip(conn))] async fn get_album_list_frequent( conn: DbTxn, params: GetAlbumListParams, @@ -127,17 +132,22 @@ async fn get_album_list_frequent( error_or!(albums) } -#[allow(unused_variables)] +#[instrument(skip(conn))] async fn get_album_list_recent( conn: DbTxn, params: GetAlbumListParams, ) -> Result, Error> { - Err(Error::Generic(Some( - "Sorting by recently played not implemented".to_string(), - ))) + let albums = Album::find() + .order_by_desc(album::Column::Played) + .limit(params.size) + .offset(params.offset) + .all(&*conn) + .await; + + error_or!(albums) } -#[allow(unused_variables)] +#[instrument(skip(conn))] async fn get_album_list_alphabetical_by_name( conn: DbTxn, params: GetAlbumListParams, @@ -152,7 +162,7 @@ async fn get_album_list_alphabetical_by_name( error_or!(albums) } -#[allow(unused_variables)] +#[instrument(skip(conn))] async fn get_album_list_alphabetical_by_artist( conn: DbTxn, params: GetAlbumListParams, @@ -171,7 +181,7 @@ async fn get_album_list_alphabetical_by_artist( error_or!(albums) } -#[allow(unused_variables)] +#[instrument(skip(conn))] async fn get_album_list_starred( conn: DbTxn, params: GetAlbumListParams, @@ -186,6 +196,7 @@ async fn get_album_list_starred( error_or!(albums) } +#[instrument(skip(conn))] async fn get_album_list_by_year( conn: DbTxn, params: GetAlbumListParams, @@ -223,14 +234,15 @@ async fn get_album_list_by_genre( ))); }; - let genre_id = Genre::find() + let genre = Genre::find() .filter(genre::Column::Name.eq(genre)) .one(&*conn) .await; - let genre = match genre_id { - Ok(Some(g)) => g, - Ok(None) | Err(_) => return Err(Error::Generic(Some("Genre not found".to_string()))), + let Ok(Some(genre)) = genre else { + return Err(Error::RequestedDataWasNotFound(Some( + "Genre not found".to_string(), + ))); }; let albums = Album::find() @@ -278,7 +290,6 @@ pub struct GetAlbumListParams { } 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() { diff --git a/rave/src/rest/get_license.rs b/rave/src/rest/get_license.rs index 86ae684..63d4552 100644 --- a/rave/src/rest/get_license.rs +++ b/rave/src/rest/get_license.rs @@ -1,5 +1,6 @@ use poem::web::Data; use poem_ext::db::DbTxn; +use tracing::instrument; use crate::{ authentication::Authentication, @@ -8,6 +9,7 @@ use crate::{ }; #[poem::handler] +#[instrument(skip(txn, auth))] pub async fn get_license(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { let u = utils::verify_user(txn.clone(), auth).await; diff --git a/rave/src/rest/get_music_folders.rs b/rave/src/rest/get_music_folders.rs index 8b40535..f3af967 100644 --- a/rave/src/rest/get_music_folders.rs +++ b/rave/src/rest/get_music_folders.rs @@ -2,6 +2,7 @@ use entities::prelude::MusicFolder; use poem::web::Data; use poem_ext::db::DbTxn; use sea_orm::EntityTrait; +use tracing::instrument; use crate::{ authentication::Authentication, @@ -10,6 +11,7 @@ use crate::{ }; #[poem::handler] +#[instrument(skip(txn, auth))] pub async fn get_music_folders(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { let u = utils::verify_user(txn.clone(), auth).await; diff --git a/rave/src/rest/get_scan_status.rs b/rave/src/rest/get_scan_status.rs index dcfaf4b..7b37dd8 100644 --- a/rave/src/rest/get_scan_status.rs +++ b/rave/src/rest/get_scan_status.rs @@ -1,6 +1,6 @@ use poem::web::Data; use poem_ext::db::DbTxn; -use tracing::warn; +use tracing::{error, instrument}; use crate::{ authentication::Authentication, @@ -10,6 +10,7 @@ use crate::{ }; #[poem::handler] +#[instrument(skip(txn, auth))] pub async fn get_scan_status(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { let u = utils::verify_user(txn.clone(), auth).await; @@ -23,7 +24,7 @@ pub async fn get_scan_status(Data(txn): Data<&DbTxn>, auth: Authentication) -> S match status { Ok(status) => SubsonicResponse::new_scan_status(status.scanning, status.count), Err(e) => { - warn!("Error getting scan status: {}", e); + error!(error = e.root_cause(), "Error getting scan status: {e}"); SubsonicResponse::new_error(Error::Generic(None)) } } diff --git a/rave/src/rest/ping.rs b/rave/src/rest/ping.rs index 820a73e..6db1903 100644 --- a/rave/src/rest/ping.rs +++ b/rave/src/rest/ping.rs @@ -1,5 +1,6 @@ use poem::web::Data; use poem_ext::db::DbTxn; +use tracing::instrument; use crate::{ authentication::Authentication, @@ -8,6 +9,7 @@ use crate::{ }; #[poem::handler] +#[instrument(skip(txn, auth))] pub async fn ping(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { let u = utils::verify_user(txn.clone(), auth).await; diff --git a/rave/src/rest/start_scan.rs b/rave/src/rest/start_scan.rs index 8e8e16b..5fcdd4c 100644 --- a/rave/src/rest/start_scan.rs +++ b/rave/src/rest/start_scan.rs @@ -1,6 +1,6 @@ use poem::web::Data; use poem_ext::db::DbTxn; -use tracing::warn; +use tracing::{error, instrument}; use crate::{ authentication::Authentication, @@ -9,6 +9,7 @@ use crate::{ }; #[poem::handler] +#[instrument(skip(txn, auth))] pub async fn start_scan(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { let u = utils::verify_user(txn.clone(), auth).await; @@ -31,15 +32,15 @@ pub async fn start_scan(Data(txn): Data<&DbTxn>, auth: Authentication) -> Subson if status.errors.is_empty() { SubsonicResponse::new_scan_status(status.scanning, status.count) } else { - warn!("Failed to start scan:"); + error!("Failed to start scan:"); for e in status.errors { - warn!("{e}"); + error!(error = e.root_cause(), "{e}"); } SubsonicResponse::new_error(Error::Generic(None)) } } Err(e) => { - warn!("Failed to start scan: {e}"); + error!(error = e.root_cause(), "Failed to start scan: {e}"); SubsonicResponse::new_error(Error::Generic(None)) } } diff --git a/rave/src/rest/stream.rs b/rave/src/rest/stream.rs index 7bdd74e..cb9d413 100644 --- a/rave/src/rest/stream.rs +++ b/rave/src/rest/stream.rs @@ -5,6 +5,7 @@ use poem::{ }; use poem_ext::db::DbTxn; use serde::Deserialize; +use tracing::instrument; use crate::{ authentication::Authentication, @@ -14,6 +15,7 @@ use crate::{ const SONG: &[u8] = include_bytes!("../../../../data.mp3"); #[poem::handler] +#[instrument(skip(txn, auth))] pub async fn stream( Data(txn): Data<&DbTxn>, auth: Authentication, diff --git a/rave/src/scan.rs b/rave/src/scan.rs index d3fcbe2..bed1eef 100644 --- a/rave/src/scan.rs +++ b/rave/src/scan.rs @@ -19,7 +19,7 @@ use std::{ }; use time::OffsetDateTime; use tokio::{fs::File, sync::RwLock}; -use tracing::{debug, info, warn}; +use tracing::{debug, error, info, instrument}; mod walk; @@ -98,7 +98,10 @@ const VALID_EXTENSIONS: &[&str] = &["mp3"]; async fn do_entry(de: walk::DirEntry, txn: DatabaseTransaction, state: Arc>) { if let Err(e) = handle_entry(&txn, de, state).await { let _ = txn.rollback().await; - warn!("Failed to handle directory entry: {e}"); + error!( + error = e.root_cause(), + "Failed to handle directory entry: {e}" + ); { let mut write = STATUS.write().await; @@ -114,6 +117,7 @@ async fn do_entry(de: walk::DirEntry, txn: DatabaseTransaction, state: Arc, @@ -223,7 +228,7 @@ async fn find_album( let album = Album::find_by_id(current_album).one(tx).await?; let Some(album) = album else { - warn!("Couldn't find album with id {current_album}"); + error!("Couldn't find album with id {current_album}"); return Err(Report::msg(format!( "Couldn't find album with id {current_album}" @@ -259,7 +264,10 @@ async fn find_album( let Ok(model) = model else { let err = model.expect_err("somehow not err"); - warn!("Failed to insert album {err}"); + error!( + error = &err as &dyn std::error::Error, + "Failed to insert album {err}" + ); return Err(Report::new(err)); }; @@ -268,15 +276,19 @@ async fn find_album( } } +#[instrument(skip(tx))] async fn find_or_create_genre(tx: &DatabaseTransaction, name: &str) -> Result { + debug!("Finding genre with name {name}"); let res = Genre::find() .filter(genre::Column::Name.eq(name)) .one(tx) .await?; if let Some(genre) = res { + debug!("Found genre with id {}", genre.id); Ok(genre.id) } else { + debug!("Trying to create genre"); let am = genre::ActiveModel { name: Set(name.to_string()), ..Default::default() @@ -286,15 +298,21 @@ async fn find_or_create_genre(tx: &DatabaseTransaction, name: &str) -> Result Result, Report> { let artist_to_search = match (tag.album_artist(), tag.artists()) { (Some(tag_artist), None) => Some(tag_artist.to_string()), @@ -325,7 +343,10 @@ async fn find_artist(tx: &DatabaseTransaction, tag: &Tag) -> Result, } +#[instrument(skip(dbc, state))] async fn create_root_music_folder( dbc: &sea_orm::DatabaseConnection, root_dir: &Path, @@ -351,7 +373,10 @@ async fn create_root_music_folder( let txn = dbc.begin().await; let Ok(txn) = txn else { let err = txn.expect_err("somehow not err"); - warn!("Failed to start database transaction to add the root music folder {err}"); + error!( + error = &err as &dyn std::error::Error, + "Failed to start database transaction to add the root music folder {err}" + ); { let mut stat = STATUS.write().await; @@ -362,8 +387,22 @@ async fn create_root_music_folder( }; debug!("created transaction"); + let name = root_dir.to_string_lossy().to_string(); + + // Check for an existing one and exit early + let res = MusicFolder::find() + .filter(music_folder::Column::Name.eq(&name)) + .one(&txn) + .await; + + if let Ok(Some(res)) = res { + state.write().await.music_folder_id = res.id; + return ControlFlow::Continue(()); + } + + // Otherwise, make it let new_music_folder = music_folder::ActiveModel { - name: Set(root_dir.to_string_lossy().to_string()), + name: Set(name), ..Default::default() }; debug!("created new music folder model"); @@ -374,7 +413,10 @@ async fn create_root_music_folder( let Ok(mf) = mf else { let err = mf.expect_err("somehow not err"); - warn!("Failed to add the root music folder {err}"); + error!( + error = &err as &dyn std::error::Error, + "Failed to add the root music folder {err}" + ); { let mut stat = STATUS.write().await; @@ -396,7 +438,10 @@ async fn create_txn(dbc: &sea_orm::DatabaseConnection) -> Option txn, Err(e) => { - warn!("Failed to start database transaction: {e}"); + error!( + error = &e as &dyn std::error::Error, + "Failed to start database transaction: {e}" + ); { let mut write = STATUS.write().await; @@ -415,7 +460,10 @@ async fn check_dir_entry( let de = match res { Ok(de) => de, Err(e) => { - warn!("Failed to read directory entry: {e}"); + error!( + error = &e as &dyn std::error::Error, + "Failed to read directory entry: {e}" + ); { let mut write = STATUS.write().await; @@ -432,7 +480,10 @@ async fn get_dbc(conn: ConnectOptions) -> Option { let dbc = Database::connect(conn).await; let Ok(dbc) = dbc else { let e = dbc.expect_err("Failed to connect to database"); - warn!("Failed to connect to database: {e}"); + error!( + error = &e as &dyn std::error::Error, + "Failed to connect to database: {e}" + ); { let mut stat = STATUS.write().await; diff --git a/rave/src/utils.rs b/rave/src/utils.rs index 41c7572..92feced 100644 --- a/rave/src/utils.rs +++ b/rave/src/utils.rs @@ -34,7 +34,10 @@ pub async fn verify_user( None, ))), Err(e) => { - error!("Error getting user: {e}"); + error!( + error = &e as &dyn std::error::Error, + "Error getting user: {e}" + ); Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword( None, )))