diff --git a/.env.example b/.env.example index 47e599c..31e7a04 100644 --- a/.env.example +++ b/.env.example @@ -7,6 +7,5 @@ DATABASE_PASSWORD=P@$$W0RD MAIN_URL=http://localhost:3000 SERVER_PORT=8181 JWT_SECRET=jwt_secrete -ADMIN_PASSWORD=P@$$W0RD -SMTP_EMAIL=example@example.example -SMTP_PASSWORD=P@$$W0RD \ No newline at end of file +ADMIN_NAME=L0g1N +ADMIN_PASSWORD=P@SSW0RD diff --git a/.gitignore b/.gitignore index 5f32e70..836600a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target/ -.env \ No newline at end of file +.env +.idea/ diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 897e722..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ diff --git a/.idea/ExternalDeptBackend.iml b/.idea/ExternalDeptBackend.iml deleted file mode 100644 index cf84ae4..0000000 --- a/.idea/ExternalDeptBackend.iml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml deleted file mode 100644 index a55e7a1..0000000 --- a/.idea/codeStyles/codeStyleConfig.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml deleted file mode 100644 index df87cf9..0000000 --- a/.idea/encodings.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml deleted file mode 100644 index 1a79a9a..0000000 --- a/.idea/inspectionProfiles/Project_Default.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 935d22e..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 35eb1dd..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index c9a217c..07f39d2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,8 @@ dependencies = [ "serde_json", "thiserror 2.0.5", "tokio", + "utoipa", + "utoipa-swagger-ui", ] [[package]] @@ -131,6 +133,15 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -237,6 +248,15 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "blowfish" version = "0.9.1" @@ -354,6 +374,30 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.6" @@ -408,6 +452,17 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "devise" version = "0.4.2" @@ -492,6 +547,16 @@ dependencies = [ "syn", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -613,6 +678,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + [[package]] name = "fnv" version = "1.0.7" @@ -1206,6 +1281,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + [[package]] name = "log" version = "0.4.22" @@ -1269,6 +1350,16 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1841,6 +1932,40 @@ dependencies = [ "uncased", ] +[[package]] +name = "rust-embed" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d" +dependencies = [ + "sha2", + "walkdir", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -1903,6 +2028,15 @@ version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "schannel" version = "0.1.27" @@ -2007,6 +2141,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sharded-slab" version = "0.1.7" @@ -2031,6 +2176,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + [[package]] name = "simple_asn1" version = "0.6.2" @@ -2507,6 +2658,46 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utoipa" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435c6f69ef38c9017b4b4eea965dfb91e71e53d869e896db40d1cf2441dd75c0" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a77d306bc75294fd52f3e99b13ece67c02c1a2789190a6f31d32f736624326f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "utoipa-swagger-ui" +version = "7.1.1-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd1174c37d2bdc9a2ce8d94aa0baf3e3c4862f80b7b1fb592738f374ab9f8803" +dependencies = [ + "mime_guess", + "regex", + "rocket", + "rust-embed", + "serde", + "serde_json", + "url", + "utoipa", + "zip", +] + [[package]] name = "valuable" version = "0.1.0" @@ -2525,6 +2716,16 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + [[package]] name = "want" version = "0.3.1" @@ -2610,6 +2811,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" @@ -2906,3 +3116,34 @@ dependencies = [ "quote", "syn", ] + +[[package]] +name = "zip" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "arbitrary", + "crc32fast", + "crossbeam-utils", + "displaydoc", + "flate2", + "indexmap", + "memchr", + "thiserror 2.0.5", + "zopfli", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml index fc36dc0..a213ef6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,8 @@ rocket_cors = "0.6.0" chrono = { version = "0.4", features = ["serde"] } regex = "1.11.1" diesel_migrations = "2.0" +utoipa = { version = "5.1.0", features = ["chrono"] } +utoipa-swagger-ui = { version = "7.1.1-rc.0", features = ["rocket"] } + [default.databases] -postgres = { url = "{env:DATABASE_URL}" } \ No newline at end of file +postgres = { url = "{env:DATABASE_URL}" } diff --git "a/\\" "b/\\" deleted file mode 100644 index 9ab753e..0000000 --- "a/\\" +++ /dev/null @@ -1 +0,0 @@ -pub mod create_jwt; diff --git a/api b/api deleted file mode 100644 index e69de29..0000000 diff --git a/migrations/2024-12-12-214746_set_up_database/up.sql b/migrations/2024-12-12-214746_set_up_database/up.sql index 5e99107..44ffd8e 100644 --- a/migrations/2024-12-12-214746_set_up_database/up.sql +++ b/migrations/2024-12-12-214746_set_up_database/up.sql @@ -4,16 +4,13 @@ CREATE TYPE hackathon_category_2024 AS ENUM ('education', 'military', 'web3_0', CREATE TYPE type_media AS ENUM ('video', 'photo'); -- Function for Updated Timestamp -CREATE -OR REPLACE FUNCTION update_timestamp() +CREATE OR REPLACE FUNCTION update_timestamp() RETURNS TRIGGER AS $$ BEGIN - NEW.updated_at -= CURRENT_TIMESTAMP; -RETURN NEW; + NEW.updated_at = CURRENT_TIMESTAMP; + RETURN NEW; END; -$$ -LANGUAGE plpgsql; +$$ LANGUAGE plpgsql; -- University Table CREATE TABLE hackathon_university_2024 @@ -72,17 +69,15 @@ CREATE TRIGGER update_hackathon_user_updated_at EXECUTE FUNCTION update_timestamp(); -- Trigger to Increment Team Member Count -CREATE -OR REPLACE FUNCTION increment_team_member_count() +CREATE OR REPLACE FUNCTION increment_team_member_count() RETURNS TRIGGER AS $$ BEGIN -UPDATE hackathon_team_2024 -SET count_members = count_members + 1 -WHERE id = NEW.team_id; -RETURN NEW; + UPDATE hackathon_team_2024 + SET count_members = count_members + 1 + WHERE id = NEW.team_id; + RETURN NEW; END; -$$ -LANGUAGE plpgsql; +$$ LANGUAGE plpgsql; CREATE TRIGGER increment_team_member_trigger AFTER INSERT @@ -90,13 +85,31 @@ CREATE TRIGGER increment_team_member_trigger FOR EACH ROW EXECUTE FUNCTION increment_team_member_count(); +-- Trigger to Decrement Team Member Count +CREATE OR REPLACE FUNCTION decrement_team_member_count() +RETURNS TRIGGER AS $$ +BEGIN + UPDATE hackathon_team_2024 + SET count_members = count_members - 1 + WHERE id = OLD.team_id; + RETURN OLD; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER decrement_team_member_trigger + AFTER DELETE OR UPDATE OF team_id + ON hackathon_user_2024 + FOR EACH ROW + WHEN (OLD.team_id IS NOT NULL) + EXECUTE FUNCTION decrement_team_member_count(); + -- News Table CREATE TABLE news ( id SERIAL PRIMARY KEY, description TEXT NOT NULL, preview_id INT, - header VARCHAR(255) NOT NULL, + header VARCHAR(255) NOT NULL UNIQUE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); @@ -116,7 +129,8 @@ CREATE TABLE news_media type_media type_media NOT NULL, position INT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + CONSTRAINT unique_position_per_news UNIQUE (news_id, position) ); CREATE TRIGGER update_news_media_updated_at @@ -130,7 +144,7 @@ CREATE TABLE announcement_banner ( id SERIAL PRIMARY KEY, src_url TEXT NOT NULL, - type_media type_media NOT NULL, + type_media type_media NOT NULL DEFAULT 'photo', description VARCHAR(255) NOT NULL, showing BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, @@ -142,3 +156,4 @@ CREATE TRIGGER update_announcement_banner_updated_at ON announcement_banner FOR EACH ROW EXECUTE FUNCTION update_timestamp(); + diff --git a/src/api/admin/get.rs b/src/api/admin/get.rs index 342d4a0..dd1a3af 100644 --- a/src/api/admin/get.rs +++ b/src/api/admin/get.rs @@ -1,9 +1,20 @@ -use crate::models::admin::admin_jwt; -use log::info; +use crate::{middleware::admin_token_match::AdminAuthData, utils::prelude_api::*}; use rocket::get; -#[allow(dead_code)] +#[utoipa::path( + get, + path = "/api/admin/get", + operation_id = "get_admin", + tag = "Admin", + responses( + (status = 200, description = "Successfully authenticated admin"), + (status = 401, description = "Unauthorized error"), + ), + security( + ("bearer_auth" = []) + ) +)] #[get("/admin/get")] -pub async fn get(claims: admin_jwt::AdminJwt) { - info!("Welcome, user with ID: {}", claims.admin_name); +pub async fn get(admin: AdminAuthData) { + info!("Auth data admin: {:?}", admin); } diff --git a/src/api/admin/post.rs b/src/api/admin/post.rs index 941a5b2..fedfcea 100644 --- a/src/api/admin/post.rs +++ b/src/api/admin/post.rs @@ -1,12 +1,22 @@ use crate::dto::request::admin::login_admin::LoginAdminData; use crate::error::api_error::ApiError; -use crate::models::admin::admin_jwt; -use crate::utils::env_configuration::EnvConfiguration; +use crate::middleware::admin_token_match::AdminAuthData; use crate::utils::prelude_api::*; use crate::utils::security; use chrono::{Duration, Utc}; use rocket::post; +#[utoipa::path( + post, + path = "/api/admin/login", + request_body = LoginAdminData, + operation_id = "login_admin", + tag = "Admin", + responses( + (status = 200, description = "Login successfully", body = String), + (status = 422, description = "Validation error", body = ApiErrorBody), + ), +)] #[post("/admin/login", data = "")] pub async fn login(data: Json) -> Result { let data = data.into_inner(); @@ -14,26 +24,16 @@ pub async fn login(data: Json) -> Result { admin_name, admin_password, } = data; - let password_env = EnvConfiguration::get().admin_password.to_owned(); - let name_env = EnvConfiguration::get().admin_name.to_owned(); - if name_env != admin_name { - return Err(ApiError::ValidationError( - "Error validation admin name".to_string(), - )); - } - if password_env != admin_password { - return Err(ApiError::ValidationError( - "Error validation admin password".to_string(), - )); - } - let my_claims = admin_jwt::AdminJwt { + let auth = AdminAuthData { admin_password, admin_name, exp: (Utc::now() + Duration::hours(24)).timestamp() as u64, }; - match security::encoded_data(&my_claims) { + auth.check_admin()?; + + match security::encoded_data(&auth) { Ok(token) => Ok(token), Err(err) => Err(ApiError::TokenGenerationError(err.to_string())), } diff --git a/src/api/hackathon_2024/team/delete.rs b/src/api/hackathon_2024/team/delete.rs index 14bc33a..bbbd2ad 100644 --- a/src/api/hackathon_2024/team/delete.rs +++ b/src/api/hackathon_2024/team/delete.rs @@ -1,9 +1,25 @@ -use crate::diesel::prelude::*; -use crate::middleware::admin_match::AdminMatch; +use crate::utils::prelude_api::*; use rocket::delete; +#[utoipa::path( + delete, + path = "/api/hackathon_2024/team/by_id/{id}", + tag = "Hackathon Team 2024", + operation_id = "delete_team_by_id", + params( + ("id" = i32, Path, description = "ID of the team to delete") + ), + responses( + (status = 200, description = "Team updated successfully"), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] #[delete("/hackathon_2024/team/by_id/")] -pub async fn by_id(db_pool: &DbState, id: i32, admin_match: AdminMatch) -> Result<(), ApiError> { +pub async fn by_id(db_pool: &DbState, id: i32, admin_match: AdminAuthData) -> Result<(), ApiError> { admin_match.check_admin()?; let id = crate::diesel::utils::hackathon_2024::team::delete::by_id(db_pool, id)?; info!("Successfully deleted team from hackathon_team_2024 with id {id}"); diff --git a/src/api/hackathon_2024/team/get.rs b/src/api/hackathon_2024/team/get.rs index e10e5aa..65f1243 100644 --- a/src/api/hackathon_2024/team/get.rs +++ b/src/api/hackathon_2024/team/get.rs @@ -1,20 +1,40 @@ -use crate::diesel::models::hackathon_2024::team::HackathonTeam2024Queryable; +use crate::dto::response::hackathon_2024::team::{Team, VecTeam}; use crate::utils::prelude_api::*; use rocket::get; +#[utoipa::path( + get, + path = "/api/hackathon_2024/team/all", + tag = "Hackathon Team 2024", + operation_id = "get_all_team", + responses( + (status = 200, description = "All team get successfully", body = Vec), + (status = 500, description = "Database error", body = ApiErrorBody), + ), +)] #[get("/hackathon_2024/team/all")] -pub async fn all(db_pool: &DbState) -> Result>, ApiError> { - Ok(Json( +pub async fn all(db_pool: &DbState) -> Result, ApiError> { + Ok(Json(VecTeam( crate::diesel::utils::hackathon_2024::team::fetch::all(db_pool)?, - )) + ))) } +#[utoipa::path( + get, + path = "/api/hackathon_2024/team/by_id/{id}", + tag = "Hackathon Team 2024", + operation_id = "get_team_by_id", + params( + ("id" = i32, Path, description = "ID of the team to get") + ), + responses( + (status = 200, description = "Team get successfully", body = Team), + (status = 500, description = "Database error", body = ApiErrorBody), + ), +)] #[get("/hackathon_2024/team/by_id/")] -pub async fn by_id( - db_pool: &DbState, - id: i32, -) -> Result, ApiError> { - Ok(Json( +pub async fn by_id(db_pool: &DbState, id: i32) -> Result, ApiError> { + Ok(Json(Team( crate::diesel::utils::hackathon_2024::team::fetch::by_id(db_pool, id)?, - )) + ))) } diff --git a/src/api/hackathon_2024/team/post.rs b/src/api/hackathon_2024/team/post.rs index 5db883d..503e3dd 100644 --- a/src/api/hackathon_2024/team/post.rs +++ b/src/api/hackathon_2024/team/post.rs @@ -3,6 +3,18 @@ use crate::utils::prelude_api::*; use crate::utils::security::hashing_data; use rocket::post; +#[utoipa::path( + post, + path = "/api/hackathon_2024/team/create", + request_body = TeamCreateData, + tag = "Hackathon Team 2024", + operation_id = "create_team", + responses( + (status = 200, description = "Team created successfully"), + (status = 422, description = "Validation error", body = ApiErrorBody), + (status = 500, description = "Database error", body = ApiErrorBody), + ), +)] #[post("/hackathon_2024/team/create", data = "")] pub async fn create(db_pool: &DbState, data: Json) -> Result<(), ApiError> { let mut team = data.into_inner().0; diff --git a/src/api/hackathon_2024/team/put.rs b/src/api/hackathon_2024/team/put.rs index e1c5b52..d2fd0ea 100644 --- a/src/api/hackathon_2024/team/put.rs +++ b/src/api/hackathon_2024/team/put.rs @@ -1,13 +1,30 @@ use crate::dto::request::hackathon_2024::team::TeamUpdateData; -use crate::middleware::admin_match::AdminMatch; use crate::utils::prelude_api::*; use rocket::put; +#[utoipa::path( + put, + path = "/api/hackathon_2024/team/by_id", + tag = "Hackathon Team 2024", + request_body = TeamUpdateData, + operation_id = "put_team_by_id", + params( + ("id" = i32, Path, description = "ID of the team to update") + ), + responses( + (status = 200, description = "Team updated successfully"), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] #[put("/hackathon_2024/team/by_id", data = "")] pub async fn by_id( db_pool: &DbState, data: Json, - admin_match: AdminMatch, + admin_match: AdminAuthData, ) -> Result<(), ApiError> { admin_match.check_admin()?; let data = data.into_inner(); diff --git a/src/api/hackathon_2024/university/delete.rs b/src/api/hackathon_2024/university/delete.rs index cd0bde3..e33f5a9 100644 --- a/src/api/hackathon_2024/university/delete.rs +++ b/src/api/hackathon_2024/university/delete.rs @@ -1,9 +1,26 @@ -use crate::middleware::admin_match::AdminMatch; use crate::utils::prelude_api::*; use rocket::delete; +#[utoipa::path( + delete, + path = "/api/hackathon_2024/university/by_id/{id}", + tag = "Hackathon University 2024", + operation_id = "delete_university_by_id", + responses( + (status = 200, description = "University deleted successfully"), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + operation_id = "delete_university_by_id", + params( + ("id" = i32, Path, description = "ID of the university to delete") + ), + security( + ("bearer_auth" = []) + ) +)] #[delete("/hackathon_2024/university/by_id/")] -pub async fn by_id(db_pool: &DbState, id: i32, admin_match: AdminMatch) -> Result<(), ApiError> { +pub async fn by_id(db_pool: &DbState, id: i32, admin_match: AdminAuthData) -> Result<(), ApiError> { admin_match.check_admin()?; let id = crate::diesel::utils::hackathon_2024::university::delete::by_id(db_pool, id)?; info!("Successfully deleted university from hackathon_university_2024 with id {id}"); diff --git a/src/api/hackathon_2024/university/get.rs b/src/api/hackathon_2024/university/get.rs index 817785a..e548b5f 100644 --- a/src/api/hackathon_2024/university/get.rs +++ b/src/api/hackathon_2024/university/get.rs @@ -2,6 +2,16 @@ use crate::dto::response::hackathon_2024::university::{University, VecUniversity use crate::utils::prelude_api::*; use rocket::get; +#[utoipa::path( + get, + path = "/api/hackathon_2024/university/all", + tag = "Hackathon University 2024", + operation_id = "get_all_university", + responses( + (status = 200, description = "All university fetched successfully", body = Vec), + (status = 500, description = "Database error", body = ApiErrorBody), + ), +)] #[get("/hackathon_2024/university/all")] pub async fn all(db_pool: &DbState) -> Result, ApiError> { Ok(Json(VecUniversity( @@ -9,6 +19,20 @@ pub async fn all(db_pool: &DbState) -> Result, ApiError> { ))) } +#[utoipa::path( + get, + path = "/api/hackathon_2024/university/by_id/{id}", + tag = "Hackathon University 2024", + operation_id = "get_university_by_id", + params( + ("id" = i32, Path, description = "ID of the university to fetch") + ), + responses( + (status = 200, description = "Single university fetched by id successfully", body = University), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error"), + ), +)] #[get("/hackathon_2024/university/by_id/")] pub async fn by_id(db_pool: &DbState, id: i32) -> Result, ApiError> { Ok(Json(University( diff --git a/src/api/hackathon_2024/university/post.rs b/src/api/hackathon_2024/university/post.rs index 17c2514..931f891 100644 --- a/src/api/hackathon_2024/university/post.rs +++ b/src/api/hackathon_2024/university/post.rs @@ -1,13 +1,26 @@ -use crate::dto::request::hackathon_2024::university::University; -use crate::middleware::admin_match::AdminMatch; +use crate::dto::request::hackathon_2024::university::{University, VecUniversity}; use crate::utils::prelude_api::*; use rocket::post; +#[utoipa::path( + post, + path = "/api/hackathon_2024/university/create", + request_body = University, tag = "Hackathon University 2024", + operation_id = "create_university", + responses( + (status = 200, description = "University created successfully"), + (status = 401, description = "Unauthorized error", body = ApiErrorBody), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] #[post("/hackathon_2024/university/create", data = "")] pub async fn create( db_pool: &DbState, data: Json, - admin_match: AdminMatch, + admin_match: AdminAuthData, ) -> Result<(), ApiError> { admin_match.check_admin()?; let id = crate::diesel::utils::hackathon_2024::university::insert::new( @@ -17,3 +30,32 @@ pub async fn create( info!("Succeed insert new university with id - {id}"); Ok(()) } + +#[utoipa::path( + post, + path = "/api/hackathon_2024/university/create_by_vec", + request_body = Vec, tag = "Hackathon University 2024", + operation_id = "create_university_by_vec", + responses( + (status = 200, description = "Vec university created successfully"), + (status = 401, description = "Unauthorized error", body = ApiErrorBody), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] +#[post("/hackathon_2024/university/create_by_vec", data = "")] +pub async fn create_by_vec( + db_pool: &DbState, + data: Json, + admin_match: AdminAuthData, +) -> Result<(), ApiError> { + admin_match.check_admin()?; + let id = crate::diesel::utils::hackathon_2024::university::insert::new_by_vec( + db_pool, + data.into_inner().0, + )?; + info!("Succeed insert new university with id - {id}"); + Ok(()) +} diff --git a/src/api/hackathon_2024/university/put.rs b/src/api/hackathon_2024/university/put.rs index 6a6e0fe..b8f32b8 100644 --- a/src/api/hackathon_2024/university/put.rs +++ b/src/api/hackathon_2024/university/put.rs @@ -1,13 +1,29 @@ use crate::dto::request::hackathon_2024::university::University; -use crate::middleware::admin_match::AdminMatch; use crate::utils::prelude_api::*; use rocket::put; +#[utoipa::path( + put, + path = "/api/hackathon_2024/university/by_id/{id}", + request_body = University, tag = "Hackathon University 2024", + operation_id = "put_university_by_id", + params( + ("id" = i32, Path, description = "ID of the university to update") + ), + responses( + (status = 200, description = "University updated successfully"), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] #[put("/hackathon_2024/university/by_id/", data = "")] pub async fn by_id( db_pool: &DbState, id: i32, - admin_match: AdminMatch, + admin_match: AdminAuthData, data: Json, ) -> Result<(), ApiError> { admin_match.check_admin()?; diff --git a/src/api/hackathon_2024/user/delete.rs b/src/api/hackathon_2024/user/delete.rs index de1c3c9..3d5deb9 100644 --- a/src/api/hackathon_2024/user/delete.rs +++ b/src/api/hackathon_2024/user/delete.rs @@ -1,14 +1,28 @@ -use crate::diesel::configurator::DbPool; -use crate::error::api_error::ApiError; -use crate::middleware::admin_match::AdminMatch; -use log::info; -use rocket::{delete, State}; +use crate::utils::prelude_api::*; +use rocket::delete; +#[utoipa::path( + delete, + path = "/api/hackathon_2024/user/by_id/{user_id}", + tag = "Hackathon User 2024", + operation_id = "update_user_by_id", + params( + ("id" = i32, Path, description = "ID of the user to delete") + ), + responses( + (status = 200, description = "User deleted successfully"), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] #[delete("/hackathon_2024/user/by_id/")] pub async fn by_id( - db_pool: &State, + db_pool: &DbState, user_id: i32, - admin_match: AdminMatch, + admin_match: AdminAuthData, ) -> Result<(), ApiError> { admin_match.check_admin()?; let user_id = crate::diesel::utils::hackathon_2024::user::delete::by_id(db_pool, user_id)?; diff --git a/src/api/hackathon_2024/user/get.rs b/src/api/hackathon_2024/user/get.rs index 17de422..9104ea1 100644 --- a/src/api/hackathon_2024/user/get.rs +++ b/src/api/hackathon_2024/user/get.rs @@ -1,7 +1,6 @@ use crate::api::hackathon_2024::user::local::create_user_by_jwt; use crate::dto::response::hackathon_2024::user::User; use crate::dto::response::hackathon_2024::user::VecUser; -use crate::middleware::admin_match::AdminMatch; use crate::middleware::claims::Claims; use crate::utils::prelude_api::*; use crate::utils::security::decoded_data; @@ -14,6 +13,19 @@ pub async fn confirm_new_user(db_pool: &DbState, jwt_token: String) -> Result<() } #[allow(dead_code)] +#[utoipa::path( + get, + path = "/api/hackathon_2024/user/authorization_user", + tag = "Hackathon User 2024", + operation_id = "get_authorization_user", + responses( + (status = 200, description = "User fetched successfully", body = User), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] #[get("/hackathon_2024/user/authorization_user")] pub async fn authorization_user(db_pool: &DbState, claims: Claims) -> Result, ApiError> { Ok(Json(User( @@ -21,10 +33,105 @@ pub async fn authorization_user(db_pool: &DbState, claims: Claims) -> Result), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] #[get("/hackathon_2024/user/all")] -pub async fn all(db_pool: &DbState, admin_match: AdminMatch) -> Result, ApiError> { +pub async fn all(db_pool: &DbState, admin_match: AdminAuthData) -> Result, ApiError> { admin_match.check_admin()?; Ok(Json(VecUser( crate::diesel::utils::hackathon_2024::user::fetch::all(db_pool)?, ))) } + +#[utoipa::path( + get, + path = "/api/hackathon_2024/user/by_id/{id}", + tag = "Hackathon User 2024", + operation_id = "get_user_by_id", + params( + ("id" = i32, Path, description = "ID of the user to fetch") + ), + responses( + (status = 200, description = "User fetched successfully", body = User), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] +#[get("/hackathon_2024/user/by_id/")] +pub async fn by_id( + db_pool: &DbState, + id: i32, + admin_match: AdminAuthData, +) -> Result, ApiError> { + admin_match.check_admin()?; + Ok(Json(User( + crate::diesel::utils::hackathon_2024::user::fetch::by_id(db_pool, id)?, + ))) +} + +#[utoipa::path( + get, + path = "/api/hackathon_2024/user/by_university/{id}", + tag = "Hackathon User 2024", + operation_id = "get_user_by_university", + params( + ("id" = i32, Path, description = "ID of the user`s university to fetch") + ), + responses( + (status = 200, description = "User fetched successfully", body = Vec), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] +#[get("/hackathon_2024/user/by_university/")] +pub async fn by_university( + db_pool: &DbState, + id: i32, + admin_match: AdminAuthData, +) -> Result, ApiError> { + admin_match.check_admin()?; + Ok(Json(VecUser( + crate::diesel::utils::hackathon_2024::user::fetch::by_university(db_pool, id)?, + ))) +} + +#[utoipa::path( + get, + path = "/api/hackathon_2024/user/by_team/{id}", + tag = "Hackathon User 2024", + operation_id = "get_user_by_team", + params( + ("id" = i32, Path, description = "ID of the user`s team to fetch") + ), + responses( + (status = 200, description = "User fetched successfully", body = Vec), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] +#[get("/hackathon_2024/user/by_team/")] +pub async fn by_team(db_pool: &DbState, id: i32) -> Result, ApiError> { + Ok(Json(VecUser( + crate::diesel::utils::hackathon_2024::user::fetch::by_team(db_pool, id)?, + ))) +} diff --git a/src/api/hackathon_2024/user/post.rs b/src/api/hackathon_2024/user/post.rs index 5a79856..56a5958 100644 --- a/src/api/hackathon_2024/user/post.rs +++ b/src/api/hackathon_2024/user/post.rs @@ -4,6 +4,18 @@ use crate::utils::security::verify_password; use crate::utils::validation; use rocket::post; +#[utoipa::path( + post, + path = "/api/hackathon_2024/user/registration_by_tg", + request_body = RegistrationData, + tag = "Hackathon User 2024", + operation_id = "user_registration_by_tg", + responses( + (status = 200, description = "User registration successfully"), + (status = 422, description = "Validation error", body = ApiErrorBody), + (status = 500, description = "Database error", body = ApiErrorBody), + ), +)] #[post("/hackathon_2024/user/registration_by_tg", data = "")] pub async fn registration_by_tg( db_pool: &DbState, diff --git a/src/api/hackathon_2024/user/put.rs b/src/api/hackathon_2024/user/put.rs index 9a3ff78..3b47c96 100644 --- a/src/api/hackathon_2024/user/put.rs +++ b/src/api/hackathon_2024/user/put.rs @@ -1,17 +1,35 @@ use crate::dto::request::hackathon_2024::user::User; -use crate::middleware::admin_match::AdminMatch; use crate::utils::prelude_api::*; use rocket::put; -#[put("/hackathon_2024/user/by_id/", data = "")] + +#[utoipa::path( + put, + path = "/api/hackathon_2024/user/by_id/{id}", + request_body = User, + tag = "Hackathon User 2024", + operation_id = "update_user_by_id", + params( + ("id" = i32, Path, description = "ID of the user to update") + ), + responses( + (status = 200, description = "User updated successfully", body = String), + (status = 401, description = "Unauthorized error"), + (status = 500, description = "Database error", body = ApiErrorBody), + ), + security( + ("bearer_auth" = []) + ) +)] +#[put("/hackathon_2024/user/by_id/", data = "")] pub async fn by_id( db_pool: &DbState, data: Json, - user_id: i32, - admin_match: AdminMatch, + id: i32, + admin_match: AdminAuthData, ) -> Result { admin_match.check_admin()?; let data = data.into_inner(); - crate::diesel::utils::hackathon_2024::user::update::by_id(db_pool, user_id, &data.0)?; + crate::diesel::utils::hackathon_2024::user::update::by_id(db_pool, id, &data.0)?; Ok(format!( "Successfully updated hackathon_user_2024 with email: {}", data.0.nickname_tg diff --git a/src/api/test/get.rs b/src/api/test/get.rs index 2e5dbb7..b23866a 100644 --- a/src/api/test/get.rs +++ b/src/api/test/get.rs @@ -1,7 +1,14 @@ use rocket::get; use rocket::serde::json::Json; -#[allow(dead_code)] +#[utoipa::path( + get, + tag = "Test", + path = "/api/test/ping", + responses( + (status = 200, description = "Ping the server to check if it's running", body = String) + ) +)] #[get("/test/ping")] pub async fn ping() -> Json<&'static str> { Json("pong") diff --git a/src/diesel/configurator.rs b/src/diesel/configurator.rs index 373411a..acfcb00 100644 --- a/src/diesel/configurator.rs +++ b/src/diesel/configurator.rs @@ -1,3 +1,4 @@ +use super::prelude::DbState; use crate::error::api_error::ApiError; use crate::utils::constants::diesel::MIGRATIONS; use crate::utils::env_configuration::EnvConfiguration; @@ -6,7 +7,6 @@ use diesel::r2d2::{ConnectionManager, Pool, PooledConnection}; use diesel::sql_types::Text; use diesel::RunQueryDsl; use diesel_migrations::MigrationHarness; -use rocket::State; pub type DbPool = Pool>; @@ -73,7 +73,7 @@ pub fn configuration_database() -> DbPool { } pub fn get_connection( - db_pool: &State, + db_pool: &DbState, ) -> Result>, ApiError> { db_pool .get() diff --git a/src/diesel/models/hackathon_2024/category.rs b/src/diesel/models/hackathon_2024/category.rs index 06ddd54..19e1bf0 100644 --- a/src/diesel/models/hackathon_2024/category.rs +++ b/src/diesel/models/hackathon_2024/category.rs @@ -11,7 +11,18 @@ use serialize::IsNull; use std::fmt::Debug; use std::io::Write; -#[derive(Debug, Clone, Copy, PartialEq, Eq, FromSqlRow, AsExpression, Deserialize, Serialize)] +#[derive( + Debug, + Clone, + Copy, + PartialEq, + Eq, + FromSqlRow, + AsExpression, + Deserialize, + Serialize, + utoipa::ToSchema, +)] #[diesel(sql_type = crate::diesel::schema::sql_types::HackathonCategory2024)] pub enum HackathonCategory2024Enum { Education, diff --git a/src/diesel/models/hackathon_2024/team.rs b/src/diesel/models/hackathon_2024/team.rs index c9dedea..1b62145 100644 --- a/src/diesel/models/hackathon_2024/team.rs +++ b/src/diesel/models/hackathon_2024/team.rs @@ -3,36 +3,36 @@ use chrono::NaiveDateTime; use diesel::{Insertable, Queryable}; use serde::{Deserialize, Serialize}; -#[derive(Debug, Queryable, Serialize)] +#[derive(Debug, Queryable, Serialize, utoipa::ToSchema)] #[diesel(table_name = crate::diesel::schema::hackathon_team_2024)] pub struct HackathonTeam2024Queryable { - #[allow(dead_code)] + #[schema(example = "1")] pub id: i32, - #[allow(dead_code)] + #[schema(example = "Team 1")] pub name: String, - #[allow(dead_code)] + #[schema(example = "Education")] pub category: HackathonCategory2024Enum, - #[allow(dead_code)] + #[schema(example = "$2b$12$9pK8ha/nYGLwRWG53UK/EuOlGYJjS/aXPCPKxdX3o7UfiuQrtFDiq")] pub password_registration: String, - #[allow(dead_code)] + #[schema(example = "2")] pub count_members: i32, - #[allow(dead_code)] - pub email: String, - #[allow(dead_code)] + #[schema(example = "skalse_456")] + pub nickname_tg: String, + #[schema(example = "2025-01-19T15:06:19.027744")] pub created_at: Option, - #[allow(dead_code)] + #[schema(example = "2025-01-19T15:06:19.027744")] pub updated_at: Option, } -#[derive(Insertable, Debug, Deserialize, Serialize)] +#[derive(Insertable, Debug, Deserialize, Serialize, utoipa::ToSchema)] #[diesel(table_name = crate::diesel::schema::hackathon_team_2024)] pub struct HackathonTeam2024Insertable { - #[allow(dead_code)] + #[schema(example = "Team 1")] pub name: String, - #[allow(dead_code)] + #[schema(example = "Education")] pub category: HackathonCategory2024Enum, - #[allow(dead_code)] + #[schema(example = "real_password123!AAA")] pub password_registration: String, - #[allow(dead_code)] + #[schema(example = "skalse_456")] pub nickname_tg: String, } diff --git a/src/diesel/models/hackathon_2024/university.rs b/src/diesel/models/hackathon_2024/university.rs index 6ff5322..a2251a9 100644 --- a/src/diesel/models/hackathon_2024/university.rs +++ b/src/diesel/models/hackathon_2024/university.rs @@ -1,22 +1,24 @@ use chrono::NaiveDateTime; use diesel::{Insertable, Queryable}; use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; -#[derive(Debug, Queryable, Serialize)] +#[derive(Debug, Queryable, Serialize, ToSchema)] #[diesel(table_name = crate::diesel::schema::hackathon_university_2024)] pub struct HackathonUniversity2024Queryable { - #[allow(dead_code)] + #[schema(example = "1")] pub id: i32, - #[allow(dead_code)] + #[schema(example = "Innovation University")] pub name: String, - #[allow(dead_code)] + #[schema(example = "2025-01-19T15:06:19.027744")] pub created_at: Option, - #[allow(dead_code)] + #[schema(example = "2025-01-19T15:06:19.027744")] pub updated_at: Option, } -#[derive(Insertable, Debug, Deserialize, Serialize)] +#[derive(Insertable, Debug, Deserialize, Serialize, ToSchema)] #[diesel(table_name = crate::diesel::schema::hackathon_university_2024)] pub struct HackathonUniversity2024Insertable { + #[schema(example = "Innovation University")] pub name: String, } diff --git a/src/diesel/models/hackathon_2024/user.rs b/src/diesel/models/hackathon_2024/user.rs index d895d78..18a7894 100644 --- a/src/diesel/models/hackathon_2024/user.rs +++ b/src/diesel/models/hackathon_2024/user.rs @@ -1,37 +1,44 @@ use chrono::NaiveDateTime; use diesel::{Insertable, Queryable}; use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; -#[derive(Debug, Queryable, Serialize)] +#[derive(Debug, Queryable, Serialize, ToSchema)] #[diesel(table_name = crate::diesel::schema::hackathon_user_2024)] pub struct HackathonUser2024Queryable { - #[allow(dead_code)] + #[schema(example = "1")] pub id: i32, - #[allow(dead_code)] + #[schema(example = "Skalse")] pub first_name: String, - #[allow(dead_code)] + #[schema(example = "Batya")] pub last_name: String, - #[allow(dead_code)] + #[schema(example = "skalse_456")] pub nickname_tg: String, - #[allow(dead_code)] + #[schema(example = "1234567890")] pub phone: String, - #[allow(dead_code)] + #[schema(example = "1")] pub university_id: Option, - #[allow(dead_code)] + #[schema(example = "1")] pub team_id: Option, - #[allow(dead_code)] + #[schema(example = "2025-01-19T15:06:19.027744")] pub created_at: Option, - #[allow(dead_code)] + #[schema(example = "2025-01-19T15:06:19.027744")] pub updated_at: Option, } -#[derive(Insertable, Debug, Deserialize, Serialize)] +#[derive(Insertable, Debug, Deserialize, Serialize, ToSchema)] #[diesel(table_name = crate::diesel::schema::hackathon_user_2024)] pub struct HackathonUser2024Insertable { + #[schema(example = "Skalse")] pub first_name: String, + #[schema(example = "Batya")] pub last_name: String, + #[schema(example = "skalse_456")] pub nickname_tg: String, + #[schema(example = "+380123456789")] pub phone: String, + #[schema(example = "1")] pub university_id: i32, + #[schema(example = "1")] pub team_id: i32, } diff --git a/src/diesel/prelude.rs b/src/diesel/prelude.rs index 8009d47..b481818 100644 --- a/src/diesel/prelude.rs +++ b/src/diesel/prelude.rs @@ -5,6 +5,5 @@ pub use diesel::ExpressionMethods; pub use diesel::QueryDsl; pub use diesel::RunQueryDsl; pub use log::error; -pub use log::info; pub use rocket::State; pub type DbState = State; diff --git a/src/diesel/utils/hackathon_2024/team/delete.rs b/src/diesel/utils/hackathon_2024/team/delete.rs index 740646d..31d6a07 100644 --- a/src/diesel/utils/hackathon_2024/team/delete.rs +++ b/src/diesel/utils/hackathon_2024/team/delete.rs @@ -10,6 +10,6 @@ pub fn by_id(db_pool: &State, id: i32) -> Result { .execute(&mut get_connection(db_pool)?) .map_err(|err| { error!("Error deleting hackathon_university_2024 with id {id}"); - ApiError::DatabaseErrorResult(err) + err.into() }) } diff --git a/src/diesel/utils/hackathon_2024/team/fetch.rs b/src/diesel/utils/hackathon_2024/team/fetch.rs index 28bd95a..32a695f 100644 --- a/src/diesel/utils/hackathon_2024/team/fetch.rs +++ b/src/diesel/utils/hackathon_2024/team/fetch.rs @@ -1,14 +1,16 @@ use crate::diesel::models::hackathon_2024::team::HackathonTeam2024Queryable; use crate::diesel::prelude::*; use crate::diesel::schema::hackathon_team_2024::dsl::hackathon_team_2024; + pub fn by_id(db_pool: &DbState, team_id: i32) -> Result { hackathon_team_2024 .filter(crate::diesel::schema::hackathon_team_2024::columns::id.eq(team_id)) .first::(&mut get_connection(db_pool)?) - .map_err(ApiError::DatabaseErrorResult) + .map_err(|err| err.into()) } + pub fn all(db_pool: &State) -> Result, ApiError> { hackathon_team_2024 .load::(&mut get_connection(db_pool)?) - .map_err(ApiError::DatabaseErrorResult) + .map_err(|err| err.into()) } diff --git a/src/diesel/utils/hackathon_2024/team/insert.rs b/src/diesel/utils/hackathon_2024/team/insert.rs index 7911dbd..8e93945 100644 --- a/src/diesel/utils/hackathon_2024/team/insert.rs +++ b/src/diesel/utils/hackathon_2024/team/insert.rs @@ -9,6 +9,6 @@ pub fn new(db_pool: &DbState, data: HackathonTeam2024Insertable) -> Result(&mut db_connection) .map_err(|err| { error!("Error inserting hackathon 2024 team with id - {:?}", err); - ApiError::DatabaseErrorResult(err) + err.into() }) } diff --git a/src/diesel/utils/hackathon_2024/team/update.rs b/src/diesel/utils/hackathon_2024/team/update.rs index a71f78e..6f2d45c 100644 --- a/src/diesel/utils/hackathon_2024/team/update.rs +++ b/src/diesel/utils/hackathon_2024/team/update.rs @@ -18,6 +18,6 @@ pub fn by_data(db_pool: &DbState, data: &TeamUpdateData) -> Result Result { - diesel::delete( - hackathon_university_2024 - .filter(crate::diesel::schema::hackathon_university_2024::id.eq(id)), - ) - .execute(&mut get_connection(db_pool)?) - .map_err(|err| { - log::error!("Error deleting university from hackathon_university_2024 with id {id}"); - ApiError::DatabaseErrorResult(err) - }) +pub fn by_id(db_pool: &DbState, delete_id: i32) -> Result { + diesel::delete(hackathon_university_2024.filter(id.eq(delete_id))) + .returning(id) + .get_result(&mut get_connection(db_pool)?) + .map_err(|err| { + error!("Error deleting university from hackathon_university_2024 with id {delete_id}"); + err.into() + }) } diff --git a/src/diesel/utils/hackathon_2024/university/fetch.rs b/src/diesel/utils/hackathon_2024/university/fetch.rs index 28155ed..eaf0dac 100644 --- a/src/diesel/utils/hackathon_2024/university/fetch.rs +++ b/src/diesel/utils/hackathon_2024/university/fetch.rs @@ -1,20 +1,22 @@ use crate::diesel::models::hackathon_2024::university::HackathonUniversity2024Queryable; use crate::diesel::prelude::*; use crate::diesel::schema::hackathon_university_2024::dsl::hackathon_university_2024; + pub fn by_id(db_pool: &DbState, id: i32) -> Result { hackathon_university_2024 .filter(crate::diesel::schema::hackathon_university_2024::columns::id.eq(id)) .first::(&mut get_connection(db_pool)?) .map_err(|err| { error!("Error to get hackathon_university_2024 with id {id}"); - ApiError::DatabaseErrorResult(err) + err.into() }) } + pub fn all(db_pool: &State) -> Result, ApiError> { hackathon_university_2024 .load::(&mut get_connection(db_pool)?) .map_err(|err| { error!("Error to get all hackathon_university_2024"); - ApiError::DatabaseErrorResult(err) + err.into() }) } diff --git a/src/diesel/utils/hackathon_2024/university/insert.rs b/src/diesel/utils/hackathon_2024/university/insert.rs index b7d10da..2233fbd 100644 --- a/src/diesel/utils/hackathon_2024/university/insert.rs +++ b/src/diesel/utils/hackathon_2024/university/insert.rs @@ -15,6 +15,24 @@ pub fn new( "Error inserting hackathon 2024 university with id - {:?}", err ); - ApiError::DatabaseErrorResult(err) + err.into() + }) +} + +pub fn new_by_vec( + db_pool: &State, + data: Vec, +) -> Result { + let mut db_connection = get_connection(db_pool)?; + diesel::insert_into(crate::diesel::schema::hackathon_university_2024::table) + .values(data) + .returning(crate::diesel::schema::hackathon_university_2024::id) + .get_result::(&mut db_connection) + .map_err(|err| { + error!( + "Error inserting hackathon 2024 university with id - {:?}", + err + ); + err.into() }) } diff --git a/src/diesel/utils/hackathon_2024/university/update.rs b/src/diesel/utils/hackathon_2024/university/update.rs index dfa9069..72d0fbd 100644 --- a/src/diesel/utils/hackathon_2024/university/update.rs +++ b/src/diesel/utils/hackathon_2024/university/update.rs @@ -7,15 +7,16 @@ pub fn by_id( db_pool: &State, university_id: i32, data: HackathonUniversity2024Insertable, -) -> Result { +) -> Result { diesel::update(hackathon_university_2024.filter(id.eq(university_id))) .set(( name.eq(data.name), updated_at.eq(chrono::Utc::now().naive_utc()), )) - .execute(&mut get_connection(db_pool)?) + .returning(id) + .get_result(&mut get_connection(db_pool)?) .map_err(|err| { error!("Error updating hackathon_university_2024"); - ApiError::DatabaseErrorResult(err) + err.into() }) } diff --git a/src/diesel/utils/hackathon_2024/user/delete.rs b/src/diesel/utils/hackathon_2024/user/delete.rs index d5b4d76..1965ec5 100644 --- a/src/diesel/utils/hackathon_2024/user/delete.rs +++ b/src/diesel/utils/hackathon_2024/user/delete.rs @@ -2,11 +2,11 @@ use crate::diesel::prelude::*; use crate::diesel::schema::hackathon_user_2024::dsl::hackathon_user_2024; use crate::diesel::schema::hackathon_user_2024::id; -pub fn by_id(db_pool: &State, user_id: i32) -> Result { +pub fn by_id(db_pool: &DbState, user_id: i32) -> Result { diesel::delete(hackathon_user_2024.filter(id.eq(user_id))) .execute(&mut get_connection(db_pool)?) .map_err(|err| { error!("Error deleting hackathon_user_2024"); - ApiError::DatabaseErrorResult(err) + err.into() }) } diff --git a/src/diesel/utils/hackathon_2024/user/fetch.rs b/src/diesel/utils/hackathon_2024/user/fetch.rs index 89ef0cb..d2eb3e5 100644 --- a/src/diesel/utils/hackathon_2024/user/fetch.rs +++ b/src/diesel/utils/hackathon_2024/user/fetch.rs @@ -1,16 +1,37 @@ use crate::diesel::models::hackathon_2024::user::HackathonUser2024Queryable; use crate::diesel::prelude::*; use crate::diesel::schema::hackathon_user_2024::dsl::hackathon_user_2024; -use crate::diesel::schema::hackathon_user_2024::id; +use crate::diesel::schema::hackathon_user_2024::{id, team_id, university_id}; pub fn all(db_pool: &DbState) -> Result, ApiError> { hackathon_user_2024 .load::(&mut get_connection(db_pool)?) - .map_err(ApiError::DatabaseErrorResult) + .map_err(|err| err.into()) } -pub fn by_id(db_pool: &DbState, user_id: i32) -> Result { + +pub fn by_id(db_pool: &DbState, path_id: i32) -> Result { hackathon_user_2024 - .filter(id.eq(user_id)) + .filter(id.eq(path_id)) .first::(&mut get_connection(db_pool)?) - .map_err(ApiError::DatabaseErrorResult) + .map_err(|err| err.into()) +} + +pub fn by_university( + db_pool: &DbState, + path_id: i32, +) -> Result, ApiError> { + hackathon_user_2024 + .filter(university_id.eq(path_id)) + .load::(&mut get_connection(db_pool)?) + .map_err(|err| err.into()) +} + +pub fn by_team( + db_pool: &DbState, + path_id: i32, +) -> Result, ApiError> { + hackathon_user_2024 + .filter(team_id.eq(path_id)) + .load::(&mut get_connection(db_pool)?) + .map_err(|err| err.into()) } diff --git a/src/diesel/utils/hackathon_2024/user/insert.rs b/src/diesel/utils/hackathon_2024/user/insert.rs index 5be4902..8794837 100644 --- a/src/diesel/utils/hackathon_2024/user/insert.rs +++ b/src/diesel/utils/hackathon_2024/user/insert.rs @@ -8,6 +8,6 @@ pub fn new(db_pool: &DbState, data: HackathonUser2024Insertable) -> Result(&mut get_connection(db_pool)?) .map_err(|err| { error!("Error inserting hackathon_user_2024"); - ApiError::DatabaseErrorResult(err) + err.into() }) } diff --git a/src/diesel/utils/hackathon_2024/user/update.rs b/src/diesel/utils/hackathon_2024/user/update.rs index 4e898ba..e2c909d 100644 --- a/src/diesel/utils/hackathon_2024/user/update.rs +++ b/src/diesel/utils/hackathon_2024/user/update.rs @@ -23,6 +23,6 @@ pub fn by_id( .execute(&mut get_connection(dp_pool)?) .map_err(|err| { error!("Error updating hackathon_user_2024, bellow error"); - ApiError::DatabaseErrorResult(err) + err.into() }) } diff --git a/src/dto/request/admin/login_admin.rs b/src/dto/request/admin/login_admin.rs index 4966832..fe27436 100644 --- a/src/dto/request/admin/login_admin.rs +++ b/src/dto/request/admin/login_admin.rs @@ -1,7 +1,9 @@ use serde::Deserialize; -#[derive(Deserialize)] +#[derive(Deserialize, utoipa::ToSchema)] pub struct LoginAdminData { + #[schema(example = "P@SSW0RD")] pub admin_password: String, + #[schema(example = "L0g1N")] pub admin_name: String, } diff --git a/src/dto/request/hackathon_2024/team.rs b/src/dto/request/hackathon_2024/team.rs index b55ab6c..8734771 100644 --- a/src/dto/request/hackathon_2024/team.rs +++ b/src/dto/request/hackathon_2024/team.rs @@ -1,19 +1,28 @@ use crate::diesel::models::hackathon_2024::category::HackathonCategory2024Enum; use crate::diesel::models::hackathon_2024::team::HackathonTeam2024Insertable; use serde::Deserialize; +use utoipa::ToSchema; -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct TeamRegistrationData { + #[schema(example = "1")] pub id: i32, + #[schema(example = "real_password123!AAA")] pub password: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, ToSchema)] pub struct TeamUpdateData { + #[schema(example = "1")] pub id: i32, + #[schema(example = "Team 2")] pub name: String, + #[schema(example = "Military")] pub category: HackathonCategory2024Enum, + #[schema(example = "bredovschik")] pub nickname_tg: String, } -#[derive(Debug, Deserialize)] + +#[derive(Debug, Deserialize, ToSchema)] +#[schema(title = "HackathonTeam2024Insertable", value_type = HackathonTeam2024Insertable, as = HackathonTeam2024Insertable)] pub struct TeamCreateData(pub HackathonTeam2024Insertable); diff --git a/src/dto/request/hackathon_2024/university.rs b/src/dto/request/hackathon_2024/university.rs index ae82690..785a957 100644 --- a/src/dto/request/hackathon_2024/university.rs +++ b/src/dto/request/hackathon_2024/university.rs @@ -1,4 +1,12 @@ -#[derive(serde::Deserialize)] -pub struct University( - pub crate::diesel::models::hackathon_2024::university::HackathonUniversity2024Insertable, -); +use serde::Deserialize; +use utoipa::ToSchema; + +use crate::diesel::models::hackathon_2024::university::HackathonUniversity2024Insertable; + +#[derive(Deserialize, ToSchema)] +#[schema(title = "HackathonUniversity2024Insertable", value_type = HackathonUniversity2024Insertable, as = HackathonUniversity2024Insertable)] +pub struct University(pub HackathonUniversity2024Insertable); + +#[derive(Deserialize, ToSchema)] +#[schema(title = "VecHackathonUniversity2024Insertable", value_type = Vec, as = Vec)] +pub struct VecUniversity(pub Vec); diff --git a/src/dto/request/hackathon_2024/user.rs b/src/dto/request/hackathon_2024/user.rs index fec945f..078444f 100644 --- a/src/dto/request/hackathon_2024/user.rs +++ b/src/dto/request/hackathon_2024/user.rs @@ -1,18 +1,19 @@ use crate::diesel::models::hackathon_2024::user::HackathonUser2024Insertable; use crate::dto::request::hackathon_2024::team::TeamRegistrationData; use serde::Deserialize; +use utoipa::ToSchema; #[non_exhaustive] -#[derive(Deserialize)] +#[derive(Deserialize, ToSchema)] pub struct RegistrationData { pub user_data: HackathonUser2024Insertable, pub team_data: TeamRegistrationData, } -#[allow(dead_code)] -#[derive(serde::Deserialize)] +#[derive(serde::Deserialize, ToSchema)] +#[schema(title = "HackathonUser2024Insertable", value_type = HackathonUser2024Insertable, as = HackathonUser2024Insertable)] pub struct User(pub HackathonUser2024Insertable); -#[allow(dead_code)] -#[derive(serde::Deserialize)] -pub struct VecUser(pub Vec); +#[derive(serde::Deserialize, ToSchema)] +#[schema(title = "VecHackathonUser2024Insertable", value_type = Vec, as = Vec)] +pub struct VecUser(#[allow(dead_code)] pub Vec); diff --git a/src/dto/response/admin.rs b/src/dto/response/admin.rs new file mode 100644 index 0000000..4a4a856 --- /dev/null +++ b/src/dto/response/admin.rs @@ -0,0 +1,4 @@ +#[derive(serde::Serialize)] +pub struct AdminResponse { + pub admin_auth: bool, +} diff --git a/src/dto/response/hackathon_2024/team.rs b/src/dto/response/hackathon_2024/team.rs index cb4cd6c..de4f68a 100644 --- a/src/dto/response/hackathon_2024/team.rs +++ b/src/dto/response/hackathon_2024/team.rs @@ -1,7 +1,11 @@ -#[derive(serde::Serialize)] -pub struct Team(pub crate::diesel::models::hackathon_2024::team::HackathonTeam2024Queryable); +use crate::diesel::models::hackathon_2024::team::HackathonTeam2024Queryable; +use serde::Serialize; +use utoipa::ToSchema; -#[derive(serde::Serialize)] -pub struct VecTeam( - pub Vec, -); +#[derive(Serialize, ToSchema)] +#[schema(title = "HackathonTeam2024Queryable", value_type = HackathonTeam2024Queryable, as = HackathonTeam2024Queryable)] +pub struct Team(pub HackathonTeam2024Queryable); + +#[derive(Serialize, ToSchema)] +#[schema(title = "VecHackathonTeam2024Queryable", value_type = Vec, as = Vec)] +pub struct VecTeam(pub Vec); diff --git a/src/dto/response/hackathon_2024/university.rs b/src/dto/response/hackathon_2024/university.rs index 294c006..f688e90 100644 --- a/src/dto/response/hackathon_2024/university.rs +++ b/src/dto/response/hackathon_2024/university.rs @@ -1,9 +1,11 @@ -#[derive(serde::Serialize)] -pub struct University( - pub crate::diesel::models::hackathon_2024::university::HackathonUniversity2024Queryable, -); +use crate::diesel::models::hackathon_2024::university::HackathonUniversity2024Queryable; +use serde::Serialize; +use utoipa::ToSchema; -#[derive(serde::Serialize)] -pub struct VecUniversity( - pub Vec, -); +#[derive(Serialize, ToSchema)] +#[schema(title = "HackathonUniversity2024Queryable", value_type = HackathonUniversity2024Queryable, as = HackathonUniversity2024Queryable)] +pub struct University(pub HackathonUniversity2024Queryable); + +#[derive(Serialize, ToSchema)] +#[schema(title = "VecHackathonUniversity2024Queryable", value_type = Vec, as = Vec)] +pub struct VecUniversity(pub Vec); diff --git a/src/dto/response/hackathon_2024/user.rs b/src/dto/response/hackathon_2024/user.rs index ece1d3f..9a326d7 100644 --- a/src/dto/response/hackathon_2024/user.rs +++ b/src/dto/response/hackathon_2024/user.rs @@ -1,7 +1,11 @@ use crate::diesel::models::hackathon_2024::user::HackathonUser2024Queryable; +use serde::Serialize; +use utoipa::ToSchema; -#[derive(serde::Serialize)] +#[derive(Serialize, ToSchema)] +#[schema(title = "HackathonUser2024Queryable", value_type = HackathonUser2024Queryable, as = HackathonUser2024Queryable)] pub struct User(pub HackathonUser2024Queryable); -#[derive(serde::Serialize)] +#[derive(Serialize, ToSchema)] +#[schema(title = "VecHackathonUser2024Queryable", value_type = Vec, as = Vec)] pub struct VecUser(pub Vec); diff --git a/src/dto/response/mod.rs b/src/dto/response/mod.rs index 2086991..38246c8 100644 --- a/src/dto/response/mod.rs +++ b/src/dto/response/mod.rs @@ -1,2 +1,2 @@ +pub mod admin; pub mod hackathon_2024; -pub mod user; diff --git a/src/dto/response/user.rs b/src/dto/response/user.rs deleted file mode 100644 index c329b56..0000000 --- a/src/dto/response/user.rs +++ /dev/null @@ -1,11 +0,0 @@ -use rocket::serde::Serialize; - -#[derive(Debug, Serialize)] -pub struct UserLoginResponse { - #[allow(dead_code)] - pub id: i32, - #[allow(dead_code)] - pub email: String, - #[allow(dead_code)] - pub token: String, -} diff --git a/src/error/api_error.rs b/src/error/api_error.rs index c56e570..ed5f9f8 100644 --- a/src/error/api_error.rs +++ b/src/error/api_error.rs @@ -5,7 +5,7 @@ use serde::Serialize; use std::io::Cursor; use thiserror::Error; -#[derive(Error, Debug)] +#[derive(Error, Debug, utoipa::ToSchema)] pub enum ApiError { #[allow(dead_code)] #[error("Error not found")] @@ -13,7 +13,7 @@ pub enum ApiError { #[allow(dead_code)] #[error("Database result error occurred")] - DatabaseErrorResult(#[from] diesel::result::Error), + DatabaseErrorResult(String), #[allow(dead_code)] #[error("Database connection error occurred")] @@ -118,8 +118,14 @@ impl<'r> Responder<'r, 'static> for ApiError { } } -#[derive(Serialize)] -struct ApiErrorBody { +#[derive(Serialize, utoipa::ToSchema)] +pub struct ApiErrorBody { error: String, message: String, } + +impl From for ApiError { + fn from(err: diesel::result::Error) -> Self { + ApiError::DatabaseErrorResult(err.to_string()) + } +} diff --git a/src/main.rs b/src/main.rs index 621da26..ad8c5c9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ mod error; mod middleware; mod models; mod server; +mod swagger; #[cfg(test)] mod tests; mod utils; diff --git a/src/middleware/admin_match.rs b/src/middleware/admin_match.rs deleted file mode 100644 index fd9c82e..0000000 --- a/src/middleware/admin_match.rs +++ /dev/null @@ -1,59 +0,0 @@ -use crate::error::api_error::ApiError; -use crate::utils::env_configuration::CONFIG; -use rocket::http::Status; -use rocket::request::FromRequest; -use rocket::{request, warn, Request}; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct AdminMatch { - is_admin: bool, -} - -impl AdminMatch { - #[allow(dead_code)] - pub fn new(is_admin: bool) -> Self { - AdminMatch { is_admin } - } - #[allow(dead_code)] - pub fn check_admin(&self) -> Result { - match &self.is_admin { - true => Ok(true), - false => Err(ApiError::Unauthorized( - "Don't admin matching password".to_owned(), - )), - } - } -} - -#[rocket::async_trait] -impl<'r> FromRequest<'r> for AdminMatch { - type Error = ApiError; - - #[allow(dead_code)] - async fn from_request(req: &'r Request<'_>) -> request::Outcome { - let header_password = req.headers().get_one("Admin"); - - match header_password { - Some(header_password) => { - let env_password = CONFIG.get().unwrap().admin_password.as_str(); - if header_password == env_password { - request::Outcome::Success(AdminMatch { is_admin: true }) - } else { - warn!("Error admin password"); - request::Outcome::Error(( - Status::Unauthorized, - ApiError::HeaderMismatched("Admin header mismatched".to_owned()), - )) - } - } - None => { - warn!("Token not found in header \"Authorization\""); - request::Outcome::Error(( - Status::Unauthorized, - ApiError::HeaderMismatched("Admin header mismatched".to_owned()), - )) - } - } - } -} diff --git a/src/middleware/admin_token_match.rs b/src/middleware/admin_token_match.rs new file mode 100644 index 0000000..db55d1d --- /dev/null +++ b/src/middleware/admin_token_match.rs @@ -0,0 +1,64 @@ +use crate::diesel::prelude::ApiError; +use crate::utils::prelude_api::EnvConfiguration; +use crate::utils::security; +use rocket::http::Status; +use rocket::request::FromRequest; +use rocket::{request, Request}; +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, utoipa::ToSchema, utoipa::IntoParams)] +pub struct AdminAuthData { + pub admin_password: String, + pub admin_name: String, + pub exp: u64, +} + +impl AdminAuthData { + pub fn check_admin(&self) -> Result<(), ApiError> { + log::info!( + "Admin name: {}, Admin password: {}", + EnvConfiguration::get().admin_name, + EnvConfiguration::get().admin_password + ); + if self.admin_name != EnvConfiguration::get().admin_name { + Err(ApiError::ValidationError(format!( + "Error validation admin name: {}", + self.admin_name + ))) + } else if self.admin_password != EnvConfiguration::get().admin_password { + Err(ApiError::ValidationError(format!( + "Error validation admin password: {}", + self.admin_password + ))) + } else { + Ok(()) + } + } +} + +#[rocket::async_trait] +impl<'r> FromRequest<'r> for AdminAuthData { + type Error = ApiError; + + #[allow(dead_code)] + async fn from_request(req: &'r Request<'_>) -> request::Outcome { + let token = req + .headers() + .get_one("Authorization") + .and_then(|header| header.strip_prefix("Bearer ")); + + match token { + Some(token) => match security::decoded_data::(token) { + Ok(token_data) => request::Outcome::Success(token_data.claims), + Err(_) => request::Outcome::Error(( + Status::Unauthorized, + ApiError::Unauthorized("Admin not authorized".to_string()), + )), + }, + None => request::Outcome::Error(( + Status::Unauthorized, + ApiError::HeaderMismatched("Admin header mismatched".to_owned()), + )), + } + } +} diff --git a/src/middleware/get_user.rs b/src/middleware/get_user.rs deleted file mode 100644 index 4d81874..0000000 --- a/src/middleware/get_user.rs +++ /dev/null @@ -1,26 +0,0 @@ -use crate::models::admin::admin_jwt; -use crate::utils::security; -use rocket::http::Status; -use rocket::request::FromRequest; -use rocket::{request, Request}; - -#[rocket::async_trait] -impl<'r> FromRequest<'r> for admin_jwt::AdminJwt { - type Error = (); - - #[allow(dead_code)] - async fn from_request(req: &'r Request<'_>) -> request::Outcome { - let token = req - .headers() - .get_one("Authorization") - .and_then(|header| header.strip_prefix("Bearer ")); - - match token { - Some(token) => match security::decoded_data::(token) { - Ok(token_data) => request::Outcome::Success(token_data.claims), - Err(_) => request::Outcome::Error((Status::Unauthorized, ())), - }, - None => request::Outcome::Error((Status::Unauthorized, ())), - } - } -} diff --git a/src/middleware/mod.rs b/src/middleware/mod.rs index c6d3a72..06fd8c7 100644 --- a/src/middleware/mod.rs +++ b/src/middleware/mod.rs @@ -1,3 +1,2 @@ -pub mod admin_match; +pub mod admin_token_match; pub mod claims; -pub mod get_user; diff --git a/src/models/admin/admin_jwt.rs b/src/models/admin/admin_jwt.rs deleted file mode 100644 index e90ab11..0000000 --- a/src/models/admin/admin_jwt.rs +++ /dev/null @@ -1,9 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[allow(dead_code)] -#[derive(Debug, Serialize, Deserialize)] -pub struct AdminJwt { - pub admin_password: String, - pub admin_name: String, - pub exp: u64, -} diff --git a/src/models/admin/mod.rs b/src/models/admin/mod.rs deleted file mode 100644 index 0c04a8b..0000000 --- a/src/models/admin/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod admin_jwt; diff --git a/src/models/mod.rs b/src/models/mod.rs index 38246c8..60226f9 100644 --- a/src/models/mod.rs +++ b/src/models/mod.rs @@ -1,2 +1 @@ -pub mod admin; pub mod hackathon_2024; diff --git a/src/server.rs b/src/server.rs index eec5378..8f125c0 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,11 +1,14 @@ use crate::api; use crate::diesel::configurator::{configuration_database, DbPool}; +use crate::swagger::ApiDoc; use crate::utils::env_configuration::EnvConfiguration; use log::LevelFilter; use rocket::figment::Figment; use rocket::{routes, Config}; use rocket_cors::{AllowedHeaders, AllowedOrigins, Cors, CorsOptions}; use std::net::IpAddr; +use utoipa::OpenApi; +use utoipa_swagger_ui::SwaggerUi; pub struct Server; @@ -42,7 +45,10 @@ impl Server { } fn configure_cors() -> Cors { - let exact = &[&format!("https://{}", EnvConfiguration::get().main_url)]; + let exact = &[ + &format!("https://{}", EnvConfiguration::get().main_url,), + &format!("http://0.0.0.0:{}", EnvConfiguration::get().server_port), + ]; CorsOptions { allowed_origins: AllowedOrigins::some_exact(exact), allowed_methods: vec!["GET", "POST", "PUT", "DELETE"] @@ -61,6 +67,10 @@ impl Server { rocket::custom(config) .attach(cors) .manage(db_pool) + .mount( + "/", + SwaggerUi::new("/swagger-ui/<_..>").url("/api-doc/openapi.json", ApiDoc::openapi()), + ) .mount( "/api", routes![ @@ -71,8 +81,12 @@ impl Server { api::hackathon_2024::user::get::all, api::hackathon_2024::user::put::by_id, api::hackathon_2024::user::delete::by_id, + api::hackathon_2024::user::get::by_id, + api::hackathon_2024::user::get::by_university, + api::hackathon_2024::user::get::by_team, // /hackathon_2024/university/* api::hackathon_2024::university::post::create, + api::hackathon_2024::university::post::create_by_vec, api::hackathon_2024::university::get::all, api::hackathon_2024::university::get::by_id, api::hackathon_2024::university::put::by_id, diff --git a/src/swagger/mod.rs b/src/swagger/mod.rs new file mode 100644 index 0000000..5d56d33 --- /dev/null +++ b/src/swagger/mod.rs @@ -0,0 +1,60 @@ +use crate::api; +use utoipa::openapi::security::{HttpAuthScheme, HttpBuilder, SecurityScheme}; +use utoipa::{Modify, OpenApi}; + +#[derive(OpenApi)] +#[openapi( + paths( + // /test/* + api::test::get::ping, + // /hackathon_2024/user/* + api::hackathon_2024::user::post::registration_by_tg, + api::hackathon_2024::user::get::all, + api::hackathon_2024::user::put::by_id, + api::hackathon_2024::user::delete::by_id, + api::hackathon_2024::user::get::by_id, + api::hackathon_2024::user::get::by_university, + api::hackathon_2024::user::get::by_team, + // /hackathon_2024/university/* + api::hackathon_2024::university::post::create, + api::hackathon_2024::university::post::create_by_vec, + api::hackathon_2024::university::get::all, + api::hackathon_2024::university::get::by_id, + api::hackathon_2024::university::put::by_id, + api::hackathon_2024::university::delete::by_id, + // /hackathon_2024/team/* + api::hackathon_2024::team::post::create, + api::hackathon_2024::team::get::all, + api::hackathon_2024::team::get::by_id, + api::hackathon_2024::team::put::by_id, + api::hackathon_2024::team::delete::by_id, + // /adnmin/ + api::admin::post::login, + api::admin::get::get, + // /other/* + ), + info( + title = "ExternalDept API", + version = "1.0.0" + ), + modifiers(&SecurityAddon) +)] +pub struct ApiDoc; + +struct SecurityAddon; + +impl Modify for SecurityAddon { + fn modify(&self, openapi: &mut utoipa::openapi::OpenApi) { + let components = openapi.components.as_mut().unwrap(); + + components.add_security_scheme( + "bearer_auth", + SecurityScheme::Http( + HttpBuilder::new() + .scheme(HttpAuthScheme::Bearer) + .bearer_format("JWT") + .build(), + ), + ); + } +} diff --git a/src/utils/prelude_api.rs b/src/utils/prelude_api.rs index fe8a534..82648aa 100644 --- a/src/utils/prelude_api.rs +++ b/src/utils/prelude_api.rs @@ -1,5 +1,6 @@ pub use crate::diesel::prelude::DbState; -pub use crate::error::api_error::ApiError; +pub use crate::error::api_error::{ApiError, ApiErrorBody}; +pub use crate::middleware::admin_token_match::AdminAuthData; #[allow(unused_imports)] pub use crate::utils::env_configuration::EnvConfiguration; pub use log::info;