diff --git a/Cargo.toml b/Cargo.toml index 7c349ddb..ed064dce 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,15 @@ [package] name = "guard" version = "0.1.0" +description = "mCaptcha - a PoW-based CAPTCHA system" +homepage = "https://mcaptcha.org" +repository = "https://github.com/mCaptcha/guard" +documentation = "https://mcaptcha.org/docs/" +lisense = "AGPLv3 or later version" authors = ["realaravinth "] edition = "2018" default-run = "guard" +build = "build.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html diff --git a/build.rs b/build.rs new file mode 100644 index 00000000..22cd97d3 --- /dev/null +++ b/build.rs @@ -0,0 +1,27 @@ +/* +* 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::process::Command; +fn main() { + // note: add error checking yourself. + let output = Command::new("git") + .args(&["rev-parse", "HEAD"]) + .output() + .unwrap(); + let git_hash = String::from_utf8(output.stdout).unwrap(); + println!("cargo:rustc-env=GIT_HASH={}", git_hash); +} diff --git a/src/api/v1/meta.rs b/src/api/v1/meta.rs new file mode 100644 index 00000000..b64af585 --- /dev/null +++ b/src/api/v1/meta.rs @@ -0,0 +1,88 @@ +/* +* 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::{get, web, HttpResponse, Responder}; +use derive_builder::Builder; +use serde::{Deserialize, Serialize}; + +use crate::Data; +use crate::{GIT_COMMIT_HASH, VERSION}; + +#[get("/api/v1/meta/build")] +/// emmits build details of the bninary +pub async fn build_details() -> impl Responder { + HttpResponse::Ok().content_type("text/html").body(format!( + "version: {}\ncommit: {}", + VERSION, *GIT_COMMIT_HASH + )) +} + +#[derive(Clone, Debug, Deserialize, Builder, Serialize)] +/// Health check return datatype +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 { + use sqlx::Connection; + + let mut resp_builder = HealthBuilder::default(); + resp_builder.db(false); + if let Ok(mut con) = data.db.acquire().await { + if let Ok(_) = con.ping().await { + resp_builder.db(true); + } + }; + + HttpResponse::Ok().json(resp_builder.build().unwrap()) +} + +#[cfg(test)] +mod tests { + use actix_web::{http::StatusCode, test, App}; + + use super::*; + use crate::api::v1::services as v1_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; + + let resp = + test::call_service(&mut app, test::TestRequest::get().uri(GET_URI).to_request()).await; + assert_eq!(resp.status(), StatusCode::OK); + } + + #[actix_rt::test] + async fn health_works() { + const GET_URI: &str = "/api/v1/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; + assert_eq!(resp.status(), StatusCode::OK); + + let health_resp: Health = test::read_body_json(resp).await; + assert_eq!(health_resp.db, true); + } +} diff --git a/src/api/v1/mod.rs b/src/api/v1/mod.rs index 84e9c363..e79480cb 100644 --- a/src/api/v1/mod.rs +++ b/src/api/v1/mod.rs @@ -19,28 +19,33 @@ use actix_web::web::ServiceConfig; pub mod auth; pub mod mcaptcha; +pub mod meta; pub fn services(cfg: &mut ServiceConfig) { - use auth::*; - use mcaptcha::*; + // auth + cfg.service(auth::signout); + cfg.service(auth::signin); + cfg.service(auth::signup); + cfg.service(auth::delete_account); - cfg.service(signout); - cfg.service(signin); - cfg.service(signup); - cfg.service(delete_account); + // mcaptcha + // 1. domain and mcaptcha + cfg.service(mcaptcha::add_domain); + cfg.service(mcaptcha::delete_domain); + cfg.service(mcaptcha::add_mcaptcha); + cfg.service(mcaptcha::delete_mcaptcha); + // levels + cfg.service(mcaptcha::add_levels); + cfg.service(mcaptcha::update_levels); + cfg.service(mcaptcha::delete_levels); + cfg.service(mcaptcha::get_levels); + // duration + cfg.service(mcaptcha::update_duration); + cfg.service(mcaptcha::get_duration); - cfg.service(add_domain); - cfg.service(delete_domain); - cfg.service(add_mcaptcha); - cfg.service(delete_mcaptcha); - - cfg.service(add_levels); - cfg.service(update_levels); - cfg.service(delete_levels); - cfg.service(get_levels); - - cfg.service(update_duration); - cfg.service(get_duration); + // meta + cfg.service(meta::build_details); + cfg.service(meta::health); } #[cfg(test)] diff --git a/src/main.rs b/src/main.rs index 150481f2..abfa8bd3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,12 +14,14 @@ * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ +use std::env; use actix_identity::{CookieIdentityPolicy, IdentityService}; use actix_web::{ error::InternalError, http::StatusCode, middleware, web::JsonConfig, App, HttpServer, }; use lazy_static::lazy_static; +use log::info; mod data; mod errors; @@ -35,15 +37,25 @@ pub use settings::Settings; lazy_static! { pub static ref SETTINGS: Settings = Settings::new().unwrap(); + pub static ref GIT_COMMIT_HASH: String = env::var("GIT_HASH").unwrap(); } +pub static VERSION: &str = env!("CARGO_PKG_VERSION"); +pub static PKG_NAME: &str = env!("CARGO_PKG_NAME"); +pub static PKG_DESCRIPTION: &str = env!("CARGO_PKG_DESCRIPTION"); +pub static PKG_HOMEPAGE: &str = env!("CARGO_PKG_HOMEPAGE"); + #[cfg(not(tarpaulin_include))] #[actix_web::main] async fn main() -> std::io::Result<()> { use api::v1::services as v1_services; + pretty_env_logger::init(); + info!( + "{}: {}.\nFor more information, see: {}\nBuild info:\nVersion: {} commit: {}", + PKG_NAME, PKG_DESCRIPTION, PKG_HOMEPAGE, VERSION, *GIT_COMMIT_HASH + ); let data = Data::new().await; - pretty_env_logger::init(); sqlx::migrate!("./migrations/").run(&data.db).await.unwrap();