From 52ef924f12e67a1a4388299f218a1bc8d65fb192 Mon Sep 17 00:00:00 2001 From: Miguel da Mota Date: Mon, 1 Jan 2024 03:18:33 +0100 Subject: [PATCH] updatse --- Cargo.lock | 252 ++++++++++++++---- Cargo.toml | 2 +- migrations/2023-12-29-191547_users/down.sql | 2 +- migrations/2023-12-29-191547_users/up.sql | 16 +- .../2023-12-30-162032_playlists/down.sql | 2 +- migrations/2023-12-30-162032_playlists/up.sql | 22 +- migrations/2023-12-30-192105_tracks/down.sql | 2 +- migrations/2023-12-30-192105_tracks/up.sql | 18 +- .../down.sql | 2 +- .../2023-12-30-214938_playlists_tracks/up.sql | 9 +- migrations/2023-12-31-104738_artists/down.sql | 2 + migrations/2023-12-31-104738_artists/up.sql | 15 ++ .../2023-12-31-104741_artists_tracks/down.sql | 2 + .../2023-12-31-104741_artists_tracks/up.sql | 8 + src/helpers/jwt.rs | 25 +- src/main.rs | 5 +- src/middlewares/user.rs | 20 +- src/models/artists.rs | 79 ++++++ src/models/mod.rs | 3 +- src/models/{playlist.rs => playlists.rs} | 47 +++- src/models/tracks.rs | 37 ++- src/models/user.rs | 5 +- src/routes/auth.rs | 47 ++-- src/routes/me.rs | 25 +- src/routes/playlists.rs | 20 +- src/routes/users.rs | 43 ++- src/schema.rs | 29 ++ src/utils.rs | 8 - 28 files changed, 577 insertions(+), 170 deletions(-) create mode 100644 migrations/2023-12-31-104738_artists/down.sql create mode 100644 migrations/2023-12-31-104738_artists/up.sql create mode 100644 migrations/2023-12-31-104741_artists_tracks/down.sql create mode 100644 migrations/2023-12-31-104741_artists_tracks/up.sql create mode 100644 src/models/artists.rs rename src/models/{playlist.rs => playlists.rs} (59%) delete mode 100644 src/utils.rs diff --git a/Cargo.lock b/Cargo.lock index 0e00b24..38e001b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,7 +30,7 @@ dependencies = [ "actix-service", "actix-utils", "ahash", - "base64 0.21.5", + "base64", "bitflags 2.4.1", "brotli", "bytes", @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -179,7 +179,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -199,9 +199,9 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] name = "ahash" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ "cfg-if", "getrandom", @@ -251,13 +251,13 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.75" +version = "0.1.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf6721fb0140e4f897002dd086c06f6c27775df19cfe1fccb21181a48fd2c98" +checksum = "531b97fb4cd3dfdce92c35dedbfdc1f0b9d8091c8ca943d6dae340ef5012d514" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -281,12 +281,6 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - [[package]] name = "base64" version = "0.21.5" @@ -299,7 +293,7 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d1c9c15093eb224f0baa400f38fcd713fc1391a6f1c389d886beef146d60a3" dependencies = [ - "base64 0.21.5", + "base64", "blowfish", "getrandom", "subtle", @@ -413,7 +407,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -479,9 +473,9 @@ dependencies = [ [[package]] name = "deranged" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eb30d70a07a3b04884d2677f06bec33509dc67ca60d92949e5535352d3191dc" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] @@ -523,7 +517,7 @@ dependencies = [ "diesel_table_macro_syntax", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -543,7 +537,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5" dependencies = [ - "syn 2.0.43", + "syn 2.0.44", ] [[package]] @@ -650,8 +644,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -719,9 +715,9 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "iana-time-zone" -version = "0.1.58" +version = "0.1.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" +checksum = "b6a67363e2aa4443928ce15e57ebae94fd8949958fd1223c4cfc0cd473ad7539" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -794,18 +790,18 @@ dependencies = [ ] [[package]] -name = "jwt" -version = "0.16.0" +name = "jsonwebtoken" +version = "9.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f" +checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4" dependencies = [ - "base64 0.13.1", - "crypto-common", - "digest", - "hmac", + "base64", + "js-sys", + "pem", + "ring", "serde", "serde_json", - "sha2", + "simple_asn1", ] [[package]] @@ -913,6 +909,27 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "num-bigint" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608e7659b5c3d7cba262d894801b9ec9d00de989e8a82bd4bef91d08da45cdc0" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -957,7 +974,7 @@ dependencies = [ "libc", "redox_syscall", "smallvec", - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -966,6 +983,16 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pem" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8fcc794035347fb64beda2d3b462595dd2753e3f268d89c5aae77e8cf2c310" +dependencies = [ + "base64", + "serde", +] + [[package]] name = "percent-encoding" version = "2.3.1" @@ -1013,18 +1040,18 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.71" +version = "1.0.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75cb1540fadbd5b8fbccc4dddad2734eba435053f725621c070711a14bb5f4b8" +checksum = "2dd5e8a1f1029c43224ad5898e50140c2aebb1705f19e67c918ebf5b9e797fe1" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "22a37c9326af5ed140c86a46655b5278de879853be5573c01df185b6f49a580a" dependencies = [ "proc-macro2", ] @@ -1108,6 +1135,20 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "ring" +version = "0.17.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74" +dependencies = [ + "cc", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys", +] + [[package]] name = "rustc-demangle" version = "0.1.23" @@ -1167,14 +1208,14 @@ checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] name = "serde_json" -version = "1.0.108" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +checksum = "cb0652c533506ad7a2e353cce269330d6afd8bdfb6d75e0ace5b35aacbd7b9e9" dependencies = [ "itoa", "ryu", @@ -1233,6 +1274,18 @@ dependencies = [ "libc", ] +[[package]] +name = "simple_asn1" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc4e5204eb1910f40f9cfa375f6f05b68c3abac4b6fd879c8ff5e7ae8a0a085" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror", + "time", +] + [[package]] name = "slab" version = "0.4.9" @@ -1258,6 +1311,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + [[package]] name = "subtle" version = "2.5.0" @@ -1277,15 +1336,35 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.43" +version = "2.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee659fb5f3d355364e1f3e5bc10fb82068efbf824a1e9d1c9504244a6469ad53" +checksum = "92d27c2c202598d05175a6dd3af46824b7f747f8d8e9b14c623f19fa5069735d" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.44", +] + [[package]] name = "time" version = "0.3.31" @@ -1442,6 +1521,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + [[package]] name = "url" version = "2.5.0" @@ -1478,7 +1563,7 @@ dependencies = [ "diesel_migrations", "dotenvy", "hmac", - "jwt", + "jsonwebtoken", "lazy_static", "serde", "serde_json", @@ -1512,7 +1597,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", "wasm-bindgen-shared", ] @@ -1534,7 +1619,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -1547,11 +1632,11 @@ checksum = "7ab9b36309365056cd639da3134bf87fa8f3d86008abf99e612384a6eecd459f" [[package]] name = "windows-core" -version = "0.51.1" +version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets", + "windows-targets 0.52.0", ] [[package]] @@ -1560,7 +1645,7 @@ version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" dependencies = [ - "windows-targets", + "windows-targets 0.48.5", ] [[package]] @@ -1569,13 +1654,28 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +dependencies = [ + "windows_aarch64_gnullvm 0.52.0", + "windows_aarch64_msvc 0.52.0", + "windows_i686_gnu 0.52.0", + "windows_i686_msvc 0.52.0", + "windows_x86_64_gnu 0.52.0", + "windows_x86_64_gnullvm 0.52.0", + "windows_x86_64_msvc 0.52.0", ] [[package]] @@ -1584,42 +1684,84 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" + [[package]] name = "windows_aarch64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" + [[package]] name = "windows_i686_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +[[package]] +name = "windows_i686_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" + [[package]] name = "windows_i686_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +[[package]] +name = "windows_i686_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" + [[package]] name = "windows_x86_64_gnu" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" + [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" + [[package]] name = "windows_x86_64_msvc" version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" + [[package]] name = "winnow" version = "0.5.31" @@ -1646,7 +1788,7 @@ checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.43", + "syn 2.0.44", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 5d4384a..c8a5e4e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,6 @@ dotenvy = "*" hmac = "0.12" sha2 = "0.10" lazy_static = "1" -jwt = "0.16" +jsonwebtoken = "9" serde = "1" serde_json = "1" diff --git a/migrations/2023-12-29-191547_users/down.sql b/migrations/2023-12-29-191547_users/down.sql index dc3714b..5795f6b 100644 --- a/migrations/2023-12-29-191547_users/down.sql +++ b/migrations/2023-12-29-191547_users/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -DROP TABLE users; +drop table users; diff --git a/migrations/2023-12-29-191547_users/up.sql b/migrations/2023-12-29-191547_users/up.sql index 46cd976..56dc507 100644 --- a/migrations/2023-12-29-191547_users/up.sql +++ b/migrations/2023-12-29-191547_users/up.sql @@ -1,13 +1,13 @@ -- Your SQL goes here -CREATE TABLE IF NOT EXISTS users +create table if not exists users ( - id VARCHAR(24) DEFAULT nanoid(24), - name VARCHAR(255) NOT NULL, - email VARCHAR(255) NOT NULL, - password TEXT NOT NULL, + id varchar(24) default nanoid(24), + name varchar(255) not null, + email varchar(255) not null, + password text not null, - updated_at TIMESTAMP, - created_at TIMESTAMP DEFAULT now() NOT NULL, + updated_at timestamp, + created_at timestamp default now() not null, - PRIMARY KEY (id) + primary key (id) ); diff --git a/migrations/2023-12-30-162032_playlists/down.sql b/migrations/2023-12-30-162032_playlists/down.sql index b8a6697..f8cf020 100644 --- a/migrations/2023-12-30-162032_playlists/down.sql +++ b/migrations/2023-12-30-162032_playlists/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -DROP TABLE playlists; +drop table playlists; diff --git a/migrations/2023-12-30-162032_playlists/up.sql b/migrations/2023-12-30-162032_playlists/up.sql index b5139a3..18789f0 100644 --- a/migrations/2023-12-30-162032_playlists/up.sql +++ b/migrations/2023-12-30-162032_playlists/up.sql @@ -1,16 +1,16 @@ -CREATE TABLE IF NOT EXISTS playlists +create table if not exists playlists ( - id VARCHAR(24) DEFAULT nanoid(24), - name VARCHAR(255) NOT NULL, + id varchar(24) default nanoid(24), + name varchar(255) not null, + public bool default false, - creator_id VARCHAR(24) NOT NULL, + creator_id varchar(24) not null, - created_at TIMESTAMP DEFAULT now() NOT NULL, - updated_at TIMESTAMP, + created_at timestamp default now() not null, + updated_at timestamp, - PRIMARY KEY (id), - FOREIGN KEY (creator_id) - REFERENCES users (id) + primary key (id), + foreign key (creator_id) + references users (id) + on delete cascade ); - -CREATE INDEX ON playlists (creator_id); diff --git a/migrations/2023-12-30-192105_tracks/down.sql b/migrations/2023-12-30-192105_tracks/down.sql index 37ad1eb..3aaeb7c 100644 --- a/migrations/2023-12-30-192105_tracks/down.sql +++ b/migrations/2023-12-30-192105_tracks/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -DROP TABLE tracks; +drop table tracks; diff --git a/migrations/2023-12-30-192105_tracks/up.sql b/migrations/2023-12-30-192105_tracks/up.sql index 8421839..3951c1e 100644 --- a/migrations/2023-12-30-192105_tracks/up.sql +++ b/migrations/2023-12-30-192105_tracks/up.sql @@ -1,16 +1,16 @@ -- Your SQL goes here -CREATE TABLE IF NOT EXISTS tracks +create table if not exists tracks ( - id VARCHAR(24) DEFAULT nanoid(24), - title VARCHAR(255) NOT NULL, - duration_ms INT NOT NULL DEFAULT 0, + id varchar(24) default nanoid(24), + title varchar(255) not null, + duration_ms int not null default 0, - created_at TIMESTAMP DEFAULT now() NOT NULL, - updated_at TIMESTAMP, + created_at timestamp default now() not null, + updated_at timestamp, -- music services - spotify_id VARCHAR(21) UNIQUE, - tidal_id VARCHAR(10) UNIQUE, + spotify_id varchar(21) unique, + tidal_id varchar(10) unique, - PRIMARY KEY (id) + primary key (id) ); diff --git a/migrations/2023-12-30-214938_playlists_tracks/down.sql b/migrations/2023-12-30-214938_playlists_tracks/down.sql index f4d964d..e3a2a4a 100644 --- a/migrations/2023-12-30-214938_playlists_tracks/down.sql +++ b/migrations/2023-12-30-214938_playlists_tracks/down.sql @@ -1,2 +1,2 @@ -- This file should undo anything in `up.sql` -DROP TABLE playlists_tracks; +drop table playlists_tracks; diff --git a/migrations/2023-12-30-214938_playlists_tracks/up.sql b/migrations/2023-12-30-214938_playlists_tracks/up.sql index 09c68e4..927a236 100644 --- a/migrations/2023-12-30-214938_playlists_tracks/up.sql +++ b/migrations/2023-12-30-214938_playlists_tracks/up.sql @@ -1,5 +1,6 @@ -CREATE TABLE IF NOT EXISTS playlists_tracks ( - playlist_id VARCHAR(24) REFERENCES playlists(id), - track_id VARCHAR(24) REFERENCES tracks(id), - PRIMARY KEY (playlist_id, track_id) +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, + primary key (playlist_id, track_id) ); diff --git a/migrations/2023-12-31-104738_artists/down.sql b/migrations/2023-12-31-104738_artists/down.sql new file mode 100644 index 0000000..f2582c3 --- /dev/null +++ b/migrations/2023-12-31-104738_artists/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +drop table artists; diff --git a/migrations/2023-12-31-104738_artists/up.sql b/migrations/2023-12-31-104738_artists/up.sql new file mode 100644 index 0000000..0deb8da --- /dev/null +++ b/migrations/2023-12-31-104738_artists/up.sql @@ -0,0 +1,15 @@ +-- Your SQL goes here +create table if not exists artists +( + id varchar(24) default nanoid(24), + name varchar(255) NOT NULL, + + created_at TIMESTAMP DEFAULT now() NOT NULL, + updated_at TIMESTAMP, + +-- music services + spotify_id VARCHAR(21) UNIQUE, + tidal_id VARCHAR(10) UNIQUE, + + primary key (id) +); diff --git a/migrations/2023-12-31-104741_artists_tracks/down.sql b/migrations/2023-12-31-104741_artists_tracks/down.sql new file mode 100644 index 0000000..f371df6 --- /dev/null +++ b/migrations/2023-12-31-104741_artists_tracks/down.sql @@ -0,0 +1,2 @@ +-- This file should undo anything in `up.sql` +drop table artists_tracks; diff --git a/migrations/2023-12-31-104741_artists_tracks/up.sql b/migrations/2023-12-31-104741_artists_tracks/up.sql new file mode 100644 index 0000000..b62be03 --- /dev/null +++ b/migrations/2023-12-31-104741_artists_tracks/up.sql @@ -0,0 +1,8 @@ +-- Your SQL goes here +create table if not exists artists_tracks +( + artist_id varchar(24) references artists (id) on delete cascade, + track_id varchar(24) references tracks (id) on delete cascade, + + primary key (artist_id, track_id) +); diff --git a/src/helpers/jwt.rs b/src/helpers/jwt.rs index 20eda43..96ac6d6 100644 --- a/src/helpers/jwt.rs +++ b/src/helpers/jwt.rs @@ -1,13 +1,18 @@ -use crate::utils::get_jwt_secret; -use jwt::VerifyWithKey; -use std::collections::BTreeMap; +use crate::routes::auth::JWTClaims; +use jsonwebtoken::errors::Error; +use jsonwebtoken::{decode, DecodingKey, EncodingKey, TokenData, Validation}; -pub fn get_token(token: &str) -> Result, &str> { - let secret = get_jwt_secret().unwrap(); - let claims = token.verify_with_key(&secret); +const JWT_SECRET: &str = "secret"; - match claims { - Ok(claims) => Ok(claims), - Err(_e) => return Err("Error parsing token"), - } +pub fn get_token(token: &str) -> Result, Error> { + let token = decode::(&token, &get_decoding_key(), &Validation::default())?; + Ok(token) +} + +pub fn get_encoding_key() -> EncodingKey { + EncodingKey::from_secret(JWT_SECRET.as_ref()) +} + +pub fn get_decoding_key() -> DecodingKey { + DecodingKey::from_secret(JWT_SECRET.as_ref()) } diff --git a/src/main.rs b/src/main.rs index 68b9f0a..aaa20f1 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,10 @@ +extern crate core; + mod helpers; mod middlewares; mod models; mod routes; mod schema; -mod utils; use crate::helpers::db; use actix_web::{web, App, HttpServer}; @@ -20,7 +21,7 @@ async fn main() -> std::io::Result<()> { .service(web::scope("/playlists").service(routes::playlists::get_playlist)) .service(routes::auth::login) .service(routes::me::routes()) - .service(web::scope("/users").service(routes::users::get_user)) + .service(routes::users::routes()) }) .bind(("127.0.0.1", 9000))? .run() diff --git a/src/middlewares/user.rs b/src/middlewares/user.rs index 02c5400..d4b91d4 100644 --- a/src/middlewares/user.rs +++ b/src/middlewares/user.rs @@ -9,18 +9,24 @@ pub fn get_user(req: HttpRequest) -> Result { match authorization { Some(header) => { - let claims = get_token(header.to_str().unwrap()); + let token_data = get_token(header.to_str().unwrap()); - match claims { - Ok(claims) => { - let user = Users::find(claims["user_id"].as_str())?; + match token_data { + Ok(token_data) => { + let user = Users::find(token_data.claims.user_id.as_str())?; Ok(user) } Err(e) => { - return Err(ErrorResponse { - message: e.to_string(), - status: StatusCode::INTERNAL_SERVER_ERROR, + return Err(match e.kind() { + jsonwebtoken::errors::ErrorKind::ExpiredSignature => ErrorResponse { + message: "Not Authorized".to_string(), + status: StatusCode::UNAUTHORIZED, + }, + _ => ErrorResponse { + message: e.to_string(), + status: StatusCode::INTERNAL_SERVER_ERROR, + }, }) } } diff --git a/src/models/artists.rs b/src/models/artists.rs new file mode 100644 index 0000000..0e82a11 --- /dev/null +++ b/src/models/artists.rs @@ -0,0 +1,79 @@ +use crate::helpers::db; +use crate::schema::{artists, artists_tracks}; +use chrono::NaiveDateTime; +use diesel::result::Error; +use diesel::{ + AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable, +}; +use serde::{Deserialize, Serialize}; + +#[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, + + created_at: NaiveDateTime, + updated_at: Option, + + pub spotify_id: Option, + pub tidal_id: Option, +} + +#[derive(Debug, Deserialize, Queryable, Serialize)] +pub struct Artists { + pub id: String, + pub title: String, + + created_at: NaiveDateTime, + updated_at: Option, + + pub spotify_id: Option, + pub tidal_id: Option, +} + +impl Artists { + pub fn find(id: String) -> Result { + let conn = &mut db::connection()?; + let playlist = artists::table.filter(artists::id.eq(id)).first(conn)?; + Ok(playlist) + } + + pub fn create(artist: Artist) -> Result { + let conn = &mut db::connection()?; + let playlist = diesel::insert_into(artists::table) + .values(Artist::from(artist)) + .get_result(conn)?; + Ok(playlist) + } + + pub fn find_by_track(track_id: &str) -> Result, Error> { + let conn = &mut db::connection()?; + let tracks: Vec<(String, String)> = artists_tracks::table + .filter(artists_tracks::track_id.eq(track_id)) + .get_results::<(String, String)>(conn)?; + + let artists = tracks + .into_iter() + .map(|(artist_id, _)| Artists::find(artist_id).unwrap()) + .collect::>(); + + 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, + } + } +} diff --git a/src/models/mod.rs b/src/models/mod.rs index 151f8b5..048b749 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,3 +1,4 @@ -pub mod playlist; +pub mod artists; +pub mod playlists; pub mod tracks; pub mod user; diff --git a/src/models/playlist.rs b/src/models/playlists.rs similarity index 59% rename from src/models/playlist.rs rename to src/models/playlists.rs index ed38933..5986d67 100644 --- a/src/models/playlist.rs +++ b/src/models/playlists.rs @@ -1,11 +1,11 @@ use crate::helpers::db; -use crate::models::tracks::Tracks; +use crate::models::tracks::{Track, Tracks, TracksWithArtists}; use crate::models::user::Users; use crate::schema::playlists; use chrono::NaiveDateTime; use diesel::result::Error; use diesel::{ - AsChangeset, EqAll, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable, + AsChangeset, ExpressionMethods, Insertable, QueryDsl, Queryable, RunQueryDsl, Selectable, }; use serde::{Deserialize, Serialize}; @@ -22,6 +22,7 @@ pub struct PlaylistCreator { pub struct Playlist { pub id: String, pub name: String, + pub public: bool, pub creator_id: String, pub created_at: NaiveDateTime, @@ -32,6 +33,7 @@ pub struct Playlist { pub struct Playlists { pub id: String, pub name: String, + pub public: bool, pub creator_id: String, pub created_at: NaiveDateTime, @@ -45,24 +47,36 @@ impl Playlists { Ok(playlist) } - pub fn create(playlist: Playlist) -> Result { + pub fn create(playlist: NewPlaylist) -> Result { let conn = &mut db::connection()?; let playlist = diesel::insert_into(playlists::table) - .values(Playlist::from(playlist)) + .values(playlist) .get_result(conn)?; Ok(playlist) } - pub fn find_for_user(user_id: &str) -> Result, Error> { + pub fn find_for_user(user_id: &str, filter_public: bool) -> Result, Error> { let conn = &mut db::connection()?; - let playlists = playlists::table + + let mut playlists = playlists::table .filter(playlists::creator_id.eq(user_id)) - .get_results(conn)?; + .into_boxed(); + + if filter_public { + playlists = playlists.filter(playlists::public.eq(true)); + } + + let playlists = playlists.get_results(conn)?; Ok(playlists) } - pub fn get_tracks(&self) -> Result, Error> { + pub fn get_tracks(&self) -> Result, Error> { let tracks = Tracks::find_by_playlist(&self.id)?; + let tracks: Vec = tracks + .into_iter() + .map(Track::with_artists) + .collect::, _>>()?; + Ok(tracks) } @@ -72,11 +86,28 @@ impl Playlists { } } +#[derive(Debug, Insertable, Deserialize)] +#[diesel(table_name = playlists)] +pub struct NewPlaylist { + pub name: String, + pub public: bool, + pub creator_id: Option, +} + 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()), + } + } + fn from(playlist: Playlist) -> Playlist { Playlist { id: playlist.id, name: playlist.name, + public: playlist.public, creator_id: playlist.creator_id, created_at: playlist.created_at, diff --git a/src/models/tracks.rs b/src/models/tracks.rs index 0ab5f12..a1bbdc5 100644 --- a/src/models/tracks.rs +++ b/src/models/tracks.rs @@ -1,4 +1,5 @@ use crate::helpers::db; +use crate::models::artists::Artists; use crate::schema::tracks; use chrono::NaiveDateTime; use diesel::result::Error; @@ -69,11 +70,10 @@ impl Tracks { Ok(tracks) } - // pub fn get_artist(&self) -> Result { - // let conn = &mut db::connection(); - // let artist = ; - // Ok(artist) - // } + pub fn get_artists(&self) -> Result, Error> { + let artists = Artists::find_by_track(&self.id)?; + Ok(artists) + } } impl Track { @@ -90,4 +90,31 @@ impl Track { tidal_id: track.tidal_id, } } + + pub fn with_artists(track: Tracks) -> Result { + let artists = track.get_artists()?; + + Ok(TracksWithArtists { + id: track.id, + title: track.title, + duration_ms: track.duration_ms, + + artists, + + spotify_id: track.spotify_id, + tidal_id: track.tidal_id, + }) + } +} + +#[derive(Deserialize, Serialize)] +pub struct TracksWithArtists { + pub id: String, + pub title: String, + pub duration_ms: i32, + + pub artists: Vec, + + pub spotify_id: Option, + pub tidal_id: Option, } diff --git a/src/models/user.rs b/src/models/user.rs index 2b6f873..7147ba8 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,5 +1,6 @@ use crate::helpers::db; use crate::schema::users; +use bcrypt::BcryptError; use chrono::NaiveDateTime; use diesel::result::Error; use diesel::{ @@ -54,8 +55,8 @@ impl Users { Ok(user) } - pub fn verify_password(password: &str, user: &Users) -> bool { - bcrypt::verify(password, &user.password).unwrap() + pub fn verify_password(password: &str, hash: &str) -> Result { + bcrypt::verify(password, hash) } } diff --git a/src/routes/auth.rs b/src/routes/auth.rs index 50ef939..70c446a 100644 --- a/src/routes/auth.rs +++ b/src/routes/auth.rs @@ -1,40 +1,53 @@ +use crate::helpers::jwt::get_encoding_key; use crate::middlewares::error::ErrorResponse; use crate::models::user::Users; -use crate::utils::get_jwt_secret; use actix_web::http::StatusCode; use actix_web::{post, web, HttpResponse}; -use jwt::SignWithKey; +use chrono::{Days, Utc}; +use jsonwebtoken::{encode, Header}; use serde::{Deserialize, Serialize}; -use std::collections::BTreeMap; + +#[derive(Debug, Deserialize, Serialize)] +pub struct JWTClaims { + pub user_id: String, + pub exp: usize, +} #[post("/auth/login")] async fn login(body: web::Json) -> Result { #[derive(Deserialize, Serialize)] struct Response { access_token: String, + exp: usize, } let user = Users::find_by_email(&body.email); match user { - Ok(user) => { - let password = Users::verify_password(&body.password, &user); + Ok(user) => match Users::verify_password(&body.password, &user.password) { + Ok(_res) => { + let exp = Utc::now() + .checked_add_days(Days::new(30)) + .expect("valid timestamp") + .timestamp(); - if password == false { + let claims = JWTClaims { + user_id: user.id.to_string(), + exp: exp as usize, + }; + let token = encode(&Header::default(), &claims, &get_encoding_key()).unwrap(); + + Ok(HttpResponse::Ok().json(Response { + access_token: token, + exp: exp as usize, + })) + } + Err(_e) => { return Err(ErrorResponse { message: "Invalid credentials.".to_string(), status: StatusCode::BAD_REQUEST, - }); + }) } - - let key = get_jwt_secret().unwrap(); - let mut claims = BTreeMap::new(); - claims.insert("user_id", &user.id); - let token_str = claims.sign_with_key(&key).unwrap(); - - Ok(HttpResponse::Ok().json(Response { - access_token: token_str, - })) - } + }, Err(_err) => { return Err(ErrorResponse { message: "Invalid credentials.".to_string(), diff --git a/src/routes/me.rs b/src/routes/me.rs index c654c75..4a63bf3 100644 --- a/src/routes/me.rs +++ b/src/routes/me.rs @@ -1,11 +1,14 @@ use crate::middlewares::error::ErrorResponse; use crate::middlewares::user::get_user; -use crate::models::playlist::Playlists; -use actix_web::{get, web, HttpRequest, HttpResponse, Scope}; +use crate::models::playlists::{NewPlaylist, Playlists}; +use actix_web::{get, post, web, HttpRequest, HttpResponse, Scope}; use serde::Serialize; pub fn routes() -> Scope { - web::scope("/me").service(me).service(me_playlists) + web::scope("/me") + .service(me) + .service(me_playlists) + .service(create_playlist) } #[derive(Serialize)] @@ -36,6 +39,20 @@ async fn me_playlists(req: HttpRequest) -> Result { } Ok(HttpResponse::Ok().json(Response { - playlists: Playlists::find_for_user(&user.id)?, + playlists: Playlists::find_for_user(&user.id, false)?, })) } + +#[post("/playlists")] +async fn create_playlist( + req: HttpRequest, + mut playlist: web::Json, +) -> Result { + let user = get_user(req)?; + + playlist.creator_id = Option::from(user.id); + + Playlists::create(playlist.into_inner())?; + + Ok(HttpResponse::Accepted().finish()) +} diff --git a/src/routes/playlists.rs b/src/routes/playlists.rs index 9e96167..5a12319 100644 --- a/src/routes/playlists.rs +++ b/src/routes/playlists.rs @@ -1,6 +1,6 @@ use crate::middlewares::error::ErrorResponse; -use crate::models::playlist::{PlaylistCreator, Playlists}; -use crate::models::tracks::Tracks; +use crate::models::playlists::{PlaylistCreator, Playlists}; +use crate::models::tracks::TracksWithArtists; use actix_web::{get, web, HttpResponse}; use serde::{Deserialize, Serialize}; @@ -9,7 +9,9 @@ struct GetPlaylistResponse { pub id: String, pub name: String, pub creator: PlaylistCreator, - pub tracks: Vec, + pub tracks: Vec, + pub tracks_count: usize, + pub duration: usize, } #[get("/{playlist_id}")] @@ -19,6 +21,14 @@ pub async fn get_playlist(path: web::Path) -> Result) -> Result Scope { + web::scope("/users") + .service(get_user) + .service(get_user_playlists) +} + #[derive(Serialize)] struct GetUserResponse { id: String, name: String, + cover: String, } -#[get("/{user_id}")] -async fn get_user(path: web::Path) -> Result { - let user_id = path.into_inner(); - - let user = Users::find(user_id.as_str()); +fn get_a_user(user_id: &str) -> Result { + let user = Users::find(user_id); match user { - Ok(user) => Ok(HttpResponse::Ok().json(GetUserResponse { - id: user.id, - name: user.name, - })), + Ok(user) => Ok(user), Err(DBError::NotFound) => { return Err(ErrorResponse { message: "User not found".to_string(), @@ -36,3 +38,24 @@ async fn get_user(path: web::Path) -> Result) -> Result { + let user = get_a_user(&path.into_inner())?; + let user_id = &user.id; + + Ok(HttpResponse::Ok().json(GetUserResponse { + id: user_id.to_string(), + name: user.name, + cover: format!("https://assets.vybr.net/users/{}.png", user_id), + })) +} + +#[get("/{user_id}/playlists")] +async fn get_user_playlists(path: web::Path) -> Result { + let user = get_a_user(&path.into_inner())?; + + let playlists = Playlists::find_for_user(&user.id, true)?; + + Ok(HttpResponse::Ok().json(playlists)) +} diff --git a/src/schema.rs b/src/schema.rs index 1ca147f..3df305e 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,11 +1,36 @@ // @generated automatically by Diesel CLI. +diesel::table! { + artists (id) { + #[max_length = 24] + id -> Varchar, + #[max_length = 255] + name -> Varchar, + created_at -> Timestamp, + updated_at -> Nullable, + #[max_length = 21] + spotify_id -> Nullable, + #[max_length = 10] + tidal_id -> Nullable, + } +} + +diesel::table! { + artists_tracks (artist_id, track_id) { + #[max_length = 24] + artist_id -> Varchar, + #[max_length = 24] + track_id -> Varchar, + } +} + diesel::table! { playlists (id) { #[max_length = 24] id -> Varchar, #[max_length = 255] name -> Varchar, + public -> Bool, #[max_length = 24] creator_id -> Varchar, created_at -> Timestamp, @@ -52,11 +77,15 @@ diesel::table! { } } +diesel::joinable!(artists_tracks -> artists (artist_id)); +diesel::joinable!(artists_tracks -> tracks (track_id)); diesel::joinable!(playlists -> users (creator_id)); diesel::joinable!(playlists_tracks -> playlists (playlist_id)); diesel::joinable!(playlists_tracks -> tracks (track_id)); diesel::allow_tables_to_appear_in_same_query!( + artists, + artists_tracks, playlists, playlists_tracks, tracks, diff --git a/src/utils.rs b/src/utils.rs deleted file mode 100644 index 5565013..0000000 --- a/src/utils.rs +++ /dev/null @@ -1,8 +0,0 @@ -use hmac::{Hmac, Mac}; -use sha2::Sha256; -use std::error::Error; - -pub fn get_jwt_secret() -> Result, Box> { - let key: Hmac = Hmac::new_from_slice(b"secret")?; - Ok(key) -}