feat: more kejiggering, added Sentry support

and tracing spans.
(personally I use glitchtip :3)
This commit is contained in:
Lys 2023-10-10 22:06:32 +03:00
parent 7e1f504368
commit 71b7eca0b9
Signed by: lyssieth
GPG key ID: C9CF3D614FAA3940
23 changed files with 721 additions and 78 deletions

6
.dockerignore Normal file
View file

@ -0,0 +1,6 @@
/.*
/target
/docker-compose.yml
/Justfile
/mounth.sh
*.md

2
.env.example Normal file
View file

@ -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

3
.gitignore vendored
View file

@ -1,5 +1,6 @@
/target /target
users.db* users.db*
.en* .env*
!.env.example
.rave-dev-db .rave-dev-db

507
Cargo.lock generated
View file

@ -599,6 +599,16 @@ dependencies = [
"version_check", "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]] [[package]]
name = "core-foundation-sys" name = "core-foundation-sys"
version = "0.8.4" version = "0.8.4"
@ -722,6 +732,16 @@ dependencies = [
"syn 2.0.38", "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]] [[package]]
name = "der" name = "der"
version = "0.7.8" version = "0.7.8"
@ -869,6 +889,18 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" 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]] [[package]]
name = "finl_unicode" name = "finl_unicode"
version = "1.2.0" version = "1.2.0"
@ -902,6 +934,21 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 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]] [[package]]
name = "form_urlencoded" name = "form_urlencoded"
version = "1.2.0" version = "1.2.0"
@ -1196,6 +1243,17 @@ dependencies = [
"windows-sys", "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]] [[package]]
name = "http" name = "http"
version = "0.2.9" version = "0.2.9"
@ -1254,6 +1312,33 @@ dependencies = [
"want", "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]] [[package]]
name = "iana-time-zone" name = "iana-time-zone"
version = "0.1.57" version = "0.1.57"
@ -1371,6 +1456,12 @@ dependencies = [
"windows-sys", "windows-sys",
] ]
[[package]]
name = "ipnet"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b29a3cd74f0f4598934efe3aeba42bae0eb4680554128851ebbecb02af14e6"
[[package]] [[package]]
name = "itertools" name = "itertools"
version = "0.11.0" version = "0.11.0"
@ -1467,6 +1558,12 @@ dependencies = [
"value-bag", "value-bag",
] ]
[[package]]
name = "match_cfg"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
[[package]] [[package]]
name = "matchers" name = "matchers"
version = "0.1.0" version = "0.1.0"
@ -1567,6 +1664,24 @@ dependencies = [
"version_check", "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]] [[package]]
name = "nom" name = "nom"
version = "7.1.3" version = "7.1.3"
@ -1678,6 +1793,50 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" 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]] [[package]]
name = "ordered-float" name = "ordered-float"
version = "3.9.1" version = "3.9.1"
@ -1687,6 +1846,17 @@ dependencies = [
"num-traits", "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]] [[package]]
name = "ouroboros" name = "ouroboros"
version = "0.17.2" version = "0.17.2"
@ -1773,6 +1943,26 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" 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]] [[package]]
name = "pin-project-lite" name = "pin-project-lite"
version = "0.2.13" version = "0.2.13"
@ -1863,6 +2053,7 @@ dependencies = [
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tokio-util", "tokio-util",
"tower",
"tracing", "tracing",
] ]
@ -2089,6 +2280,8 @@ dependencies = [
"poem-ext", "poem-ext",
"quick-xml", "quick-xml",
"sea-orm", "sea-orm",
"sentry",
"sentry-tracing",
"serde", "serde",
"serde_json", "serde_json",
"time", "time",
@ -2153,6 +2346,49 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" 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]] [[package]]
name = "rfc7239" name = "rfc7239"
version = "0.1.0" version = "0.1.0"
@ -2247,6 +2483,7 @@ version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8" checksum = "cd8d6c9f025a446bc4d18ad9632e69aec8f287aa84499ee335599fabd20c3fd8"
dependencies = [ dependencies = [
"log",
"ring", "ring",
"rustls-webpki", "rustls-webpki",
"sct", "sct",
@ -2277,6 +2514,15 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" 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]] [[package]]
name = "scopeguard" name = "scopeguard"
version = "1.2.0" version = "1.2.0"
@ -2437,12 +2683,157 @@ dependencies = [
"syn 1.0.109", "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]] [[package]]
name = "semver" name = "semver"
version = "1.0.19" version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ad977052201c6de01a8ef2aa3378c4bd23217a056337d1d6da40468d267a4fb0" 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]] [[package]]
name = "serde" name = "serde"
version = "1.0.188" version = "1.0.188"
@ -2674,7 +3065,7 @@ dependencies = [
"tokio-stream", "tokio-stream",
"tracing", "tracing",
"url", "url",
"webpki-roots", "webpki-roots 0.24.0",
] ]
[[package]] [[package]]
@ -2879,6 +3270,27 @@ dependencies = [
"unicode-ident", "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]] [[package]]
name = "tempfile" name = "tempfile"
version = "3.8.0" version = "3.8.0"
@ -2997,6 +3409,26 @@ dependencies = [
"syn 2.0.38", "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]] [[package]]
name = "tokio-shield" name = "tokio-shield"
version = "0.1.1" version = "0.1.1"
@ -3049,6 +3481,29 @@ dependencies = [
"winnow", "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]] [[package]]
name = "tower-service" name = "tower-service"
version = "0.3.2" version = "0.3.2"
@ -3166,6 +3621,15 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
[[package]]
name = "uname"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "uncased" name = "uncased"
version = "0.9.9" version = "0.9.9"
@ -3239,6 +3703,22 @@ version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" 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]] [[package]]
name = "url" name = "url"
version = "2.4.1" version = "2.4.1"
@ -3266,6 +3746,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
[[package]]
name = "uuid"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79daa5ed5740825c40b389c5e50312b9c86df53fccd33f281df655642b43869d"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "valuable" name = "valuable"
version = "0.1.0" version = "0.1.0"
@ -3396,6 +3885,12 @@ dependencies = [
"rustls-webpki", "rustls-webpki",
] ]
[[package]]
name = "webpki-roots"
version = "0.25.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14247bb57be4f377dfb94c72830b8ce8fc6beac03cf4bf7b9732eadd414123fc"
[[package]] [[package]]
name = "whoami" name = "whoami"
version = "1.4.1" version = "1.4.1"
@ -3508,6 +4003,16 @@ dependencies = [
"memchr", "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]] [[package]]
name = "zeroize" name = "zeroize"
version = "1.6.0" version = "1.6.0"

View file

@ -3,5 +3,12 @@ set dotenv-load := false
build: build:
docker buildx build --pull --platform linux/amd64 -t "forge.lys.ee/lyssieth/rave:amd64" --load . docker buildx build --pull --platform linux/amd64 -t "forge.lys.ee/lyssieth/rave:amd64" --load .
run: build mount:
docker run -it -v ./data:/storage lyssieth/rave:latest 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

View file

@ -18,7 +18,7 @@ pub struct Model {
pub starred: Option<TimeDateTimeWithTimeZone>, pub starred: Option<TimeDateTimeWithTimeZone>,
pub year: Option<i32>, pub year: Option<i32>,
pub genre_ids: Option<Vec<i64>>, pub genre_ids: Option<Vec<i64>>,
pub played: TimeDateTimeWithTimeZone, pub played: Option<TimeDateTimeWithTimeZone>,
pub user_rating: Option<i16>, pub user_rating: Option<i16>,
pub artist_ids: Option<Vec<i64>>, pub artist_ids: Option<Vec<i64>>,
pub original_release_date: Option<TimeDateTimeWithTimeZone>, pub original_release_date: Option<TimeDateTimeWithTimeZone>,

View file

@ -13,9 +13,6 @@ pub struct Model {
pub artist_image_url: Option<String>, pub artist_image_url: Option<String>,
pub album_count: i32, pub album_count: i32,
pub starred: bool, pub starred: bool,
pub music_brainz_id: String,
pub sort_name: String,
pub roles: String,
} }
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] #[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]

View file

@ -36,14 +36,6 @@ impl MigrationTrait for Migration {
.not_null() .not_null()
.default(false), .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(), .to_owned(),
) )
.await?; .await?;
@ -79,7 +71,4 @@ pub enum Artist {
ArtistImageUrl, ArtistImageUrl,
AlbumCount, AlbumCount,
Starred, Starred,
MusicBrainzId,
SortName,
Roles,
} }

View file

@ -53,7 +53,7 @@ impl MigrationTrait for Migration {
.col( .col(
ColumnDef::new(Album::Played) ColumnDef::new(Album::Played)
.timestamp_with_time_zone() .timestamp_with_time_zone()
.not_null(), .null(),
) )
.col(ColumnDef::new(Album::UserRating).tiny_integer().null()) .col(ColumnDef::new(Album::UserRating).tiny_integer().null())
.col( .col(

23
mount.sh Normal file
View file

@ -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

View file

@ -17,6 +17,7 @@ poem = { version = "1.3.58", features = [
"session", "session",
"static-files", "static-files",
"xml", "xml",
"tower-compat",
] } ] }
poem-ext = "0.9.4" poem-ext = "0.9.4"
quick-xml = { version = "0.30.0", features = ["serialize"] } 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" futures-lite = "1.13.0"
id3 = { version = "1.8.0", features = ["tokio"] } id3 = { version = "1.8.0", features = ["tokio"] }
tracing-appender = "0.2.2" 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"] }

View file

@ -8,13 +8,14 @@ use color_eyre::Result;
use migration::{Migrator, MigratorTrait}; use migration::{Migrator, MigratorTrait};
use poem::{ use poem::{
listener::TcpListener, listener::TcpListener,
middleware, middleware::{self, TowerLayerCompatExt},
web::{CompressionAlgo, CompressionLevel}, web::{CompressionAlgo, CompressionLevel},
Endpoint, EndpointExt, Route, Endpoint, EndpointExt, Request, Route,
}; };
use poem_ext::db::DbTransactionMiddleware; use poem_ext::db::DbTransactionMiddleware;
use sea_orm::{ConnectOptions, Database, DatabaseConnection}; 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_appender::non_blocking::WorkerGuard;
use tracing_subscriber::{ use tracing_subscriber::{
fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer, fmt, prelude::__tracing_subscriber_SubscriberExt, util::SubscriberInitExt, EnvFilter, Layer,
@ -36,6 +37,21 @@ async fn main() -> Result<()> {
color_eyre::install()?; color_eyre::install()?;
let _guards = install_tracing().await?; 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 route = create_route();
let dbc = create_pool().await?; let dbc = create_pool().await?;
@ -72,6 +88,7 @@ fn create_route() -> Box<dyn Endpoint<Output = poem::Response>> {
Route::new() Route::new()
.nest("/", ui::build()) .nest("/", ui::build())
.nest("/rest", rest::build()) .nest("/rest", rest::build())
.with(NewSentryLayer::<Request>::new_from_top().compat())
.with(middleware::CatchPanic::new()) .with(middleware::CatchPanic::new())
.with( .with(
middleware::Compression::new() middleware::Compression::new()
@ -117,6 +134,7 @@ async fn install_tracing() -> Result<[WorkerGuard; 1]> {
.with_writer(non_blocking) .with_writer(non_blocking)
.with_filter(filter), .with_filter(filter),
) )
.with(sentry_tracing::layer())
.try_init()?; .try_init()?;
Ok([guard]) Ok([guard])

View file

@ -1,7 +1,7 @@
use std::str::FromStr; use std::str::FromStr;
use serde::Deserialize; use serde::Deserialize;
use tracing::warn; use tracing::error;
use crate::subsonic::Error; use crate::subsonic::Error;
@ -45,7 +45,7 @@ impl FromStr for SortType {
"bygenre" => Ok(Self::ByGenre), "bygenre" => Ok(Self::ByGenre),
_ => { _ => {
warn!("got invalid type parameter {s}"); error!("got invalid type parameter {s}");
Err(Error::Generic(Some( Err(Error::Generic(Some(
"type parameter is invalid".to_string(), "type parameter is invalid".to_string(),
))) )))

View file

@ -9,9 +9,10 @@ use poem::web::{Data, Query};
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use sea_orm::{EntityTrait, ModelTrait}; use sea_orm::{EntityTrait, ModelTrait};
use serde::Deserialize; use serde::Deserialize;
use tracing::warn; use tracing::{error, instrument, warn};
#[poem::handler] #[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn get_album( pub async fn get_album(
Data(txn): Data<&DbTxn>, Data(txn): Data<&DbTxn>,
auth: Authentication, auth: Authentication,
@ -30,7 +31,10 @@ pub async fn get_album(
Ok(Some(_)) => unreachable!("Some(album) covered by `let .. else`"), Ok(Some(_)) => unreachable!("Some(album) covered by `let .. else`"),
Ok(None) => return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(None)), Ok(None) => return SubsonicResponse::new_error(Error::RequestedDataWasNotFound(None)),
Err(e) => { 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)); return SubsonicResponse::new_error(Error::Generic(None));
} }
} }
@ -41,12 +45,15 @@ pub async fn get_album(
let tracks = match tracks { let tracks = match tracks {
Ok(t) => t, Ok(t) => t,
Err(e) => { 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)); return SubsonicResponse::new_error(Error::Generic(None));
} }
}; };
todo!() todo!("finish implementing get_album")
// SubsonicResponse::new_album(album, tracks) // SubsonicResponse::new_album(album, tracks)
} }

View file

@ -11,7 +11,7 @@ use poem::{
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect}; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect};
use serde::Deserialize; use serde::Deserialize;
use tracing::debug; use tracing::instrument;
use crate::{ use crate::{
authentication::Authentication, authentication::Authentication,
@ -30,6 +30,7 @@ macro_rules! error_or {
} }
#[poem::handler] #[poem::handler]
#[instrument(skip(req, txn, auth))]
pub async fn get_album_list( pub async fn get_album_list(
req: &Request, req: &Request,
Data(txn): Data<&DbTxn>, Data(txn): Data<&DbTxn>,
@ -66,7 +67,6 @@ pub async fn get_album_list(
// match album_list { // match album_list {
// Ok(a) => { // Ok(a) => {
// debug!("uri path: {}", req.uri().path());
// if req.uri().path().contains("getAlbumList2") { // if req.uri().path().contains("getAlbumList2") {
// SubsonicResponse::new_album_list2(a) // SubsonicResponse::new_album_list2(a)
// } else { // } else {
@ -77,17 +77,17 @@ pub async fn get_album_list(
// } // }
} }
#[allow(unused_variables)] #[instrument(skip(_conn, _params))]
async fn get_album_list_random( async fn get_album_list_random(
conn: DbTxn, _conn: DbTxn,
params: GetAlbumListParams, _params: GetAlbumListParams,
) -> Result<Vec<album::Model>, Error> { ) -> Result<Vec<album::Model>, Error> {
Err(Error::Generic(Some( Err(Error::Generic(Some(
"Sorting by random not implemented".to_string(), "Sorting by random not implemented".to_string(),
))) )))
} }
#[allow(unused_variables)] #[instrument(skip(conn))]
async fn get_album_list_newest( async fn get_album_list_newest(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, params: GetAlbumListParams,
@ -102,17 +102,22 @@ async fn get_album_list_newest(
error_or!(albums) error_or!(albums)
} }
#[allow(unused_variables)] #[instrument(skip(conn))]
async fn get_album_list_highest( async fn get_album_list_highest(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, params: GetAlbumListParams,
) -> Result<Vec<album::Model>, Error> { ) -> Result<Vec<album::Model>, Error> {
Err(Error::Generic(Some( let albums = Album::find()
"Sorting by highest rating not implemented".to_string(), .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( async fn get_album_list_frequent(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, params: GetAlbumListParams,
@ -127,17 +132,22 @@ async fn get_album_list_frequent(
error_or!(albums) error_or!(albums)
} }
#[allow(unused_variables)] #[instrument(skip(conn))]
async fn get_album_list_recent( async fn get_album_list_recent(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, params: GetAlbumListParams,
) -> Result<Vec<album::Model>, Error> { ) -> Result<Vec<album::Model>, Error> {
Err(Error::Generic(Some( let albums = Album::find()
"Sorting by recently played not implemented".to_string(), .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( async fn get_album_list_alphabetical_by_name(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, params: GetAlbumListParams,
@ -152,7 +162,7 @@ async fn get_album_list_alphabetical_by_name(
error_or!(albums) error_or!(albums)
} }
#[allow(unused_variables)] #[instrument(skip(conn))]
async fn get_album_list_alphabetical_by_artist( async fn get_album_list_alphabetical_by_artist(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, params: GetAlbumListParams,
@ -171,7 +181,7 @@ async fn get_album_list_alphabetical_by_artist(
error_or!(albums) error_or!(albums)
} }
#[allow(unused_variables)] #[instrument(skip(conn))]
async fn get_album_list_starred( async fn get_album_list_starred(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, params: GetAlbumListParams,
@ -186,6 +196,7 @@ async fn get_album_list_starred(
error_or!(albums) error_or!(albums)
} }
#[instrument(skip(conn))]
async fn get_album_list_by_year( async fn get_album_list_by_year(
conn: DbTxn, conn: DbTxn,
params: GetAlbumListParams, 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)) .filter(genre::Column::Name.eq(genre))
.one(&*conn) .one(&*conn)
.await; .await;
let genre = match genre_id { let Ok(Some(genre)) = genre else {
Ok(Some(g)) => g, return Err(Error::RequestedDataWasNotFound(Some(
Ok(None) | Err(_) => return Err(Error::Generic(Some("Genre not found".to_string()))), "Genre not found".to_string(),
)));
}; };
let albums = Album::find() let albums = Album::find()
@ -278,7 +290,6 @@ pub struct GetAlbumListParams {
} }
impl GetAlbumListParams { impl GetAlbumListParams {
#[allow(clippy::result_large_err)]
pub fn verify(self) -> Result<Self, SubsonicResponse> { pub fn verify(self) -> Result<Self, SubsonicResponse> {
if self.r#type == SortType::ByYear { if self.r#type == SortType::ByYear {
if self.from_year.is_none() || self.to_year.is_none() { if self.from_year.is_none() || self.to_year.is_none() {

View file

@ -1,5 +1,6 @@
use poem::web::Data; use poem::web::Data;
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use tracing::instrument;
use crate::{ use crate::{
authentication::Authentication, authentication::Authentication,
@ -8,6 +9,7 @@ use crate::{
}; };
#[poem::handler] #[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn get_license(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { pub async fn get_license(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse {
let u = utils::verify_user(txn.clone(), auth).await; let u = utils::verify_user(txn.clone(), auth).await;

View file

@ -2,6 +2,7 @@ use entities::prelude::MusicFolder;
use poem::web::Data; use poem::web::Data;
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use sea_orm::EntityTrait; use sea_orm::EntityTrait;
use tracing::instrument;
use crate::{ use crate::{
authentication::Authentication, authentication::Authentication,
@ -10,6 +11,7 @@ use crate::{
}; };
#[poem::handler] #[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn get_music_folders(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { pub async fn get_music_folders(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse {
let u = utils::verify_user(txn.clone(), auth).await; let u = utils::verify_user(txn.clone(), auth).await;

View file

@ -1,6 +1,6 @@
use poem::web::Data; use poem::web::Data;
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use tracing::warn; use tracing::{error, instrument};
use crate::{ use crate::{
authentication::Authentication, authentication::Authentication,
@ -10,6 +10,7 @@ use crate::{
}; };
#[poem::handler] #[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn get_scan_status(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { pub async fn get_scan_status(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse {
let u = utils::verify_user(txn.clone(), auth).await; 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 { match status {
Ok(status) => SubsonicResponse::new_scan_status(status.scanning, status.count), Ok(status) => SubsonicResponse::new_scan_status(status.scanning, status.count),
Err(e) => { Err(e) => {
warn!("Error getting scan status: {}", e); error!(error = e.root_cause(), "Error getting scan status: {e}");
SubsonicResponse::new_error(Error::Generic(None)) SubsonicResponse::new_error(Error::Generic(None))
} }
} }

View file

@ -1,5 +1,6 @@
use poem::web::Data; use poem::web::Data;
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use tracing::instrument;
use crate::{ use crate::{
authentication::Authentication, authentication::Authentication,
@ -8,6 +9,7 @@ use crate::{
}; };
#[poem::handler] #[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn ping(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { pub async fn ping(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse {
let u = utils::verify_user(txn.clone(), auth).await; let u = utils::verify_user(txn.clone(), auth).await;

View file

@ -1,6 +1,6 @@
use poem::web::Data; use poem::web::Data;
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use tracing::warn; use tracing::{error, instrument};
use crate::{ use crate::{
authentication::Authentication, authentication::Authentication,
@ -9,6 +9,7 @@ use crate::{
}; };
#[poem::handler] #[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn start_scan(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse { pub async fn start_scan(Data(txn): Data<&DbTxn>, auth: Authentication) -> SubsonicResponse {
let u = utils::verify_user(txn.clone(), auth).await; 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() { if status.errors.is_empty() {
SubsonicResponse::new_scan_status(status.scanning, status.count) SubsonicResponse::new_scan_status(status.scanning, status.count)
} else { } else {
warn!("Failed to start scan:"); error!("Failed to start scan:");
for e in status.errors { for e in status.errors {
warn!("{e}"); error!(error = e.root_cause(), "{e}");
} }
SubsonicResponse::new_error(Error::Generic(None)) SubsonicResponse::new_error(Error::Generic(None))
} }
} }
Err(e) => { Err(e) => {
warn!("Failed to start scan: {e}"); error!(error = e.root_cause(), "Failed to start scan: {e}");
SubsonicResponse::new_error(Error::Generic(None)) SubsonicResponse::new_error(Error::Generic(None))
} }
} }

View file

@ -5,6 +5,7 @@ use poem::{
}; };
use poem_ext::db::DbTxn; use poem_ext::db::DbTxn;
use serde::Deserialize; use serde::Deserialize;
use tracing::instrument;
use crate::{ use crate::{
authentication::Authentication, authentication::Authentication,
@ -14,6 +15,7 @@ use crate::{
const SONG: &[u8] = include_bytes!("../../../../data.mp3"); const SONG: &[u8] = include_bytes!("../../../../data.mp3");
#[poem::handler] #[poem::handler]
#[instrument(skip(txn, auth))]
pub async fn stream( pub async fn stream(
Data(txn): Data<&DbTxn>, Data(txn): Data<&DbTxn>,
auth: Authentication, auth: Authentication,

View file

@ -19,7 +19,7 @@ use std::{
}; };
use time::OffsetDateTime; use time::OffsetDateTime;
use tokio::{fs::File, sync::RwLock}; use tokio::{fs::File, sync::RwLock};
use tracing::{debug, info, warn}; use tracing::{debug, error, info, instrument};
mod walk; mod walk;
@ -98,7 +98,10 @@ const VALID_EXTENSIONS: &[&str] = &["mp3"];
async fn do_entry(de: walk::DirEntry, txn: DatabaseTransaction, state: Arc<RwLock<ScanState>>) { async fn do_entry(de: walk::DirEntry, txn: DatabaseTransaction, state: Arc<RwLock<ScanState>>) {
if let Err(e) = handle_entry(&txn, de, state).await { if let Err(e) = handle_entry(&txn, de, state).await {
let _ = txn.rollback().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; let mut write = STATUS.write().await;
@ -114,6 +117,7 @@ async fn do_entry(de: walk::DirEntry, txn: DatabaseTransaction, state: Arc<RwLoc
} }
} }
#[instrument(skip(tx, state))]
async fn handle_entry( async fn handle_entry(
tx: &DatabaseTransaction, tx: &DatabaseTransaction,
entry: walk::DirEntry, entry: walk::DirEntry,
@ -129,7 +133,7 @@ async fn handle_entry(
let file_ext = path.extension(); let file_ext = path.extension();
let Some(ext) = file_ext else { let Some(ext) = file_ext else {
warn!("Couldn't get file extension for {path:?}"); error!("Couldn't get file extension for {path:?}");
{ {
STATUS STATUS
@ -145,10 +149,15 @@ async fn handle_entry(
let ext = ext.to_string_lossy(); let ext = ext.to_string_lossy();
if !VALID_EXTENSIONS.contains(&ext.as_ref()) {
debug!("Skipping file with invalid extension: {path:?}");
return Ok(());
}
let file_stem = path.file_stem(); let file_stem = path.file_stem();
let Some(stem) = file_stem else { let Some(stem) = file_stem else {
warn!("Couldn't get file stem for {path:?}"); error!("Couldn't get file stem for {path:?}");
{ {
STATUS STATUS
@ -164,11 +173,6 @@ async fn handle_entry(
let stem = stem.to_string_lossy(); let stem = stem.to_string_lossy();
if !VALID_EXTENSIONS.contains(&ext.as_ref()) {
debug!("Skipping file with invalid extension: {path:?}");
return Ok(());
}
let meta = { File::open(&path).await?.metadata().await? }; let meta = { File::open(&path).await?.metadata().await? };
let current_album = { state.read().await.album_id }; let current_album = { state.read().await.album_id };
@ -212,6 +216,7 @@ async fn handle_entry(
Ok(()) Ok(())
} }
#[instrument(skip(tx, tag, state))]
async fn find_album( async fn find_album(
tx: &DatabaseTransaction, tx: &DatabaseTransaction,
current_album: Option<i64>, current_album: Option<i64>,
@ -223,7 +228,7 @@ async fn find_album(
let album = Album::find_by_id(current_album).one(tx).await?; let album = Album::find_by_id(current_album).one(tx).await?;
let Some(album) = album else { 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!( return Err(Report::msg(format!(
"Couldn't find album with id {current_album}" "Couldn't find album with id {current_album}"
@ -259,7 +264,10 @@ async fn find_album(
let Ok(model) = model else { let Ok(model) = model else {
let err = model.expect_err("somehow not err"); 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)); 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<i64, Report> { async fn find_or_create_genre(tx: &DatabaseTransaction, name: &str) -> Result<i64, Report> {
debug!("Finding genre with name {name}");
let res = Genre::find() let res = Genre::find()
.filter(genre::Column::Name.eq(name)) .filter(genre::Column::Name.eq(name))
.one(tx) .one(tx)
.await?; .await?;
if let Some(genre) = res { if let Some(genre) = res {
debug!("Found genre with id {}", genre.id);
Ok(genre.id) Ok(genre.id)
} else { } else {
debug!("Trying to create genre");
let am = genre::ActiveModel { let am = genre::ActiveModel {
name: Set(name.to_string()), name: Set(name.to_string()),
..Default::default() ..Default::default()
@ -286,15 +298,21 @@ async fn find_or_create_genre(tx: &DatabaseTransaction, name: &str) -> Result<i6
let Ok(model) = model else { let Ok(model) = model else {
let err = model.expect_err("somehow not err"); let err = model.expect_err("somehow not err");
warn!("Failed to insert genre {err}"); error!(
error = &err as &dyn std::error::Error,
"Failed to insert genre {err}"
);
return Err(Report::new(err)); return Err(Report::new(err));
}; };
debug!("Inserted genre with id {}", model.id);
Ok(model.id) Ok(model.id)
} }
} }
#[instrument(skip(tx, tag))]
async fn find_artist(tx: &DatabaseTransaction, tag: &Tag) -> Result<Option<artist::Model>, Report> { async fn find_artist(tx: &DatabaseTransaction, tag: &Tag) -> Result<Option<artist::Model>, Report> {
let artist_to_search = match (tag.album_artist(), tag.artists()) { let artist_to_search = match (tag.album_artist(), tag.artists()) {
(Some(tag_artist), None) => Some(tag_artist.to_string()), (Some(tag_artist), None) => Some(tag_artist.to_string()),
@ -325,7 +343,10 @@ async fn find_artist(tx: &DatabaseTransaction, tag: &Tag) -> Result<Option<artis
let Ok(model) = model else { let Ok(model) = model else {
let err = model.expect_err("somehow not err"); let err = model.expect_err("somehow not err");
warn!("Failed to insert artist {err}"); error!(
error = &err as &dyn std::error::Error,
"Failed to insert artist {err}"
);
return Err(Report::new(err)); return Err(Report::new(err));
}; };
@ -343,6 +364,7 @@ struct ScanState {
pub album_id: Option<i64>, pub album_id: Option<i64>,
} }
#[instrument(skip(dbc, state))]
async fn create_root_music_folder( async fn create_root_music_folder(
dbc: &sea_orm::DatabaseConnection, dbc: &sea_orm::DatabaseConnection,
root_dir: &Path, root_dir: &Path,
@ -351,7 +373,10 @@ async fn create_root_music_folder(
let txn = dbc.begin().await; let txn = dbc.begin().await;
let Ok(txn) = txn else { let Ok(txn) = txn else {
let err = txn.expect_err("somehow not err"); 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; let mut stat = STATUS.write().await;
@ -362,8 +387,22 @@ async fn create_root_music_folder(
}; };
debug!("created transaction"); 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 { let new_music_folder = music_folder::ActiveModel {
name: Set(root_dir.to_string_lossy().to_string()), name: Set(name),
..Default::default() ..Default::default()
}; };
debug!("created new music folder model"); debug!("created new music folder model");
@ -374,7 +413,10 @@ async fn create_root_music_folder(
let Ok(mf) = mf else { let Ok(mf) = mf else {
let err = mf.expect_err("somehow not err"); 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; let mut stat = STATUS.write().await;
@ -396,7 +438,10 @@ async fn create_txn(dbc: &sea_orm::DatabaseConnection) -> Option<DatabaseTransac
let txn = match dbc.begin().await { let txn = match dbc.begin().await {
Ok(txn) => txn, Ok(txn) => txn,
Err(e) => { 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; let mut write = STATUS.write().await;
@ -415,7 +460,10 @@ async fn check_dir_entry(
let de = match res { let de = match res {
Ok(de) => de, Ok(de) => de,
Err(e) => { 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; let mut write = STATUS.write().await;
@ -432,7 +480,10 @@ async fn get_dbc(conn: ConnectOptions) -> Option<sea_orm::DatabaseConnection> {
let dbc = Database::connect(conn).await; let dbc = Database::connect(conn).await;
let Ok(dbc) = dbc else { let Ok(dbc) = dbc else {
let e = dbc.expect_err("Failed to connect to database"); 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; let mut stat = STATUS.write().await;

View file

@ -34,7 +34,10 @@ pub async fn verify_user(
None, None,
))), ))),
Err(e) => { 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( Err(SubsonicResponse::new_error(Error::WrongUsernameOrPassword(
None, None,
))) )))