diff --git a/src/api/v1/account/delete.rs b/src/api/v1/account/delete.rs new file mode 100644 index 00000000..e7c58c75 --- /dev/null +++ b/src/api/v1/account/delete.rs @@ -0,0 +1,60 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; + +use super::auth::Password; +use crate::errors::*; +use crate::Data; + +//#[post("/api/v1/account/delete", wrap = "CheckLogin")] +pub async fn delete_account( + id: Identity, + payload: web::Json, + data: web::Data, +) -> ServiceResult { + use argon2_creds::Config; + use sqlx::Error::RowNotFound; + + let username = id.identity().unwrap(); + + let rec = sqlx::query_as!( + Password, + r#"SELECT password FROM mcaptcha_users WHERE name = ($1)"#, + &username, + ) + .fetch_one(&data.db) + .await; + + id.forget(); + + match rec { + Ok(s) => { + if Config::verify(&s.password, &payload.password)? { + sqlx::query!("DELETE FROM mcaptcha_users WHERE name = ($1)", &username) + .execute(&data.db) + .await?; + Ok(HttpResponse::Ok()) + } else { + Err(ServiceError::WrongPassword) + } + } + Err(RowNotFound) => return Err(ServiceError::UsernameNotFound), + Err(_) => return Err(ServiceError::InternalServerError)?, + } +} diff --git a/src/api/v1/account/email.rs b/src/api/v1/account/email.rs new file mode 100644 index 00000000..77142db4 --- /dev/null +++ b/src/api/v1/account/email.rs @@ -0,0 +1,87 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ +use std::borrow::Cow; + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use serde::{Deserialize, Serialize}; + +use super::{AccountCheckPayload, AccountCheckResp}; +use crate::errors::*; +use crate::Data; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Email { + pub email: String, +} + +//#[post("/api/v1/account/email/exists")] +pub async fn email_exists( + payload: web::Json, + data: web::Data, +) -> ServiceResult { + let res = sqlx::query!( + "SELECT EXISTS (SELECT 1 from mcaptcha_users WHERE email = $1)", + &payload.val, + ) + .fetch_one(&data.db) + .await?; + + let mut resp = AccountCheckResp { exists: false }; + + if let Some(x) = res.exists { + if x { + resp.exists = true; + } + } + + Ok(HttpResponse::Ok().json(resp)) +} + +/// update email +//#[post("/api/v1/account/email/", wrap = "CheckLogin")] +//#[post("/api/v1/", wrap = "CheckLogin")] +pub async fn set_email( + id: Identity, + payload: web::Json, + data: web::Data, +) -> ServiceResult { + let username = id.identity().unwrap(); + + data.creds.email(&payload.email)?; + + let res = sqlx::query!( + "UPDATE mcaptcha_users set email = $1 + WHERE name = $2", + &payload.email, + &username, + ) + .execute(&data.db) + .await; + if !res.is_ok() { + if let Err(sqlx::Error::Database(err)) = res { + if err.code() == Some(Cow::from("23505")) + && err.message().contains("mcaptcha_users_email_key") + { + Err(ServiceError::EmailTaken)? + } else { + Err(sqlx::Error::Database(err))? + } + }; + } + Ok(HttpResponse::Ok()) +} diff --git a/src/api/v1/account/mod.rs b/src/api/v1/account/mod.rs new file mode 100644 index 00000000..a6752f5d --- /dev/null +++ b/src/api/v1/account/mod.rs @@ -0,0 +1,116 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +use serde::{Deserialize, Serialize}; + +pub mod delete; +pub mod email; +pub mod secret; +#[cfg(test)] +pub mod test; +pub mod username; + +pub use super::auth; +pub use super::mcaptcha; + +pub mod routes { + + pub struct Account { + pub delete: &'static str, + pub email_exists: &'static str, + pub update_email: &'static str, + pub get_secret: &'static str, + pub update_secret: &'static str, + pub username_exists: &'static str, + } + + impl Account { + pub const fn new() -> Account { + let get_secret = "/api/v1/account/secret/get"; + let update_secret = "/api/v1/account/secret/update"; + let delete = "/api/v1/account/delete"; + let email_exists = "/api/v1/account/email/exists"; + let username_exists = "/api/v1/account/username/exists"; + let update_email = "/api/v1/account/email/update"; + Account { + get_secret, + update_secret, + username_exists, + update_email, + delete, + email_exists, + } + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AccountCheckPayload { + pub val: String, +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct AccountCheckResp { + pub exists: bool, +} + +pub fn service(cfg: &mut actix_web::web::ServiceConfig) { + use crate::define_resource; + use crate::V1_API_ROUTES; + + define_resource!( + cfg, + V1_API_ROUTES.account.delete, + Methods::ProtectPost, + delete::delete_account + ); + + define_resource!( + cfg, + V1_API_ROUTES.account.username_exists, + Methods::Post, + username::username_exists + ); + + define_resource!( + cfg, + V1_API_ROUTES.account.email_exists, + Methods::Post, + email::email_exists + ); + + define_resource!( + cfg, + V1_API_ROUTES.account.update_email, + Methods::Post, + email::set_email + ); + + define_resource!( + cfg, + V1_API_ROUTES.account.get_secret, + Methods::ProtectGet, + secret::get_secret + ); + + define_resource!( + cfg, + V1_API_ROUTES.account.update_secret, + Methods::ProtectPost, + secret::update_user_secret + ); +} diff --git a/src/api/v1/account/secret.rs b/src/api/v1/account/secret.rs new file mode 100644 index 00000000..669754f9 --- /dev/null +++ b/src/api/v1/account/secret.rs @@ -0,0 +1,81 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ +use std::borrow::Cow; + +use actix_identity::Identity; +use actix_web::{web, HttpResponse, Responder}; +use serde::{Deserialize, Serialize}; + +use crate::api::v1::mcaptcha::get_random; +use crate::errors::*; +use crate::Data; + +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct Secret { + pub secret: String, +} + +//#[get("/api/v1/account/secret/", wrap = "CheckLogin")] +pub async fn get_secret(id: Identity, data: web::Data) -> ServiceResult { + let username = id.identity().unwrap(); + + let secret = sqlx::query_as!( + Secret, + r#"SELECT secret FROM mcaptcha_users WHERE name = ($1)"#, + &username, + ) + .fetch_one(&data.db) + .await?; + + Ok(HttpResponse::Ok().json(secret)) +} + +//#[post("/api/v1/account/secret/", wrap = "CheckLogin")] +pub async fn update_user_secret( + id: Identity, + data: web::Data, +) -> ServiceResult { + let username = id.identity().unwrap(); + + let mut secret; + + loop { + secret = get_random(32); + let res = sqlx::query!( + "UPDATE mcaptcha_users set secret = $1 + WHERE name = $2", + &secret, + &username, + ) + .execute(&data.db) + .await; + if res.is_ok() { + break; + } else { + if let Err(sqlx::Error::Database(err)) = res { + if err.code() == Some(Cow::from("23505")) + && err.message().contains("mcaptcha_users_secret_key") + { + continue; + } else { + Err(sqlx::Error::Database(err))?; + } + }; + } + } + Ok(HttpResponse::Ok()) +} diff --git a/src/api/v1/tests/account.rs b/src/api/v1/account/test.rs similarity index 95% rename from src/api/v1/tests/account.rs rename to src/api/v1/account/test.rs index 7ca7b968..af4e1246 100644 --- a/src/api/v1/tests/account.rs +++ b/src/api/v1/account/test.rs @@ -18,11 +18,11 @@ use actix_web::http::{header, StatusCode}; use actix_web::test; -use crate::api::v1::account::*; +use super::email::*; +use super::*; use crate::api::v1::auth::*; use crate::api::v1::ROUTES; use crate::data::Data; -use crate::errors::*; use crate::*; use crate::tests::*; @@ -81,7 +81,7 @@ async fn uname_email_exists_works() { let email_doesnt_exist = test::call_service( &mut app, - post_request!(&payload, ROUTES.account.email_exist) + post_request!(&payload, ROUTES.account.email_exists) .cookie(cookies.clone()) .to_request(), ) @@ -94,7 +94,7 @@ async fn uname_email_exists_works() { let email_exist = test::call_service( &mut app, - post_request!(&payload, ROUTES.account.email_exist) + post_request!(&payload, ROUTES.account.email_exists) .cookie(cookies.clone()) .to_request(), ) @@ -125,6 +125,7 @@ async fn email_udpate_password_validation_del_userworks() { let email_update_resp = test::call_service( &mut app, post_request!(&email_payload, ROUTES.account.update_email) + //post_request!(&email_payload, EMAIL_UPDATE) .cookie(cookies.clone()) .to_request(), ) diff --git a/src/api/v1/account/username.rs b/src/api/v1/account/username.rs new file mode 100644 index 00000000..c161ae35 --- /dev/null +++ b/src/api/v1/account/username.rs @@ -0,0 +1,44 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ +use actix_web::{web, HttpResponse, Responder}; + +use super::{AccountCheckPayload, AccountCheckResp}; +use crate::errors::*; +use crate::Data; + +//#[post("/api/v1/account/username/exists")] +pub async fn username_exists( + payload: web::Json, + data: web::Data, +) -> ServiceResult { + let res = sqlx::query!( + "SELECT EXISTS (SELECT 1 from mcaptcha_users WHERE name = $1)", + &payload.val, + ) + .fetch_one(&data.db) + .await?; + + let mut resp = AccountCheckResp { exists: false }; + + if let Some(x) = res.exists { + if x { + resp.exists = true; + } + } + + Ok(HttpResponse::Ok().json(resp)) +} diff --git a/src/api/v1/account.rs b/src/api/v1/accountrs similarity index 96% rename from src/api/v1/account.rs rename to src/api/v1/accountrs index 275ffc76..c459fefa 100644 --- a/src/api/v1/account.rs +++ b/src/api/v1/accountrs @@ -37,15 +37,15 @@ pub mod routes { pub username_exists: &'static str, } - impl Default for Account { - fn default() -> Self { + impl Account { + pub const fn new() -> Account { let get_secret = "/api/v1/account/secret/"; let update_secret = "/api/v1/account/secret"; let delete = "/api/v1/account/delete"; let email_exists = "/api/v1/account/email/exists"; let username_exists = "/api/v1/account/username/exists"; - let update_email = "/api/v1/account/email"; - Self { + let update_email = "/api/v1/account/email/"; + Account { get_secret, update_secret, username_exists, @@ -212,12 +212,11 @@ pub struct Email { } /// update email -#[post("/api/v1/account/email/", wrap = "CheckLogin")] +//#[post("/api/v1/account/email/", wrap = "CheckLogin")] +#[post("/api/v1/", wrap = "CheckLogin")] pub async fn set_email( id: Identity, - payload: web::Json, - data: web::Data, ) -> ServiceResult { let username = id.identity().unwrap(); diff --git a/src/api/v1/auth.rs b/src/api/v1/auth.rs index 1aa9e872..df922403 100644 --- a/src/api/v1/auth.rs +++ b/src/api/v1/auth.rs @@ -18,13 +18,12 @@ use std::borrow::Cow; use actix_identity::Identity; use actix_web::http::header; -use actix_web::{get, post, web, HttpResponse, Responder}; +use actix_web::{web, HttpResponse, Responder}; use log::debug; use serde::{Deserialize, Serialize}; use super::mcaptcha::get_random; use crate::errors::*; -use crate::CheckLogin; use crate::Data; pub mod routes { @@ -34,12 +33,12 @@ pub mod routes { pub register: &'static str, } - impl Default for Auth { - fn default() -> Self { + impl Auth { + pub const fn new() -> Auth { let login = "/api/v1/signin"; let logout = "/logout"; let register = "/api/v1/signup"; - Self { + Auth { login, logout, register, @@ -48,6 +47,15 @@ pub mod routes { } } +pub fn service(cfg: &mut web::ServiceConfig) { + use crate::define_resource; + use crate::V1_API_ROUTES; + + define_resource!(cfg, V1_API_ROUTES.auth.register, Methods::Post, signup); + define_resource!(cfg, V1_API_ROUTES.auth.logout, Methods::ProtectGet, signout); + define_resource!(cfg, V1_API_ROUTES.auth.login, Methods::Post, signin); +} + #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Register { pub username: String, @@ -67,8 +75,8 @@ pub struct Password { pub password: String, } -#[post("/api/v1/signup")] -pub async fn signup( +//#[post("/api/v1/signup")] +async fn signup( payload: web::Json, data: web::Data, ) -> ServiceResult { @@ -135,8 +143,8 @@ pub async fn signup( Ok(HttpResponse::Ok()) } -#[post("/api/v1/signin")] -pub async fn signin( +//#[post("/api/v1/signin")] +async fn signin( id: Identity, payload: web::Json, data: web::Data, @@ -167,8 +175,8 @@ pub async fn signin( } } -#[get("/logout", wrap = "CheckLogin")] -pub async fn signout(id: Identity) -> impl Responder { +//#[get("/logout", wrap = "CheckLogin")] +async fn signout(id: Identity) -> impl Responder { if let Some(_) = id.identity() { id.forget(); } diff --git a/src/api/v1/mcaptcha/duration.rs b/src/api/v1/mcaptcha/duration.rs index b91ac45b..cd96fa45 100644 --- a/src/api/v1/mcaptcha/duration.rs +++ b/src/api/v1/mcaptcha/duration.rs @@ -29,10 +29,9 @@ pub mod routes { pub update: &'static str, pub get: &'static str, } - - impl Default for Duration { - fn default() -> Self { - Self { + impl Duration { + pub const fn new() -> Duration { + Duration { update: "/api/v1/mcaptcha/domain/token/duration/update", get: "/api/v1/mcaptcha/domain/token/duration/get", } diff --git a/src/api/v1/mcaptcha/levels.rs b/src/api/v1/mcaptcha/levels.rs index 21813634..ac6ed520 100644 --- a/src/api/v1/mcaptcha/levels.rs +++ b/src/api/v1/mcaptcha/levels.rs @@ -34,13 +34,13 @@ pub mod routes { pub get: &'static str, } - impl Default for Levels { - fn default() -> Self { + impl Levels { + pub const fn new() -> Levels { let add = "/api/v1/mcaptcha/levels/add"; let update = "/api/v1/mcaptcha/levels/update"; let delete = "/api/v1/mcaptcha/levels/delete"; let get = "/api/v1/mcaptcha/levels/get"; - Self { + Levels { add, get, update, diff --git a/src/api/v1/mcaptcha/mcaptcha.rs b/src/api/v1/mcaptcha/mcaptcha.rs index 611195bc..afa9039b 100644 --- a/src/api/v1/mcaptcha/mcaptcha.rs +++ b/src/api/v1/mcaptcha/mcaptcha.rs @@ -33,9 +33,9 @@ pub mod routes { pub update_key: &'static str, } - impl Default for MCaptcha { - fn default() -> Self { - Self { + impl MCaptcha { + pub const fn new() -> MCaptcha { + MCaptcha { add: "/api/v1/mcaptcha/add", update_key: "/api/v1/mcaptcha/update/key", get_token: "/api/v1/mcaptcha/get", diff --git a/src/api/v1/meta.rs b/src/api/v1/meta.rs index 7268cf93..fd6097c3 100644 --- a/src/api/v1/meta.rs +++ b/src/api/v1/meta.rs @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -use actix_web::{get, web, HttpResponse, Responder}; +use actix_web::{web, HttpResponse, Responder}; use derive_builder::Builder; use serde::{Deserialize, Serialize}; @@ -28,9 +28,24 @@ pub struct BuildDetails { pub git_commit_hash: &'static str, } -#[get("/api/v1/meta/build")] +pub mod routes { + pub struct Meta { + pub build_details: &'static str, + pub health: &'static str, + } + + impl Meta { + pub const fn new() -> Self { + Self { + build_details: "/api/v1/meta/build", + health: "/api/v1/meta/health", + } + } + } +} + /// emmits build details of the bninary -pub async fn build_details() -> impl Responder { +async fn build_details() -> impl Responder { let build = BuildDetails { version: VERSION, git_commit_hash: &GIT_COMMIT_HASH, @@ -44,9 +59,8 @@ pub struct Health { db: bool, } -#[get("/api/v1/meta/health")] /// checks all components of the system -pub async fn health(data: web::Data) -> impl Responder { +async fn health(data: web::Data) -> impl Responder { use sqlx::Connection; let mut resp_builder = HealthBuilder::default(); @@ -60,33 +74,57 @@ pub async fn health(data: web::Data) -> impl Responder { HttpResponse::Ok().json(resp_builder.build().unwrap()) } +pub fn service(cfg: &mut web::ServiceConfig) { + use crate::define_resource; + use crate::V1_API_ROUTES; + + define_resource!( + cfg, + V1_API_ROUTES.meta.build_details, + Methods::Get, + build_details + ); + define_resource!(cfg, V1_API_ROUTES.meta.health, Methods::Get, health); +} + #[cfg(test)] mod tests { use actix_web::{http::StatusCode, test, App}; use super::*; - use crate::api::v1::services as v1_services; + use crate::api::v1::new_services; use crate::*; #[actix_rt::test] async fn build_details_works() { - const GET_URI: &str = "/api/v1/meta/build"; - let mut app = test::init_service(App::new().configure(v1_services)).await; + // const GET_URI: &str = "/api/v1/meta/build"; + let mut app = test::init_service(App::new().configure(new_services)).await; - let resp = - test::call_service(&mut app, test::TestRequest::get().uri(GET_URI).to_request()).await; + let resp = test::call_service( + &mut app, + test::TestRequest::get() + .uri(V1_API_ROUTES.meta.build_details) + .to_request(), + ) + .await; assert_eq!(resp.status(), StatusCode::OK); } #[actix_rt::test] async fn health_works() { - const GET_URI: &str = "/api/v1/meta/health"; + // const GET_URI: &str = "/api/v1/meta/health"; + println!("{}", V1_API_ROUTES.meta.health); let data = Data::new().await; let mut app = get_app!(data).await; - let resp = - test::call_service(&mut app, test::TestRequest::get().uri(GET_URI).to_request()).await; + let resp = test::call_service( + &mut app, + test::TestRequest::get() + .uri(V1_API_ROUTES.meta.health) + .to_request(), + ) + .await; assert_eq!(resp.status(), StatusCode::OK); let health_resp: Health = test::read_body_json(resp).await; diff --git a/src/api/v1/mod.rs b/src/api/v1/mod.rs index c2f6f8a7..f1ce0b14 100644 --- a/src/api/v1/mod.rs +++ b/src/api/v1/mod.rs @@ -27,23 +27,6 @@ mod routes; pub use routes::ROUTES; pub fn services(cfg: &mut ServiceConfig) { - // meta - cfg.service(meta::build_details); - cfg.service(meta::health); - - // auth - cfg.service(auth::signout); - cfg.service(auth::signin); - cfg.service(auth::signup); - - // account - cfg.service(account::delete_account); - cfg.service(account::username_exists); - cfg.service(account::email_exists); - cfg.service(account::get_secret); - cfg.service(account::update_user_secret); - cfg.service(account::set_email); - // mcaptcha cfg.service(mcaptcha::mcaptcha::add_mcaptcha); cfg.service(mcaptcha::mcaptcha::delete_mcaptcha); @@ -61,5 +44,69 @@ pub fn services(cfg: &mut ServiceConfig) { cfg.service(mcaptcha::duration::get_duration); } +pub fn new_services(cfg: &mut ServiceConfig) { + meta::service(cfg); + auth::service(cfg); + account::service(cfg); + + //define_resource!( + // cfg, + // ROUTES.meta.build_details, + // Methods::Get, + // meta::build_details + //); + //define_resource!(cfg, ROUTES.meta.health, Methods::Get, meta::health); + + // auth + + //define_resource!(cfg, ROUTES.auth.register, Methods::Post, auth::signup); + //define_resource!(cfg, ROUTES.auth.logout, Methods::ProtectGet, auth::signout); + //define_resource!(cfg, ROUTES.auth.login, Methods::Post, auth::signin); + + // account + + // define_resource!( + // cfg, + // ROUTES.account.delete, + // Methods::ProtectPost, + // account::delete::delete_account + // ); + // + // define_resource!( + // cfg, + // ROUTES.account.username_exists, + // Methods::Post, + // account::username::username_exists + // ); + // + // define_resource!( + // cfg, + // ROUTES.account.email_exists, + // Methods::Post, + // account::email::email_exists + // ); + // + // define_resource!( + // cfg, + // ROUTES.account.update_email, + // Methods::Post, + // account::email::set_email + // ); + // + // define_resource!( + // cfg, + // ROUTES.account.get_secret, + // Methods::ProtectGet, + // account::secret::get_secret + // ); + // + // define_resource!( + // cfg, + // ROUTES.account.update_secret, + // Methods::ProtectPost, + // account::secret::update_user_secret + // ); +} + #[cfg(test)] mod tests; diff --git a/src/api/v1/routes.rs b/src/api/v1/routes.rs index 764c8c97..1190167b 100644 --- a/src/api/v1/routes.rs +++ b/src/api/v1/routes.rs @@ -15,23 +15,33 @@ * along with this program. If not, see . */ -use lazy_static::lazy_static; - use super::account::routes::Account; use super::auth::routes::Auth; use super::mcaptcha::duration::routes::Duration; use super::mcaptcha::levels::routes::Levels; use super::mcaptcha::mcaptcha::routes::MCaptcha; +use super::meta::routes::Meta; -lazy_static! { - pub static ref ROUTES: Routes = Routes::default(); -} +pub const ROUTES: Routes = Routes::new(); -#[derive(Default)] pub struct Routes { pub auth: Auth, pub account: Account, pub levels: Levels, pub mcaptcha: MCaptcha, pub duration: Duration, + pub meta: Meta, +} + +impl Routes { + const fn new() -> Routes { + Routes { + auth: Auth::new(), + account: Account::new(), + levels: Levels::new(), + mcaptcha: MCaptcha::new(), + duration: Duration::new(), + meta: Meta::new(), + } + } } diff --git a/src/main.rs b/src/main.rs index a717a84f..48d31657 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,11 +31,14 @@ mod errors; mod settings; mod static_assets; mod templates; +#[macro_use] +mod routes; #[cfg(test)] #[macro_use] mod tests; mod middleware; +pub use api::v1::ROUTES as V1_API_ROUTES; pub use data::Data; pub use settings::Settings; use static_assets::FileMap; @@ -87,6 +90,7 @@ async fn main() -> std::io::Result<()> { )) .configure(v1::pow::services) .configure(v1::services) + .configure(v1::new_services) .configure(docs::services) .configure(static_assets::services) .configure(templates::services) diff --git a/src/routes.rs b/src/routes.rs new file mode 100644 index 00000000..c9dcd1b4 --- /dev/null +++ b/src/routes.rs @@ -0,0 +1,61 @@ +/* +* Copyright (C) 2021 Aravinth Manivannan +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU Affero General Public License as +* published by the Free Software Foundation, either version 3 of the +* License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU Affero General Public License for more details. +* +* You should have received a copy of the GNU Affero General Public License +* along with this program. If not, see . +*/ + +#[allow(dead_code)] +pub enum Methods { + Get, + Post, + ProtectGet, + ProtectPost, +} + +#[macro_export] +macro_rules! define_resource { + ($cfg:expr, $path:expr, Methods::Get, $to:expr) => { + $cfg.service( + actix_web::web::resource($path) + .guard(actix_web::guard::Get()) + .to($to), + ); + }; + + ($cfg:expr, $path:expr, Methods::Post, $to:expr) => { + $cfg.service( + actix_web::Resource::new($path) + .guard(actix_web::guard::Post()) + .to($to), + ); + }; + + ($cfg:expr, $path:expr, Methods::ProtectPost, $to:expr) => { + $cfg.service( + actix_web::web::resource($path) + .wrap(crate::CheckLogin) + .guard(actix_web::guard::Post()) + .to($to), + ); + }; + + ($cfg:expr, $path:expr, Methods::ProtectGet, $to:expr) => { + $cfg.service( + actix_web::web::resource($path) + .wrap(crate::CheckLogin) + .guard(actix_web::guard::Get()) + .to($to), + ); + }; +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index 2a546ab8..d82519a0 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -57,6 +57,7 @@ macro_rules! get_app { )) .configure(crate::api::v1::pow::services) .configure(crate::api::v1::services) + .configure(crate::api::v1::new_services) .data($data.clone()), ) }; diff --git a/templates/auth/login/index.html b/templates/auth/login/index.html index 1912a147..b1f6e456 100644 --- a/templates/auth/login/index.html +++ b/templates/auth/login/index.html @@ -3,7 +3,7 @@ " class="form__logo" alt="" />

Sign in to mCaptcha

-
+