diff --git a/src/api/v1/mcaptcha/easy.rs b/src/api/v1/mcaptcha/easy.rs index 9ce99a2b..b226a47e 100644 --- a/src/api/v1/mcaptcha/easy.rs +++ b/src/api/v1/mcaptcha/easy.rs @@ -219,6 +219,7 @@ async fn update( mod tests { use actix_web::http::StatusCode; use actix_web::test; + use actix_web::web::Bytes; use super::*; use crate::api::v1::mcaptcha::create::MCaptchaDetails; @@ -392,5 +393,33 @@ mod tests { assert_ne!(res_levels, default_levels); assert_eq!(res_levels, updated_default_values); // END update_easy + + // test easy edit page + let easy_url = PAGES.panel.sitekey.get_edit_easy(&token_key.key); + + let easy_edit_page = test::call_service( + &app, + test::TestRequest::get() + .uri(&easy_url) + .cookie(cookies.clone()) + .to_request(), + ) + .await; + assert_eq!(easy_edit_page.status(), StatusCode::OK); + + let body: Bytes = test::read_body(easy_edit_page).await; + let body = String::from_utf8(body.to_vec()).unwrap(); + assert!(body.contains(&token_key.name)); + + assert!(body.contains( + &payload + .pattern + .broke_my_site_traffic + .as_ref() + .unwrap() + .to_string() + )); + assert!(body.contains(&payload.pattern.avg_traffic.to_string())); + assert!(body.contains(&payload.pattern.peak_sustainable_traffic.to_string())); } } diff --git a/src/pages/panel/sitekey/edit.rs b/src/pages/panel/sitekey/edit.rs index 7cea8c2b..1356e7e0 100644 --- a/src/pages/panel/sitekey/edit.rs +++ b/src/pages/panel/sitekey/edit.rs @@ -15,13 +15,15 @@ * along with this program. If not, see . */ use actix_identity::Identity; -use actix_web::{web, HttpResponse, Responder}; +use actix_web::{http, web, HttpResponse, Responder}; use sailfish::TemplateOnce; +use sqlx::Error::RowNotFound; +use crate::api::v1::mcaptcha::easy::TrafficPattern; use crate::errors::*; use crate::AppData; -const PAGE: &str = "SiteKeys"; +const PAGE: &str = "Edit Sitekey"; #[derive(Clone)] struct McaptchaConfig { @@ -100,9 +102,118 @@ pub async fn advance( .body(body)) } +#[derive(TemplateOnce, Clone)] +#[template(path = "panel/sitekey/edit/easy/index.html")] +pub struct EasyEditPage<'a> { + pub form_title: &'a str, + pub pattern: TrafficPattern, + pub key: String, +} + +impl<'a> EasyEditPage<'a> { + pub fn new(key: String, pattern: TrafficPattern) -> Self { + Self { + form_title: PAGE, + pattern, + key, + } + } +} + +/// route handler that renders individual views for sitekeys +#[my_codegen::get( + path = "crate::PAGES.panel.sitekey.edit_easy", + wrap = "crate::CheckLogin" +)] +pub async fn easy( + path: web::Path, + data: AppData, + id: Identity, +) -> PageResult { + let username = id.identity().unwrap(); + let key = path.into_inner(); + + struct Traffic { + peak_sustainable_traffic: i32, + avg_traffic: i32, + broke_my_site_traffic: Option, + } + + match sqlx::query_as!( + Traffic, + "SELECT + avg_traffic, + peak_sustainable_traffic, + broke_my_site_traffic + FROM + mcaptcha_sitekey_user_provided_avg_traffic + WHERE + config_id = ( + SELECT + config_id + FROM + mcaptcha_config + WHERE + KEY = $1 + AND user_id = ( + SELECT + id + FROM + mcaptcha_users + WHERE + NAME = $2 + ) + ) + ", + &key, + &username + ) + .fetch_one(&data.db) + .await + { + Ok(c) => { + struct Description { + name: String, + } + let description = sqlx::query_as!( + Description, + "SELECT name FROM mcaptcha_config + WHERE key = $1 + AND user_id = ( + SELECT user_id FROM mcaptcha_users WHERE NAME = $2)", + &key, + &username + ) + .fetch_one(&data.db) + .await?; + + let pattern = TrafficPattern { + peak_sustainable_traffic: c.peak_sustainable_traffic as u32, + avg_traffic: c.avg_traffic as u32, + broke_my_site_traffic: c.broke_my_site_traffic.map(|n| n as u32), + description: description.name, + }; + + let page = EasyEditPage::new(key, pattern).render_once().unwrap(); + return Ok(HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(page)); + } + Err(RowNotFound) => { + return Ok(HttpResponse::Found() + .insert_header(( + http::header::LOCATION, + crate::PAGES.panel.sitekey.get_edit_advance(&key), + )) + .finish()); + } + Err(e) => Err(e.into()), + } +} + #[cfg(test)] mod test { - use actix_web::http::StatusCode; + use actix_web::http::{header, StatusCode}; use actix_web::test; use actix_web::web::Bytes; @@ -148,5 +259,19 @@ mod test { assert!(body.contains(&L1.difficulty_factor.to_string())); assert!(body.contains(&L2.difficulty_factor.to_string())); assert!(body.contains(&L2.visitor_threshold.to_string())); + + let easy_url = PAGES.panel.sitekey.get_edit_easy(&key.key); + + let redirect_resp = test::call_service( + &app, + test::TestRequest::get() + .uri(&easy_url) + .cookie(cookies.clone()) + .to_request(), + ) + .await; + assert_eq!(redirect_resp.status(), StatusCode::FOUND); + let headers = redirect_resp.headers(); + assert_eq!(headers.get(header::LOCATION).unwrap(), &url); } } diff --git a/src/pages/panel/sitekey/mod.rs b/src/pages/panel/sitekey/mod.rs index 6aa3224a..1d1cb654 100644 --- a/src/pages/panel/sitekey/mod.rs +++ b/src/pages/panel/sitekey/mod.rs @@ -73,6 +73,7 @@ pub fn services(cfg: &mut actix_web::web::ServiceConfig) { cfg.service(list::list_sitekeys); cfg.service(view::view_sitekey); cfg.service(edit::advance); + cfg.service(edit::easy); cfg.service(delete::delete_sitekey); } diff --git a/templates/index.ts b/templates/index.ts index 0c2d6814..a4b0fc6b 100644 --- a/templates/index.ts +++ b/templates/index.ts @@ -25,7 +25,8 @@ import * as deleteAccount from "./panel/settings/account/delete"; import * as updateSecret from "./panel/settings/secret/update"; import * as addSiteKeyAdvance from "./panel/sitekey/add/advance/ts"; import * as addSiteKeyEasy from "./panel/sitekey/add/novice/ts"; -import * as editSitekey from "./panel/sitekey/edit/"; +import * as editSitekeyAdvance from "./panel/sitekey/edit/"; +import * as editSitekeyEasy from "./panel/sitekey/edit/easy/"; import * as deleteSitekey from "./panel/sitekey/delete/"; import * as listSitekeys from "./panel/sitekey/list/ts"; import * as notidications from "./panel/notifications/ts"; @@ -48,7 +49,8 @@ router.register(VIEWS.notifications, notidications.index); router.register(VIEWS.listSitekey, listSitekeys.index); router.register(VIEWS.addSiteKeyAdvance,addSiteKeyAdvance.index); router.register(VIEWS.addSiteKeyEasy, addSiteKeyEasy.index); -router.register(VIEWS.editSitekeyAdvance("[A-Z),a-z,0-9]+"), editSitekey.index); +router.register(VIEWS.editSitekeyAdvance("[A-Z),a-z,0-9]+"), editSitekeyAdvance.index); +router.register(VIEWS.editSitekeyEasy("[A-Z),a-z,0-9]+"), editSitekeyEasy.index); router.register(VIEWS.deleteSitekey("[A-Z),a-z,0-9]+"), deleteSitekey.index); try { diff --git a/templates/panel/sitekey/add/novice/ts/form.ts b/templates/panel/sitekey/add/novice/ts/form.ts index 2f86759d..087e7f92 100644 --- a/templates/panel/sitekey/add/novice/ts/form.ts +++ b/templates/panel/sitekey/add/novice/ts/form.ts @@ -45,9 +45,14 @@ export const break_my_site_name = "traffic that broke your website"; export const avg_traffic_name = "average"; export const peak_traffic_name = "maximum traffic your website can handle"; -const submit = async (e: Event) => { - e.preventDefault(); +type TrafficPattern = { + avg_traffic: number; + peak_sustainable_traffic: number; + broke_my_site_traffic?: number; + description: string; +}; +export const validate = (e: Event): TrafficPattern => { const description = validateDescription(e); let broke_is_set = false; @@ -89,8 +94,6 @@ const submit = async (e: Event) => { } } - const formUrl = getFormUrl(FORM); - const payload = { avg_traffic, peak_sustainable_traffic, @@ -98,6 +101,14 @@ const submit = async (e: Event) => { description, }; + return payload; +}; + +const submit = async (e: Event) => { + e.preventDefault(); + + const formUrl = getFormUrl(FORM); + const payload = validate(e); console.debug(`[form submition] json payload: ${JSON.stringify(payload)}`); const res = await fetch(formUrl, genJsonPayload(payload)); diff --git a/templates/panel/sitekey/edit/easy/form.html b/templates/panel/sitekey/edit/easy/form.html new file mode 100644 index 00000000..0f82e433 --- /dev/null +++ b/templates/panel/sitekey/edit/easy/form.html @@ -0,0 +1,67 @@ +
+
+

+ <.= form_title .> +

+ + Advance Options + +
+ + + + + + + + + + + +
diff --git a/templates/panel/sitekey/edit/easy/form.ts b/templates/panel/sitekey/edit/easy/form.ts new file mode 100644 index 00000000..76a01e33 --- /dev/null +++ b/templates/panel/sitekey/edit/easy/form.ts @@ -0,0 +1,51 @@ +/* + * 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 . + */ +import getFormUrl from "../../../../utils/getFormUrl"; +import genJsonPayload from "../../../../utils/genJsonPayload"; +import createError from "../../../../components/error"; + +import VIEWS from "../../../../views/v1/routes"; + +import { validate, FORM } from "../../add/novice/ts/form"; + +const SUBMIT_BTN = ( + document.querySelector(".sitekey-form__submit") +); +const key = SUBMIT_BTN.dataset.sitekey; +const submit = async (e: Event) => { + e.preventDefault(); + + const formUrl = getFormUrl(FORM); + const payload = { + pattern: validate(e), + key, + }; + console.debug(`[form submition] json payload: ${JSON.stringify(payload)}`); + + const res = await fetch(formUrl, genJsonPayload(payload)); + if (res.ok) { + window.location.assign(VIEWS.viewSitekey(key)); + } else { + const err = await res.json(); + createError(err.error); + } +}; + +const addSubmitEventListener = (): void => + FORM.addEventListener("submit", submit, true); + +export default addSubmitEventListener; diff --git a/templates/panel/sitekey/edit/easy/index.html b/templates/panel/sitekey/edit/easy/index.html new file mode 100644 index 00000000..0a0ee36f --- /dev/null +++ b/templates/panel/sitekey/edit/easy/index.html @@ -0,0 +1,15 @@ +<. include!("../../../../components/headers/index.html"); .> +<. include!("../../../navbar/index.html"); .> +
+<. include!("../../../header/index.html"); .> + +
+ <. include!("../../../help-banner/index.html"); .> + +
+ + + <. include!("./form.html"); .> +
+ +<. include!("../../../../components/footers.html"); .> diff --git a/templates/panel/sitekey/edit/easy/index.ts b/templates/panel/sitekey/edit/easy/index.ts new file mode 100644 index 00000000..808180c5 --- /dev/null +++ b/templates/panel/sitekey/edit/easy/index.ts @@ -0,0 +1,22 @@ +/* + * 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 . + */ + +import addSubmitEventListener from "./form"; + +export const index = (): void => { + addSubmitEventListener(); +};