Compare commits
No commits in common. "fa8ed2b5992396a92278775f75bc3c303b490850" and "52ef924f12e67a1a4388299f218a1bc8d65fb192" have entirely different histories.
fa8ed2b599
...
52ef924f12
33 changed files with 301 additions and 1270 deletions
541
Cargo.lock
generated
541
Cargo.lock
generated
|
@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -179,7 +179,7 @@ dependencies = [
|
|||
"actix-router",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -251,13 +251,13 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.77"
|
||||
version = "0.1.76"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
|
||||
checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -437,16 +437,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "core-foundation-sys"
|
||||
version = "0.8.6"
|
||||
|
@ -455,9 +445,9 @@ checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504"
|
||||
checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -527,7 +517,7 @@ dependencies = [
|
|||
"diesel_table_macro_syntax",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -547,7 +537,7 @@ version = "0.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
|
||||
dependencies = [
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -576,41 +566,12 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_logger"
|
||||
version = "0.10.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece"
|
||||
dependencies = [
|
||||
"humantime",
|
||||
"is-terminal",
|
||||
"log",
|
||||
"regex",
|
||||
"termcolor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.28"
|
||||
|
@ -627,21 +588,6 @@ version = "1.0.7"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||
dependencies = [
|
||||
"foreign-types-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||
|
||||
[[package]]
|
||||
name = "form_urlencoded"
|
||||
version = "1.2.1"
|
||||
|
@ -651,27 +597,12 @@ dependencies = [
|
|||
"percent-encoding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.30"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.30"
|
||||
|
@ -691,12 +622,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -753,12 +681,6 @@ version = "0.14.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||
|
||||
[[package]]
|
||||
name = "hmac"
|
||||
version = "0.12.1"
|
||||
|
@ -779,17 +701,6 @@ dependencies = [
|
|||
"itoa",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "http-body"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"http",
|
||||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "httparse"
|
||||
version = "1.8.0"
|
||||
|
@ -802,49 +713,6 @@ version = "1.0.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
||||
|
||||
[[package]]
|
||||
name = "humantime"
|
||||
version = "2.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"httparse",
|
||||
"httpdate",
|
||||
"itoa",
|
||||
"pin-project-lite",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper-tls"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"hyper",
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "iana-time-zone"
|
||||
version = "0.1.59"
|
||||
|
@ -897,23 +765,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipnet"
|
||||
version = "2.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itoa"
|
||||
version = "1.0.10"
|
||||
|
@ -967,15 +818,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.152"
|
||||
version = "0.2.151"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456"
|
||||
checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4"
|
||||
|
||||
[[package]]
|
||||
name = "local-channel"
|
||||
|
@ -1061,25 +906,7 @@ dependencies = [
|
|||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[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",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1112,16 +939,6 @@ dependencies = [
|
|||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.2"
|
||||
|
@ -1137,50 +954,6 @@ version = "1.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.62"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"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.48",
|
||||
]
|
||||
|
||||
[[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.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
|
@ -1267,18 +1040,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.76"
|
||||
version = "1.0.73"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c"
|
||||
checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.35"
|
||||
version = "1.0.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
|
||||
checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1362,44 +1135,6 @@ version = "0.8.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
|
||||
|
||||
[[package]]
|
||||
name = "reqwest"
|
||||
version = "0.11.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
|
||||
dependencies = [
|
||||
"base64",
|
||||
"bytes",
|
||||
"encoding_rs",
|
||||
"futures-core",
|
||||
"futures-util",
|
||||
"h2",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper-tls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"log",
|
||||
"mime",
|
||||
"native-tls",
|
||||
"once_cell",
|
||||
"percent-encoding",
|
||||
"pin-project-lite",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serde_urlencoded",
|
||||
"system-configuration",
|
||||
"tokio",
|
||||
"tokio-native-tls",
|
||||
"tower-service",
|
||||
"url",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
"winreg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.17.7"
|
||||
|
@ -1411,7 +1146,7 @@ dependencies = [
|
|||
"libc",
|
||||
"spin",
|
||||
"untrusted",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1429,34 +1164,12 @@ dependencies = [
|
|||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.38.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c"
|
||||
|
||||
[[package]]
|
||||
name = "schannel"
|
||||
version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scheduled-thread-pool"
|
||||
version = "0.2.7"
|
||||
|
@ -1472,60 +1185,37 @@ version = "1.2.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[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.21"
|
||||
version = "1.0.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97ed7a9823b74f99c7742f5336af7be5ecd3eeafcb1507d1fa93347b1d589b0"
|
||||
checksum = "836fa6a3e1e547f9a2c4040802ec865b5d85f4014efe00555d7090a3dcaa1090"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.195"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02"
|
||||
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.195"
|
||||
version = "1.0.193"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c"
|
||||
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.111"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
|
||||
checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"ryu",
|
||||
|
@ -1618,7 +1308,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1646,76 +1336,33 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.48"
|
||||
version = "2.0.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
|
||||
checksum = "92d27c2c202598d05175a6dd3af46824b7f747f8d8e9b14c623f19fa5069735d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"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.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"redox_syscall",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "termcolor"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff1bc3d3f05aff0403e8ac0d92ced918ec05b666a43f83297ccef5bea8a3d449"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.56"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
|
||||
checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.56"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
|
||||
checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1772,22 +1419,11 @@ dependencies = [
|
|||
"bytes",
|
||||
"libc",
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"parking_lot",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"socket2",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[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",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1838,12 +1474,6 @@ dependencies = [
|
|||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower-service"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52"
|
||||
|
||||
[[package]]
|
||||
name = "tracing"
|
||||
version = "0.1.40"
|
||||
|
@ -1864,12 +1494,6 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
|
@ -1931,7 +1555,6 @@ name = "vybr-api"
|
|||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"actix-rt",
|
||||
"actix-service",
|
||||
"actix-web",
|
||||
"async-trait",
|
||||
"bcrypt",
|
||||
|
@ -1939,26 +1562,14 @@ dependencies = [
|
|||
"diesel",
|
||||
"diesel_migrations",
|
||||
"dotenvy",
|
||||
"env_logger",
|
||||
"hmac",
|
||||
"jsonwebtoken",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "want"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e"
|
||||
dependencies = [
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
|
@ -1986,22 +1597,10 @@ dependencies = [
|
|||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-futures"
|
||||
version = "0.4.39"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac36a15a220124ac510204aec1c3e5db8a22ab06fd6706d881dc6149f8ed9a12"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
"web-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.89"
|
||||
|
@ -2020,7 +1619,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
@ -2031,47 +1630,6 @@ version = "0.2.89"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.66"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50c24a44ec86bb68fbecd1b3efed7e85ea5621b39b35ef2766b66cd984f8010f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "windows-core"
|
||||
version = "0.52.0"
|
||||
|
@ -2090,15 +1648,6 @@ dependencies = [
|
|||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
|
||||
dependencies = [
|
||||
"windows-targets 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
|
@ -2215,23 +1764,13 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
|
|||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.5.33"
|
||||
version = "0.5.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7520bbdec7211caa7c4e682eb1fbe07abe20cee6756b6e00f537c82c11816aa"
|
||||
checksum = "97a4882e6b134d6c28953a387571f1acdd3496830d5e36c5e3a1075580ea641c"
|
||||
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 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.32"
|
||||
|
@ -2249,7 +1788,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.48",
|
||||
"syn 2.0.44",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
@ -14,13 +14,9 @@ chrono = { version = "0.4", features = ["serde"] }
|
|||
diesel = { version = "2", features = ["r2d2", "postgres", "chrono"] }
|
||||
diesel_migrations = "2"
|
||||
dotenvy = "*"
|
||||
env_logger = "0.10"
|
||||
hmac = "0.12"
|
||||
log = "0.4"
|
||||
reqwest = { version = "0.11", features = ["blocking", "json"] }
|
||||
sha2 = "0.10"
|
||||
lazy_static = "1"
|
||||
jsonwebtoken = "9"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
||||
actix-service = "2.0.2"
|
||||
|
|
|
@ -34,99 +34,3 @@ BEGIN
|
|||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- nanoid function
|
||||
CREATE EXTENSION IF NOT EXISTS pgcrypto;
|
||||
|
||||
-- The `nanoid()` function generates a compact, URL-friendly unique identifier.
|
||||
-- Based on the given size and alphabet, it creates a randomized string that's ideal for
|
||||
-- use-cases requiring small, unpredictable IDs (e.g., URL shorteners, generated file names, etc.).
|
||||
-- While it comes with a default configuration, the function is designed to be flexible,
|
||||
-- allowing for customization to meet specific needs.
|
||||
DROP FUNCTION IF EXISTS nanoid(int, text, float);
|
||||
CREATE OR REPLACE FUNCTION nanoid(
|
||||
size int DEFAULT 24, -- The number of symbols in the NanoId String. Must be greater than 0.
|
||||
alphabet text DEFAULT '_-0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', -- The symbols used in the NanoId String. Must contain between 1 and 255 symbols.
|
||||
additionalBytesFactor float DEFAULT 1.6 -- The additional bytes factor used for calculating the step size. Must be equal or greater then 1.
|
||||
)
|
||||
RETURNS text -- A randomly generated NanoId String
|
||||
LANGUAGE plpgsql
|
||||
VOLATILE
|
||||
LEAKPROOF
|
||||
PARALLEL SAFE
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
alphabetArray text[];
|
||||
alphabetLength int := 64;
|
||||
mask int := 63;
|
||||
step int := 34;
|
||||
BEGIN
|
||||
IF size IS NULL OR size < 1 THEN
|
||||
RAISE EXCEPTION 'The size must be defined and greater than 0!';
|
||||
END IF;
|
||||
|
||||
IF alphabet IS NULL OR length(alphabet) = 0 OR length(alphabet) > 255 THEN
|
||||
RAISE EXCEPTION 'The alphabet can''t be undefined, zero or bigger than 255 symbols!';
|
||||
END IF;
|
||||
|
||||
IF additionalBytesFactor IS NULL OR additionalBytesFactor < 1 THEN
|
||||
RAISE EXCEPTION 'The additional bytes factor can''t be less than 1!';
|
||||
END IF;
|
||||
|
||||
alphabetArray := regexp_split_to_array(alphabet, '');
|
||||
alphabetLength := array_length(alphabetArray, 1);
|
||||
mask := (2 << cast(floor(log(alphabetLength - 1) / log(2)) as int)) - 1;
|
||||
step := cast(ceil(additionalBytesFactor * mask * size / alphabetLength) AS int);
|
||||
|
||||
IF step > 1024 THEN
|
||||
step := 1024; -- The step size % can''t be bigger then 1024!
|
||||
END IF;
|
||||
|
||||
RETURN nanoid_optimized(size, alphabet, mask, step);
|
||||
END
|
||||
$$;
|
||||
|
||||
-- Generates an optimized random string of a specified size using the given alphabet, mask, and step.
|
||||
-- This optimized version is designed for higher performance and lower memory overhead.
|
||||
-- No checks are performed! Use it only if you really know what you are doing.
|
||||
DROP FUNCTION IF EXISTS nanoid_optimized(int, text, int, int);
|
||||
CREATE OR REPLACE FUNCTION nanoid_optimized(
|
||||
size int, -- The desired length of the generated string.
|
||||
alphabet text, -- The set of characters to choose from for generating the string.
|
||||
mask int, -- The mask used for mapping random bytes to alphabet indices. Should be `(2^n) - 1` where `n` is a power of 2 less than or equal to the alphabet size.
|
||||
step int -- The number of random bytes to generate in each iteration. A larger value may speed up the function but increase memory usage.
|
||||
)
|
||||
RETURNS text -- A randomly generated NanoId String
|
||||
LANGUAGE plpgsql
|
||||
VOLATILE
|
||||
LEAKPROOF
|
||||
PARALLEL SAFE
|
||||
AS
|
||||
$$
|
||||
DECLARE
|
||||
idBuilder text := '';
|
||||
counter int := 0;
|
||||
bytes bytea;
|
||||
alphabetIndex int;
|
||||
alphabetArray text[];
|
||||
alphabetLength int := 64;
|
||||
BEGIN
|
||||
alphabetArray := regexp_split_to_array(alphabet, '');
|
||||
alphabetLength := array_length(alphabetArray, 1);
|
||||
|
||||
LOOP
|
||||
bytes := gen_random_bytes(step);
|
||||
FOR counter IN 0..step - 1
|
||||
LOOP
|
||||
alphabetIndex := (get_byte(bytes, counter) & mask) + 1;
|
||||
IF alphabetIndex <= alphabetLength THEN
|
||||
idBuilder := idBuilder || alphabetArray[alphabetIndex];
|
||||
IF length(idBuilder) = size THEN
|
||||
RETURN idBuilder;
|
||||
END IF;
|
||||
END IF;
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
END
|
||||
$$;
|
||||
|
|
|
@ -7,7 +7,7 @@ create table if not exists users
|
|||
password text not null,
|
||||
|
||||
updated_at timestamp,
|
||||
created_at timestamp default now(),
|
||||
created_at timestamp default now() not null,
|
||||
|
||||
primary key (id)
|
||||
);
|
||||
|
|
|
@ -1,16 +1,12 @@
|
|||
create type playlist_type as enum ('playlist', 'folder');
|
||||
|
||||
create table if not exists playlists
|
||||
(
|
||||
id varchar(24) default nanoid(24),
|
||||
name varchar(255) not null,
|
||||
public bool default false not null,
|
||||
type playlist_type default 'playlist' not null,
|
||||
public bool default false,
|
||||
|
||||
creator_id varchar(24) not null,
|
||||
parent_id varchar(24) references playlists (id) on delete set null,
|
||||
|
||||
created_at timestamp default now(),
|
||||
created_at timestamp default now() not null,
|
||||
updated_at timestamp,
|
||||
|
||||
primary key (id),
|
||||
|
|
|
@ -5,11 +5,11 @@ create table if not exists tracks
|
|||
title varchar(255) not null,
|
||||
duration_ms int not null default 0,
|
||||
|
||||
created_at timestamp default now(),
|
||||
created_at timestamp default now() not null,
|
||||
updated_at timestamp,
|
||||
|
||||
-- music services
|
||||
spotify_id varchar(22) unique,
|
||||
spotify_id varchar(21) unique,
|
||||
tidal_id varchar(10) unique,
|
||||
|
||||
primary key (id)
|
||||
|
|
|
@ -2,6 +2,5 @@ create table if not exists playlists_tracks
|
|||
(
|
||||
playlist_id varchar(24) references playlists (id) on delete cascade,
|
||||
track_id varchar(24) references tracks (id) on delete cascade,
|
||||
added_at timestamp default now() not null,
|
||||
primary key (playlist_id, track_id)
|
||||
);
|
||||
|
|
|
@ -3,14 +3,13 @@ create table if not exists artists
|
|||
(
|
||||
id varchar(24) default nanoid(24),
|
||||
name varchar(255) NOT NULL,
|
||||
slug varchar unique not null generated always as (lower(replace(name, ' ', '-'))) stored,
|
||||
|
||||
created_at timestamp default now(),
|
||||
updated_at timestamp,
|
||||
created_at TIMESTAMP DEFAULT now() NOT NULL,
|
||||
updated_at TIMESTAMP,
|
||||
|
||||
-- music services
|
||||
spotify_id varchar(22) unique,
|
||||
tidal_id varchar(10) unique,
|
||||
spotify_id VARCHAR(21) UNIQUE,
|
||||
tidal_id VARCHAR(10) UNIQUE,
|
||||
|
||||
primary key (id)
|
||||
);
|
||||
|
|
|
@ -16,7 +16,6 @@ lazy_static! {
|
|||
}
|
||||
|
||||
pub fn init() {
|
||||
log::info!("Initializing db pool.");
|
||||
lazy_static::initialize(&POOL);
|
||||
let _conn = connection().expect("Failed to get db connection.");
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ use jsonwebtoken::{decode, DecodingKey, EncodingKey, TokenData, Validation};
|
|||
const JWT_SECRET: &str = "secret";
|
||||
|
||||
pub fn get_token(token: &str) -> Result<TokenData<JWTClaims>, Error> {
|
||||
let token = decode::<JWTClaims>(token, &get_decoding_key(), &Validation::default())?;
|
||||
let token = decode::<JWTClaims>(&token, &get_decoding_key(), &Validation::default())?;
|
||||
Ok(token)
|
||||
}
|
||||
|
||||
|
|
17
src/main.rs
17
src/main.rs
|
@ -5,32 +5,23 @@ mod middlewares;
|
|||
mod models;
|
||||
mod routes;
|
||||
mod schema;
|
||||
mod services;
|
||||
mod utils;
|
||||
|
||||
use crate::helpers::db;
|
||||
use actix_web::{web, App, HttpServer};
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
use actix_web::{App, HttpServer};
|
||||
|
||||
dotenvy::dotenv().expect("No .env file found");
|
||||
env_logger::init();
|
||||
|
||||
// Register services.
|
||||
let _ = services::spotify::instance().await;
|
||||
std::env::set_var("RUST_LOG", "debug");
|
||||
|
||||
db::init();
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.service(routes::auth::routes())
|
||||
// .wrap(middlewares::auth::auth())
|
||||
.service(routes::playlists::routes())
|
||||
.service(web::scope("/playlists").service(routes::playlists::get_playlist))
|
||||
.service(routes::auth::login)
|
||||
.service(routes::me::routes())
|
||||
.service(routes::import::routes())
|
||||
.service(routes::users::routes())
|
||||
.service(routes::search::routes())
|
||||
})
|
||||
.bind(("127.0.0.1", 9000))?
|
||||
.run()
|
||||
|
|
|
@ -9,15 +9,6 @@ pub struct ErrorResponse {
|
|||
pub status: StatusCode,
|
||||
}
|
||||
|
||||
impl ErrorResponse {
|
||||
pub fn new(message: &str, status: StatusCode) -> Self {
|
||||
ErrorResponse {
|
||||
message: message.to_string(),
|
||||
status,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ErrorResponse {
|
||||
fn fmt(&self, f: &mut Formatter) -> Result {
|
||||
write!(f, "{}: {}", self.status, self.message)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::helpers::jwt::get_token;
|
||||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::models::users::Users;
|
||||
use crate::models::user::Users;
|
||||
use actix_web::http::{header, StatusCode};
|
||||
use actix_web::HttpRequest;
|
||||
|
||||
|
@ -19,29 +19,23 @@ pub fn get_user(req: HttpRequest) -> Result<Users, ErrorResponse> {
|
|||
}
|
||||
Err(e) => {
|
||||
return Err(match e.kind() {
|
||||
jsonwebtoken::errors::ErrorKind::ExpiredSignature => {
|
||||
ErrorResponse::new("Not Authorized", StatusCode::UNAUTHORIZED)
|
||||
}
|
||||
_ => ErrorResponse::new(
|
||||
e.to_string().as_str(),
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
),
|
||||
jsonwebtoken::errors::ErrorKind::ExpiredSignature => ErrorResponse {
|
||||
message: "Not Authorized".to_string(),
|
||||
status: StatusCode::UNAUTHORIZED,
|
||||
},
|
||||
_ => ErrorResponse {
|
||||
message: e.to_string(),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Err(ErrorResponse::new(
|
||||
"Not Authorized",
|
||||
StatusCode::UNAUTHORIZED,
|
||||
)),
|
||||
None => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Not Authorized".to_string(),
|
||||
status: StatusCode::UNAUTHORIZED,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_user_option(req: HttpRequest) -> Option<Users> {
|
||||
let user = get_user(req);
|
||||
|
||||
match user {
|
||||
Ok(user) => Some(user),
|
||||
Err(_) => None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,33 @@
|
|||
use crate::helpers::db;
|
||||
use crate::schema::{artists, artists_tracks};
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
use diesel::{
|
||||
AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(AsChangeset, Identifiable, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||
#[diesel(table_name = artists)]
|
||||
pub struct Artists {
|
||||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||
#[diesel(table_name = crate::schema::artists)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Artist {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub slug: String,
|
||||
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
created_at: NaiveDateTime,
|
||||
updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Insertable, Deserialize, Serialize)]
|
||||
#[diesel(table_name = artists)]
|
||||
pub struct NewArtist {
|
||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
||||
pub struct Artists {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub title: String,
|
||||
|
||||
created_at: NaiveDateTime,
|
||||
updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
|
@ -32,14 +36,14 @@ pub struct NewArtist {
|
|||
impl Artists {
|
||||
pub fn find(id: String) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let artist = artists::table.filter(artists::id.eq(id)).first(conn)?;
|
||||
Ok(artist)
|
||||
let playlist = artists::table.filter(artists::id.eq(id)).first(conn)?;
|
||||
Ok(playlist)
|
||||
}
|
||||
|
||||
pub fn create(artist: NewArtist) -> Result<Self, Error> {
|
||||
pub fn create(artist: Artist) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let playlist = diesel::insert_into(artists::table)
|
||||
.values(artist)
|
||||
.values(Artist::from(artist))
|
||||
.get_result(conn)?;
|
||||
Ok(playlist)
|
||||
}
|
||||
|
@ -58,3 +62,18 @@ impl Artists {
|
|||
Ok(artists)
|
||||
}
|
||||
}
|
||||
|
||||
impl Artist {
|
||||
fn from(artist: Artist) -> Artist {
|
||||
Artist {
|
||||
id: artist.id,
|
||||
name: artist.name,
|
||||
|
||||
created_at: artist.created_at,
|
||||
updated_at: artist.updated_at,
|
||||
|
||||
spotify_id: artist.spotify_id,
|
||||
tidal_id: artist.tidal_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::helpers::db;
|
||||
|
||||
use super::artists::Artists;
|
||||
use super::tracks::Tracks;
|
||||
|
||||
use crate::schema::{artists, artists_tracks, tracks};
|
||||
|
||||
#[derive(Identifiable, Selectable, Queryable, Associations, Debug)]
|
||||
#[diesel(belongs_to(Artists, foreign_key = artist_id))]
|
||||
#[diesel(belongs_to(Tracks, foreign_key = track_id))]
|
||||
#[diesel(table_name = artists_tracks)]
|
||||
#[diesel(primary_key(artist_id, track_id))]
|
||||
pub struct ArtistTracks {
|
||||
pub artist_id: String,
|
||||
pub track_id: String,
|
||||
}
|
||||
|
||||
impl ArtistTracks {
|
||||
pub fn get_tracks(artist: &Artists) -> Result<Vec<Tracks>, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let track_ids = ArtistTracks::belonging_to(artist).select(artists_tracks::track_id);
|
||||
|
||||
let tracks: Vec<Tracks> = tracks::table
|
||||
.filter(tracks::id.eq_any(track_ids))
|
||||
.load::<Tracks>(conn)?;
|
||||
|
||||
Ok(tracks)
|
||||
}
|
||||
|
||||
pub fn get_artists(track: &Tracks) -> Result<Vec<TrackArtist>, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let artist_ids = ArtistTracks::belonging_to(track).select(artists_tracks::artist_id);
|
||||
|
||||
let artists = artists::table
|
||||
.select((artists::id, artists::name))
|
||||
.filter(artists::id.eq_any(artist_ids))
|
||||
.load::<TrackArtist>(conn)?;
|
||||
|
||||
Ok(artists)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Queryable)]
|
||||
#[diesel(table_name = artists)]
|
||||
pub struct TrackArtist {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
|
@ -1,7 +1,4 @@
|
|||
pub mod artists;
|
||||
pub mod artists_tracks;
|
||||
pub mod playlists;
|
||||
pub mod playlists_tracks;
|
||||
pub mod spotify;
|
||||
pub mod tracks;
|
||||
pub mod users;
|
||||
pub mod user;
|
||||
|
|
|
@ -1,45 +1,45 @@
|
|||
use crate::helpers::db;
|
||||
use crate::schema::{playlists, playlists_tracks};
|
||||
use crate::models::tracks::{Track, Tracks, TracksWithArtists};
|
||||
use crate::models::user::Users;
|
||||
use crate::schema::playlists;
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::{prelude::*, result::Error};
|
||||
use diesel::result::Error;
|
||||
use diesel::{
|
||||
AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::artists_tracks::{ArtistTracks, TrackArtist};
|
||||
use super::{playlists_tracks::PlaylistTracks, tracks::Tracks, users::Users};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PlaylistCreator {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
AsChangeset, Debug, Deserialize, Identifiable, Queryable, Selectable, Serialize, PartialEq,
|
||||
)]
|
||||
#[diesel(table_name = playlists)]
|
||||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||
#[diesel(table_name = crate::schema::playlists)]
|
||||
#[diesel(belongs_to(Users))]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Playlist {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub public: bool,
|
||||
pub creator_id: String,
|
||||
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
||||
pub struct Playlists {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub public: bool,
|
||||
|
||||
pub playlist_type: String,
|
||||
pub parent_id: Option<String>,
|
||||
|
||||
pub creator_id: String,
|
||||
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Insertable)]
|
||||
#[diesel(table_name = playlists)]
|
||||
pub struct NewPlaylist {
|
||||
pub name: String,
|
||||
pub public: bool,
|
||||
// pub playlist_type: String,
|
||||
pub creator_id: String,
|
||||
}
|
||||
|
||||
impl Playlists {
|
||||
pub fn find(id: &str) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
|
@ -59,26 +59,23 @@ impl Playlists {
|
|||
let conn = &mut db::connection()?;
|
||||
|
||||
let mut playlists = playlists::table
|
||||
.distinct()
|
||||
.select(Playlists::as_select())
|
||||
.order((playlists::playlist_type.desc(), playlists::parent_id.desc()))
|
||||
.filter(playlists::creator_id.eq(user_id))
|
||||
.into_boxed();
|
||||
|
||||
if filter_public {
|
||||
playlists = playlists.filter(
|
||||
playlists::public
|
||||
.eq(true)
|
||||
.and(playlists::playlist_type.ne("folder")),
|
||||
);
|
||||
playlists = playlists.filter(playlists::public.eq(true));
|
||||
}
|
||||
|
||||
let playlists = playlists.get_results(conn)?;
|
||||
Ok(playlists)
|
||||
}
|
||||
|
||||
pub fn get_tracks(&self) -> Result<Vec<PlaylistTrack>, Error> {
|
||||
let tracks: Vec<PlaylistTrack> = PlaylistTracks::get_tracks(self)?;
|
||||
pub fn get_tracks(&self) -> Result<Vec<TracksWithArtists>, Error> {
|
||||
let tracks = Tracks::find_by_playlist(&self.id)?;
|
||||
let tracks: Vec<TracksWithArtists> = tracks
|
||||
.into_iter()
|
||||
.map(Track::with_artists)
|
||||
.collect::<Result<Vec<TracksWithArtists>, _>>()?;
|
||||
|
||||
Ok(tracks)
|
||||
}
|
||||
|
@ -87,69 +84,34 @@ impl Playlists {
|
|||
let creator = Users::find(&self.creator_id)?;
|
||||
Ok(creator)
|
||||
}
|
||||
|
||||
pub fn get_data(&self, tracks: &[PlaylistTrack]) -> (usize, usize) {
|
||||
if tracks.is_empty() {
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
let duration = tracks
|
||||
.iter()
|
||||
.map(|track| track.duration_ms)
|
||||
.reduce(|a, b| a + b)
|
||||
.unwrap() as usize;
|
||||
|
||||
(duration, tracks.len())
|
||||
#[derive(Debug, Insertable, Deserialize)]
|
||||
#[diesel(table_name = playlists)]
|
||||
pub struct NewPlaylist {
|
||||
pub name: String,
|
||||
pub public: bool,
|
||||
pub creator_id: Option<String>,
|
||||
}
|
||||
|
||||
pub fn can_see(&self, creator: Option<Users>) -> bool {
|
||||
self.public || creator.map_or(false, |user| user.id == self.creator_id)
|
||||
impl Playlist {
|
||||
pub fn create(name: &str, public: bool, creator_id: &str) -> NewPlaylist {
|
||||
NewPlaylist {
|
||||
name: name.to_string(),
|
||||
public,
|
||||
creator_id: Some(creator_id.to_string()),
|
||||
}
|
||||
}
|
||||
|
||||
// // Folder
|
||||
// struct Folder {
|
||||
// id: String,
|
||||
// name: String,
|
||||
// playlists: Vec<Playlists>,
|
||||
// }
|
||||
fn from(playlist: Playlist) -> Playlist {
|
||||
Playlist {
|
||||
id: playlist.id,
|
||||
name: playlist.name,
|
||||
public: playlist.public,
|
||||
creator_id: playlist.creator_id,
|
||||
|
||||
// impl Folder {
|
||||
// pub fn get_playlists(&self) -> Result<Vec<Playlists>, Error> {
|
||||
// let playlists = playlists::table
|
||||
// }
|
||||
// }
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct PlaylistTrack {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub duration_ms: i32,
|
||||
|
||||
pub artists: Vec<TrackArtist>,
|
||||
pub added_at: NaiveDateTime,
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
}
|
||||
|
||||
impl From<Tracks> for PlaylistTrack {
|
||||
fn from(track: Tracks) -> Self {
|
||||
let artists = ArtistTracks::get_artists(&track).unwrap();
|
||||
|
||||
let added_at = playlists_tracks::table
|
||||
.filter(playlists_tracks::track_id.eq(&track.id))
|
||||
.select(playlists_tracks::added_at)
|
||||
.first::<NaiveDateTime>(&mut db::connection().unwrap())
|
||||
.unwrap();
|
||||
|
||||
PlaylistTrack {
|
||||
id: track.id,
|
||||
title: track.title,
|
||||
duration_ms: track.duration_ms,
|
||||
artists,
|
||||
added_at,
|
||||
spotify_id: track.spotify_id,
|
||||
tidal_id: track.tidal_id,
|
||||
created_at: playlist.created_at,
|
||||
updated_at: playlist.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
|
||||
use crate::helpers::db;
|
||||
|
||||
use super::playlists::{PlaylistTrack, Playlists};
|
||||
use super::tracks::Tracks;
|
||||
|
||||
use crate::schema::{playlists_tracks, tracks};
|
||||
|
||||
#[derive(Identifiable, Selectable, Queryable, Associations, Debug)]
|
||||
#[diesel(belongs_to(Playlists, foreign_key = playlist_id))]
|
||||
#[diesel(belongs_to(Tracks, foreign_key = track_id))]
|
||||
#[diesel(table_name = playlists_tracks)]
|
||||
#[diesel(primary_key(playlist_id, track_id))]
|
||||
pub struct PlaylistTracks {
|
||||
pub playlist_id: String,
|
||||
pub track_id: String,
|
||||
pub added_at: String,
|
||||
}
|
||||
|
||||
impl PlaylistTracks {
|
||||
pub fn get_tracks(playlist: &Playlists) -> Result<Vec<PlaylistTrack>, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let track_ids = PlaylistTracks::belonging_to(playlist).select(playlists_tracks::track_id);
|
||||
|
||||
let tracks: Vec<Tracks> = tracks::table
|
||||
.filter(tracks::id.eq_any(track_ids))
|
||||
.load::<Tracks>(conn)?;
|
||||
|
||||
let tracks = tracks
|
||||
.into_iter()
|
||||
.map(PlaylistTrack::from)
|
||||
.collect::<Vec<PlaylistTrack>>();
|
||||
|
||||
Ok(tracks)
|
||||
}
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Track {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub duration_ms: i32,
|
||||
pub artists: Vec<Artist>,
|
||||
pub album: Album,
|
||||
|
||||
pub external_ids: ExternalIds,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct ExternalIds {
|
||||
pub isrc: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Artist {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct Album {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub artists: Vec<Artist>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct APISearchResponse {
|
||||
pub albums: APISearchItems<Album>,
|
||||
pub artists: APISearchItems<Artist>,
|
||||
pub tracks: APISearchItems<Track>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct APISearchItems<T> {
|
||||
pub items: Vec<T>,
|
||||
}
|
|
@ -1,34 +1,39 @@
|
|||
use crate::helpers::db;
|
||||
use crate::models::{artists::Artists, spotify};
|
||||
use crate::schema::{playlists_tracks, tracks};
|
||||
use crate::models::artists::Artists;
|
||||
use crate::schema::tracks;
|
||||
use chrono::NaiveDateTime;
|
||||
use diesel::prelude::*;
|
||||
use diesel::result::Error;
|
||||
use diesel::{
|
||||
AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(
|
||||
AsChangeset, Debug, Deserialize, Serialize, Queryable, Identifiable, Selectable, Clone,
|
||||
)]
|
||||
#[diesel(table_name = tracks)]
|
||||
pub struct Tracks {
|
||||
use crate::schema::playlists_tracks;
|
||||
|
||||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||
#[diesel(table_name = crate::schema::tracks)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct Track {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub duration_ms: i32,
|
||||
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(AsChangeset, Clone, Debug, Deserialize, Insertable)]
|
||||
#[diesel(table_name = tracks)]
|
||||
pub struct NewTrack {
|
||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
||||
pub struct Tracks {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub duration_ms: i32,
|
||||
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
|
||||
pub spotify_id: Option<String>,
|
||||
pub tidal_id: Option<String>,
|
||||
}
|
||||
|
@ -40,29 +45,51 @@ impl Tracks {
|
|||
Ok(playlist)
|
||||
}
|
||||
|
||||
pub fn create(track: NewTrack) -> Result<Self, Error> {
|
||||
pub fn create(track: Track) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let playlist = diesel::insert_into(tracks::table)
|
||||
.values(track)
|
||||
.values(Track::from(track))
|
||||
.get_result(conn)?;
|
||||
Ok(playlist)
|
||||
}
|
||||
|
||||
pub fn create_or_update(track: NewTrack) -> Result<Self, Error> {
|
||||
pub fn find_by_playlist(playlist_id: &str) -> Result<Vec<Tracks>, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let playlist = diesel::insert_into(tracks::table)
|
||||
.values(track.clone())
|
||||
.on_conflict(tracks::id)
|
||||
.do_update()
|
||||
.set(track)
|
||||
.get_result(conn)?;
|
||||
Ok(playlist)
|
||||
let tracks: Vec<(String, String)> = playlists_tracks::table
|
||||
.filter(playlists_tracks::playlist_id.eq(playlist_id))
|
||||
.get_results::<(String, String)>(conn)?;
|
||||
|
||||
let tracks = tracks
|
||||
.into_iter()
|
||||
.map(|(playlist_id, track_id)| {
|
||||
println!("{}: {}", playlist_id, track_id);
|
||||
Tracks::find(track_id).unwrap()
|
||||
})
|
||||
.collect::<Vec<Tracks>>();
|
||||
|
||||
Ok(tracks)
|
||||
}
|
||||
|
||||
pub fn get_artists(&self) -> Result<Vec<Artists>, Error> {
|
||||
let artists = Artists::find_by_track(&self.id)?;
|
||||
Ok(artists)
|
||||
}
|
||||
}
|
||||
|
||||
impl Track {
|
||||
fn from(track: Track) -> Track {
|
||||
Track {
|
||||
id: track.id,
|
||||
title: track.title,
|
||||
duration_ms: track.duration_ms,
|
||||
|
||||
created_at: track.created_at,
|
||||
updated_at: track.updated_at,
|
||||
|
||||
spotify_id: track.spotify_id,
|
||||
tidal_id: track.tidal_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_artists(track: Tracks) -> Result<TracksWithArtists, Error> {
|
||||
let artists = track.get_artists()?;
|
||||
|
@ -80,20 +107,6 @@ impl Tracks {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<spotify::Track> for Tracks {
|
||||
fn from(value: spotify::Track) -> Tracks {
|
||||
Tracks {
|
||||
id: value.external_ids.isrc,
|
||||
title: value.name,
|
||||
duration_ms: value.duration_ms,
|
||||
spotify_id: Some(value.id),
|
||||
tidal_id: None,
|
||||
created_at: None,
|
||||
updated_at: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct TracksWithArtists {
|
||||
pub id: String,
|
||||
|
|
|
@ -11,6 +11,18 @@ use serde::{Deserialize, Serialize};
|
|||
#[derive(AsChangeset, Insertable, Queryable, Selectable, Deserialize, Serialize)]
|
||||
#[diesel(table_name = crate::schema::users)]
|
||||
#[diesel(check_for_backend(diesel::pg::Pg))]
|
||||
pub struct User {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub email: String,
|
||||
|
||||
pub password: String,
|
||||
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
pub created_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Queryable, Serialize)]
|
||||
pub struct Users {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
|
@ -18,8 +30,8 @@ pub struct Users {
|
|||
|
||||
pub password: String,
|
||||
|
||||
pub created_at: Option<NaiveDateTime>,
|
||||
pub updated_at: Option<NaiveDateTime>,
|
||||
pub created_at: NaiveDateTime,
|
||||
}
|
||||
|
||||
impl Users {
|
||||
|
@ -29,6 +41,14 @@ impl Users {
|
|||
Ok(user)
|
||||
}
|
||||
|
||||
pub fn create(user: User) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let user = diesel::insert_into(users::table)
|
||||
.values(User::from(user))
|
||||
.get_result(conn)?;
|
||||
Ok(user)
|
||||
}
|
||||
|
||||
pub fn find_by_email(email: &str) -> Result<Self, Error> {
|
||||
let conn = &mut db::connection()?;
|
||||
let user = users::table.filter(users::email.eq(email)).first(conn)?;
|
||||
|
@ -39,3 +59,16 @@ impl Users {
|
|||
bcrypt::verify(password, hash)
|
||||
}
|
||||
}
|
||||
|
||||
impl User {
|
||||
fn from(user: User) -> User {
|
||||
User {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
password: user.password,
|
||||
created_at: user.created_at,
|
||||
updated_at: user.updated_at,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,28 +1,24 @@
|
|||
use crate::helpers::jwt::get_encoding_key;
|
||||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::models::users::Users;
|
||||
use crate::models::user::Users;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{post, web, HttpResponse, Scope};
|
||||
use actix_web::{post, web, HttpResponse};
|
||||
use chrono::{Days, Utc};
|
||||
use jsonwebtoken::{encode, Header};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn routes() -> Scope {
|
||||
web::scope("/auth").service(login)
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct JWTClaims {
|
||||
pub user_id: String,
|
||||
pub exp: i64,
|
||||
pub exp: usize,
|
||||
}
|
||||
|
||||
#[post("/login")]
|
||||
#[post("/auth/login")]
|
||||
async fn login(body: web::Json<LoginBody>) -> Result<HttpResponse, ErrorResponse> {
|
||||
#[derive(Deserialize, Serialize)]
|
||||
struct Response {
|
||||
access_token: String,
|
||||
exp: i64,
|
||||
exp: usize,
|
||||
}
|
||||
|
||||
let user = Users::find_by_email(&body.email);
|
||||
|
@ -36,24 +32,28 @@ async fn login(body: web::Json<LoginBody>) -> Result<HttpResponse, ErrorResponse
|
|||
|
||||
let claims = JWTClaims {
|
||||
user_id: user.id.to_string(),
|
||||
exp,
|
||||
exp: exp as usize,
|
||||
};
|
||||
let token = encode(&Header::default(), &claims, &get_encoding_key()).unwrap();
|
||||
|
||||
Ok(HttpResponse::Ok().json(Response {
|
||||
access_token: token,
|
||||
exp,
|
||||
exp: exp as usize,
|
||||
}))
|
||||
}
|
||||
Err(_e) => Err(ErrorResponse::new(
|
||||
"Invalid credentials",
|
||||
StatusCode::BAD_REQUEST,
|
||||
)),
|
||||
Err(_e) => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Invalid credentials.".to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
})
|
||||
}
|
||||
},
|
||||
Err(_err) => Err(ErrorResponse::new(
|
||||
"Invalid credentials",
|
||||
StatusCode::BAD_REQUEST,
|
||||
)),
|
||||
Err(_err) => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Invalid credentials.".to_string(),
|
||||
status: StatusCode::BAD_REQUEST,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
use actix_web::{get, web, HttpResponse, Result, Scope};
|
||||
|
||||
use crate::middlewares::error::ErrorResponse;
|
||||
|
||||
pub fn routes() -> Scope {
|
||||
web::scope("/import")
|
||||
}
|
||||
|
||||
#[get("/spotify/{playlist_id}")]
|
||||
async fn import_spotify() -> Result<HttpResponse, ErrorResponse> {
|
||||
Ok(HttpResponse::Ok().finish())
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::middlewares::user::get_user;
|
||||
use crate::models::playlists::{NewPlaylist, PlaylistCreator, Playlists};
|
||||
use crate::routes::playlists::GetPlaylistResponse;
|
||||
use crate::models::playlists::{NewPlaylist, Playlists};
|
||||
use actix_web::{get, post, web, HttpRequest, HttpResponse, Scope};
|
||||
use serde::Serialize;
|
||||
|
||||
|
@ -34,32 +33,14 @@ async fn me(req: HttpRequest) -> Result<HttpResponse, ErrorResponse> {
|
|||
async fn me_playlists(req: HttpRequest) -> Result<HttpResponse, ErrorResponse> {
|
||||
let user = get_user(req)?;
|
||||
|
||||
let playlists = Playlists::find_for_user(&user.id, false)?;
|
||||
let playlists = playlists
|
||||
.into_iter()
|
||||
.map(|playlist| {
|
||||
let creator = playlist.get_creator().unwrap();
|
||||
|
||||
let tracks = playlist.get_tracks().unwrap();
|
||||
let (duration, tracks_count) = playlist.get_data(&tracks);
|
||||
|
||||
GetPlaylistResponse {
|
||||
id: playlist.id.to_string(),
|
||||
name: playlist.name.to_string(),
|
||||
playlist_type: playlist.playlist_type.to_string(),
|
||||
parent_id: playlist.parent_id,
|
||||
creator: PlaylistCreator {
|
||||
id: creator.id,
|
||||
name: creator.name,
|
||||
},
|
||||
tracks,
|
||||
tracks_count,
|
||||
duration,
|
||||
#[derive(Serialize)]
|
||||
struct Response {
|
||||
playlists: Vec<Playlists>,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<GetPlaylistResponse>>();
|
||||
|
||||
Ok(HttpResponse::Ok().json(playlists))
|
||||
Ok(HttpResponse::Ok().json(Response {
|
||||
playlists: Playlists::find_for_user(&user.id, false)?,
|
||||
}))
|
||||
}
|
||||
|
||||
#[post("/playlists")]
|
||||
|
@ -69,7 +50,7 @@ async fn create_playlist(
|
|||
) -> Result<HttpResponse, ErrorResponse> {
|
||||
let user = get_user(req)?;
|
||||
|
||||
playlist.creator_id = user.id;
|
||||
playlist.creator_id = Option::from(user.id);
|
||||
|
||||
Playlists::create(playlist.into_inner())?;
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
pub mod auth;
|
||||
pub mod import;
|
||||
pub mod me;
|
||||
pub mod playlists;
|
||||
pub mod search;
|
||||
pub mod users;
|
||||
|
|
|
@ -1,51 +1,37 @@
|
|||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::middlewares::user::get_user_option;
|
||||
use crate::models::playlists::{PlaylistCreator, PlaylistTrack, Playlists};
|
||||
use actix_web::{get, web, HttpRequest, HttpResponse, Scope};
|
||||
use reqwest::StatusCode;
|
||||
use crate::models::playlists::{PlaylistCreator, Playlists};
|
||||
use crate::models::tracks::TracksWithArtists;
|
||||
use actix_web::{get, web, HttpResponse};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn routes() -> Scope {
|
||||
web::scope("/playlists").service(get_playlist)
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct GetPlaylistResponse {
|
||||
struct GetPlaylistResponse {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub playlist_type: String,
|
||||
pub parent_id: Option<String>,
|
||||
pub creator: PlaylistCreator,
|
||||
pub tracks: Vec<PlaylistTrack>,
|
||||
pub tracks: Vec<TracksWithArtists>,
|
||||
pub tracks_count: usize,
|
||||
pub duration: usize,
|
||||
}
|
||||
|
||||
#[get("/{playlist_id}")]
|
||||
async fn get_playlist(
|
||||
req: HttpRequest,
|
||||
path: web::Path<String>,
|
||||
) -> Result<HttpResponse, ErrorResponse> {
|
||||
pub async fn get_playlist(path: web::Path<String>) -> Result<HttpResponse, ErrorResponse> {
|
||||
let playlist_id = path.into_inner();
|
||||
let playlist = Playlists::find(playlist_id.as_str())?;
|
||||
|
||||
if !playlist.can_see(get_user_option(req)) {
|
||||
return Err(ErrorResponse::new(
|
||||
"Playlist not found",
|
||||
StatusCode::NOT_FOUND,
|
||||
));
|
||||
}
|
||||
|
||||
let creator = playlist.get_creator()?;
|
||||
|
||||
let tracks = playlist.get_tracks()?;
|
||||
let (duration, tracks_count) = playlist.get_data(&tracks);
|
||||
let tracks_count = tracks.len();
|
||||
let duration = tracks
|
||||
.iter()
|
||||
.map(|track| track.duration_ms)
|
||||
.reduce(|a, b| a + b)
|
||||
.unwrap() as usize;
|
||||
|
||||
Ok(HttpResponse::Ok().json(GetPlaylistResponse {
|
||||
id: playlist.id.to_string(),
|
||||
name: playlist.name.to_string(),
|
||||
playlist_type: playlist.playlist_type.to_string(),
|
||||
parent_id: playlist.parent_id,
|
||||
creator: PlaylistCreator {
|
||||
id: creator.id,
|
||||
name: creator.name,
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::models::spotify;
|
||||
use crate::services::spotify as Spotify;
|
||||
use actix_web::{get, web, HttpResponse, Result, Scope};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub fn routes() -> Scope {
|
||||
web::scope("/search").service(search)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SearchQuery {
|
||||
q: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SearchResponse {
|
||||
tracks: Vec<SearchTracks>,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SearchTracks {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
// pub artists: String,
|
||||
pub duration_ms: i32,
|
||||
}
|
||||
|
||||
impl From<spotify::Track> for SearchTracks {
|
||||
fn from(value: spotify::Track) -> SearchTracks {
|
||||
SearchTracks {
|
||||
id: value.id,
|
||||
title: value.name,
|
||||
duration_ms: value.duration_ms,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[get("")]
|
||||
async fn search(query: web::Query<SearchQuery>) -> Result<HttpResponse, ErrorResponse> {
|
||||
let spotify = Spotify::instance().await;
|
||||
let search = spotify.search(&query.q).await.unwrap();
|
||||
|
||||
Ok(HttpResponse::Ok().json(SearchResponse {
|
||||
tracks: search
|
||||
.tracks
|
||||
.items
|
||||
.into_iter()
|
||||
.map(SearchTracks::from)
|
||||
.collect::<Vec<SearchTracks>>(),
|
||||
}))
|
||||
}
|
|
@ -1,9 +1,6 @@
|
|||
use crate::middlewares::error::ErrorResponse;
|
||||
use crate::models::{
|
||||
playlists::{PlaylistCreator, Playlists},
|
||||
users::Users,
|
||||
};
|
||||
use crate::routes::playlists::GetPlaylistResponse;
|
||||
use crate::models::playlists::Playlists;
|
||||
use crate::models::user::Users;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{get, web, HttpResponse, Result, Scope};
|
||||
use diesel::result::Error as DBError;
|
||||
|
@ -27,11 +24,18 @@ fn get_a_user(user_id: &str) -> Result<Users, ErrorResponse> {
|
|||
|
||||
match user {
|
||||
Ok(user) => Ok(user),
|
||||
Err(DBError::NotFound) => Err(ErrorResponse::new("User not found", StatusCode::NOT_FOUND)),
|
||||
_ => Err(ErrorResponse::new(
|
||||
"Unknown error",
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
)),
|
||||
Err(DBError::NotFound) => {
|
||||
return Err(ErrorResponse {
|
||||
message: "User not found".to_string(),
|
||||
status: StatusCode::NOT_FOUND,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
return Err(ErrorResponse {
|
||||
message: "Unknown error".to_string(),
|
||||
status: StatusCode::INTERNAL_SERVER_ERROR,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,29 +56,6 @@ async fn get_user_playlists(path: web::Path<String>) -> Result<HttpResponse, Err
|
|||
let user = get_a_user(&path.into_inner())?;
|
||||
|
||||
let playlists = Playlists::find_for_user(&user.id, true)?;
|
||||
let playlists = playlists
|
||||
.into_iter()
|
||||
.map(|playlist| {
|
||||
let creator = playlist.get_creator().unwrap();
|
||||
|
||||
let tracks = playlist.get_tracks().unwrap();
|
||||
let (duration, tracks_count) = playlist.get_data(&tracks);
|
||||
|
||||
GetPlaylistResponse {
|
||||
id: playlist.id.to_string(),
|
||||
name: playlist.name.to_string(),
|
||||
playlist_type: playlist.playlist_type.to_string(),
|
||||
parent_id: playlist.parent_id,
|
||||
creator: PlaylistCreator {
|
||||
id: creator.id,
|
||||
name: creator.name,
|
||||
},
|
||||
tracks,
|
||||
tracks_count,
|
||||
duration,
|
||||
}
|
||||
})
|
||||
.collect::<Vec<GetPlaylistResponse>>();
|
||||
|
||||
Ok(HttpResponse::Ok().json(playlists))
|
||||
}
|
||||
|
|
|
@ -6,10 +6,9 @@ diesel::table! {
|
|||
id -> Varchar,
|
||||
#[max_length = 255]
|
||||
name -> Varchar,
|
||||
slug -> Varchar,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
created_at -> Timestamp,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
#[max_length = 22]
|
||||
#[max_length = 21]
|
||||
spotify_id -> Nullable<Varchar>,
|
||||
#[max_length = 10]
|
||||
tidal_id -> Nullable<Varchar>,
|
||||
|
@ -33,12 +32,8 @@ diesel::table! {
|
|||
name -> Varchar,
|
||||
public -> Bool,
|
||||
#[max_length = 24]
|
||||
playlist_type -> Varchar,
|
||||
#[max_length = 24]
|
||||
parent_id -> Nullable<Varchar>,
|
||||
#[max_length = 24]
|
||||
creator_id -> Varchar,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
created_at -> Timestamp,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +44,6 @@ diesel::table! {
|
|||
playlist_id -> Varchar,
|
||||
#[max_length = 24]
|
||||
track_id -> Varchar,
|
||||
added_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -60,9 +54,9 @@ diesel::table! {
|
|||
#[max_length = 255]
|
||||
title -> Varchar,
|
||||
duration_ms -> Int4,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
created_at -> Timestamp,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
#[max_length = 22]
|
||||
#[max_length = 21]
|
||||
spotify_id -> Nullable<Varchar>,
|
||||
#[max_length = 10]
|
||||
tidal_id -> Nullable<Varchar>,
|
||||
|
@ -79,7 +73,7 @@ diesel::table! {
|
|||
email -> Varchar,
|
||||
password -> Text,
|
||||
updated_at -> Nullable<Timestamp>,
|
||||
created_at -> Nullable<Timestamp>,
|
||||
created_at -> Timestamp,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
mod service;
|
||||
pub mod spotify;
|
|
@ -1,11 +0,0 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
#[async_trait]
|
||||
pub trait Service: Send + Sync {
|
||||
fn get_token(&self) -> &String;
|
||||
|
||||
async fn fetch_token(&mut self);
|
||||
// async fn get_track_by_isrc(&self, isrc: &str);
|
||||
|
||||
fn is_expired(&self) -> bool;
|
||||
}
|
|
@ -1,129 +0,0 @@
|
|||
use crate::{models::spotify::APISearchResponse, services::service::Service, utils::get_env};
|
||||
use actix_web::http::Method;
|
||||
use async_trait::async_trait;
|
||||
use chrono::{Duration, Utc};
|
||||
use lazy_static::lazy_static;
|
||||
use reqwest::{Client, RequestBuilder};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Mutex;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SpotifyResponse {
|
||||
access_token: String,
|
||||
expires_in: i64,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Spotify {
|
||||
token: String,
|
||||
expires_at: i64,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
static ref SPOTIFY_INSTANCE: Mutex<Spotify> = Mutex::new(Spotify::new());
|
||||
}
|
||||
|
||||
pub async fn instance() -> Spotify {
|
||||
let mut spotify = SPOTIFY_INSTANCE.lock().unwrap().clone();
|
||||
|
||||
// Fetch token if expired
|
||||
spotify.fetch_token().await;
|
||||
|
||||
spotify
|
||||
}
|
||||
|
||||
impl Spotify {
|
||||
pub fn new() -> Spotify {
|
||||
Spotify {
|
||||
token: String::new(),
|
||||
expires_at: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn api(&self, url: &str, method: Method) -> RequestBuilder {
|
||||
let client = Client::new();
|
||||
|
||||
client
|
||||
.request(method, format!("https://api.spotify.com/v1{url}"))
|
||||
.bearer_auth(self.get_token())
|
||||
}
|
||||
|
||||
// pub async fn get_track(self, spotify_id: &str) {
|
||||
// let spotify = &SPOTIFY;
|
||||
// }
|
||||
|
||||
// pub async fn get_artist(self, spotify_id: &str) {}
|
||||
|
||||
// pub async fn get_track_by_isrc(self, isrc: &str) {}
|
||||
|
||||
pub async fn search(&self, search: &str) -> Result<APISearchResponse, ()> {
|
||||
let response = self
|
||||
.api("/search", Method::GET)
|
||||
.query(&[("q", search), ("type", "album,artist,track")])
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match response {
|
||||
Ok(res) => {
|
||||
let data = res.json::<APISearchResponse>().await;
|
||||
|
||||
match data {
|
||||
Ok(data) => Ok(data),
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Search)")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Search)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Service for Spotify {
|
||||
fn get_token(&self) -> &String {
|
||||
&self.token
|
||||
}
|
||||
|
||||
fn is_expired(&self) -> bool {
|
||||
self.expires_at < chrono::Utc::now().timestamp()
|
||||
}
|
||||
|
||||
async fn fetch_token(&mut self) {
|
||||
if !self.is_expired() {
|
||||
return;
|
||||
}
|
||||
|
||||
let response = Client::new()
|
||||
.post("https://accounts.spotify.com/api/token")
|
||||
.basic_auth(
|
||||
get_env("SPOTIFY_CLIENT_ID"),
|
||||
Some(get_env("SPOTIFY_CLIENT_SECRET")),
|
||||
)
|
||||
.form(&[("grant_type", "client_credentials")])
|
||||
.send()
|
||||
.await;
|
||||
|
||||
match response {
|
||||
Ok(res) => {
|
||||
let data = res.json::<SpotifyResponse>().await;
|
||||
|
||||
match data {
|
||||
Ok(data) => {
|
||||
self.token = data.access_token;
|
||||
self.expires_at =
|
||||
(Utc::now() + Duration::seconds(data.expires_in)).timestamp();
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Token)")
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
panic!("Invalid response (Spotify Token)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
pub fn get_env(key: &str) -> String {
|
||||
std::env::var(key).unwrap()
|
||||
}
|
Loading…
Reference in a new issue