diff --git a/src/pages/panel/sitekey/add.rs b/src/pages/panel/sitekey/add.rs index a9b38b65..b7f57160 100644 --- a/src/pages/panel/sitekey/add.rs +++ b/src/pages/panel/sitekey/add.rs @@ -22,21 +22,23 @@ use sailfish::TemplateOnce; const PAGE: &str = "Add Sitekey"; lazy_static! { - static ref INDEX: String = IndexPage::default().render_once().unwrap(); + static ref ADVANCE_INDEX: String = + AdvanceIndexPage::default().render_once().unwrap(); + static ref EASY_INDEX: String = EasyIndexPage::default().render_once().unwrap(); } #[derive(TemplateOnce, Clone)] #[template(path = "panel/sitekey/add/advance/index.html")] -pub struct IndexPage<'a> { +pub struct AdvanceIndexPage<'a> { pub levels: usize, pub form_title: &'a str, pub form_description: &'a str, pub form_duration: usize, } -impl<'a> Default for IndexPage<'a> { +impl<'a> Default for AdvanceIndexPage<'a> { fn default() -> Self { - IndexPage { + Self { levels: 1, form_description: "", form_title: PAGE, @@ -45,9 +47,44 @@ impl<'a> Default for IndexPage<'a> { } } -#[my_codegen::get(path = "crate::PAGES.panel.sitekey.add", wrap = "crate::CheckLogin")] -pub async fn add_sitekey() -> impl Responder { +#[my_codegen::get( + path = "crate::PAGES.panel.sitekey.add_advance", + wrap = "crate::CheckLogin" +)] +pub async fn advance() -> impl Responder { HttpResponse::Ok() .content_type("text/html; charset=utf-8") - .body(&*INDEX) + .body(&*ADVANCE_INDEX) +} + +#[derive(TemplateOnce, Clone)] +#[template(path = "panel/sitekey/add/novice/index.html")] +pub struct EasyIndexPage<'a> { + pub form_description: &'a str, + pub form_title: &'a str, + pub peak_sustainable_traffic: Option, + pub avg_traffic: Option, + pub broke_my_site_traffic: Option, +} + +impl<'a> Default for EasyIndexPage<'a> { + fn default() -> Self { + Self { + form_description: "", + peak_sustainable_traffic: None, + avg_traffic: None, + broke_my_site_traffic: None, + form_title: PAGE, + } + } +} + +#[my_codegen::get( + path = "crate::PAGES.panel.sitekey.add_easy", + wrap = "crate::CheckLogin" +)] +pub async fn easy() -> impl Responder { + HttpResponse::Ok() + .content_type("text/html; charset=utf-8") + .body(&*EASY_INDEX) } diff --git a/templates/panel/sitekey/add/novice/form.html b/templates/panel/sitekey/add/novice/form.html new file mode 100644 index 00000000..141fb8bb --- /dev/null +++ b/templates/panel/sitekey/add/novice/form.html @@ -0,0 +1,72 @@ +
+
+

+ <.= form_title .> +

+ + Advance Options + +
+ + + + + + + + + + + +
diff --git a/templates/panel/sitekey/add/novice/index.html b/templates/panel/sitekey/add/novice/index.html new file mode 100644 index 00000000..0a0ee36f --- /dev/null +++ b/templates/panel/sitekey/add/novice/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/add/novice/ts/form.test.ts b/templates/panel/sitekey/add/novice/ts/form.test.ts new file mode 100644 index 00000000..a0ab42a1 --- /dev/null +++ b/templates/panel/sitekey/add/novice/ts/form.test.ts @@ -0,0 +1,164 @@ +/* + * 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 { + getAddForm, + fillAvgTraffic, + fillDescription, + fillPeakSustainable, + fillBrokemySite, +} from "./setupTests"; +import setup from "../../../../../components/error/setUpTests"; + +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"; + +beforeEach(() => { + document.body.innerHTML = getAddForm(); + document.body.appendChild(setup()); +}); + +afterEach(() => { + document.body.replaceWith(document.createElement("body")); +}); + +const checkEmpty = (e: Error, name: string) => { + expect(e.message.includes(name)).toBeTruthy(); + expect(e.message.includes(name)).toBeTruthy(); +}; + +it("empty description", () => { + const form = document.querySelector("form"); + + fillAvgTraffic(1); + fillPeakSustainable(2); + try { + form.submit(); + } catch (e) { + checkEmpty(e, "description"); + } +}); + +it("empty average traffic", () => { + const form = document.querySelector("form"); + + fillDescription("foo"); + fillPeakSustainable(2); + try { + form.submit(); + } catch (e) { + checkEmpty(e, avg_traffic_name); + } +}); + +it("empty peak traffic", () => { + const form = document.querySelector("form"); + fillDescription("foo"); + fillAvgTraffic(1); + try { + form.submit(); + } catch (e) { + checkEmpty(e, peak_traffic_name); + } +}); + +const checkNan = (e: Error, name: string) => { + expect(e.message.includes(`${name} must be a number`)).toBeTruthy(); +}; + +it("NAN peak traffic", () => { + const form = document.querySelector("form"); + fillDescription("foo"); + fillAvgTraffic(1); + fillPeakSustainable("foo"); + try { + form.submit(); + } catch (e) { + checkNan(e, peak_traffic_name); + } +}); + +it("NAN Avg Traffic traffic", () => { + const form = document.querySelector("form"); + fillDescription("foo"); + fillAvgTraffic("foo"); + fillPeakSustainable(1); + try { + form.submit(); + } catch (e) { + checkNan(e, avg_traffic_name); + } +}); + +it("NAN Break my site Traffic traffic", () => { + const form = document.querySelector("form"); + fillDescription("foo"); + fillAvgTraffic(1); + fillPeakSustainable(1); + fillBrokemySite("foo"); + try { + form.submit(); + } catch (e) { + checkNan(e, break_my_site_name); + } +}); + +const GetMustB = (lhs: string, rhs: string) => + `${lhs} must be greater than ${rhs}`; +const CheckMustBeGreater = (e: Error, lhs: string, rhs: string) => { + const msg = GetMustB(lhs, rhs); + expect(e.message.includes(msg)).toBeTruthy(); +}; + +it(GetMustB(break_my_site_name, peak_traffic_name), () => { + const form = document.querySelector("form"); + fillDescription("foo"); + fillAvgTraffic(100); + fillPeakSustainable(1000); + fillBrokemySite(999); + try { + form.submit(); + } catch (e) { + CheckMustBeGreater(e, break_my_site_name, peak_traffic_name); + } + + fillBrokemySite(1000); + try { + form.submit(); + } catch (e) { + CheckMustBeGreater(e, break_my_site_name, peak_traffic_name); + } +}); + +it(GetMustB(peak_traffic_name, avg_traffic_name), () => { + const form = document.querySelector("form"); + fillDescription("foo"); + fillAvgTraffic(1000); + fillPeakSustainable(999); + try { + form.submit(); + } catch (e) { + CheckMustBeGreater(e, peak_traffic_name, avg_traffic_name); + } + + fillPeakSustainable(1000); + try { + form.submit(); + } catch (e) { + CheckMustBeGreater(e, peak_traffic_name, avg_traffic_name); + } +}); diff --git a/templates/panel/sitekey/add/novice/ts/form.ts b/templates/panel/sitekey/add/novice/ts/form.ts new file mode 100644 index 00000000..2f86759d --- /dev/null +++ b/templates/panel/sitekey/add/novice/ts/form.ts @@ -0,0 +1,113 @@ +/* + * 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 isBlankString from "../../../../../utils/isBlankString"; +import isNumber from "../../../../../utils/isNumber"; + +import VIEWS from "../../../../../views/v1/routes"; + +import validateDescription from "../../advance/ts/form/validateDescription"; + +import createError from "../../../../../components/error"; + +export const SITE_KEY_FORM_CLASS = "sitekey-form"; +export const FORM = ( + document.querySelector(`.${SITE_KEY_FORM_CLASS}`) +); + +export const AVG_TRAFFIC = FORM.querySelector("#avg_traffic"); +export const PEAK_TRAFFIC = ( + FORM.querySelector("#peak_sustainable_traffic") +); +export const BROKE_MY_SITE_TRAFFIC = ( + FORM.querySelector("#broke_my_site_traffic") +); + +export const addSubmitEventListener = (): void => + FORM.addEventListener("submit", submit, true); + +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(); + + const description = validateDescription(e); + + let broke_is_set = false; + + isBlankString(AVG_TRAFFIC.value, avg_traffic_name); + isBlankString(PEAK_TRAFFIC.value, peak_traffic_name); + + const numberCheck = (name: string, field: HTMLInputElement) => { + if (!isNumber(field.value)) { + createError(`${name} must be a number`); + throw new Error(`${name} must be a number`); + } + return true; + }; + + numberCheck(avg_traffic_name, AVG_TRAFFIC); + numberCheck(peak_traffic_name, PEAK_TRAFFIC); + if (BROKE_MY_SITE_TRAFFIC.value.trim().length > 0) { + numberCheck(break_my_site_name, BROKE_MY_SITE_TRAFFIC); + broke_is_set = true; + } + + const avg_traffic = Number.parseInt(AVG_TRAFFIC.value); + const peak_sustainable_traffic = Number.parseInt(PEAK_TRAFFIC.value); + let broke_my_site_traffic = null; + + const mustBeGreater = (lhs: string, rhs: string) => { + const msg = `${lhs} must be greater than ${rhs}`; + createError(msg); + throw new Error(msg); + }; + + if (avg_traffic >= peak_sustainable_traffic) { + mustBeGreater(peak_traffic_name, avg_traffic_name); + } else if (broke_is_set) { + broke_my_site_traffic = Number.parseInt(BROKE_MY_SITE_TRAFFIC.value); + if (peak_sustainable_traffic >= broke_my_site_traffic) { + mustBeGreater(break_my_site_name, peak_traffic_name); + } + } + + const formUrl = getFormUrl(FORM); + + const payload = { + avg_traffic, + peak_sustainable_traffic, + broke_my_site_traffic, + description, + }; + + console.debug(`[form submition] json payload: ${JSON.stringify(payload)}`); + + const res = await fetch(formUrl, genJsonPayload(payload)); + if (res.ok) { + const data = await res.json(); + window.location.assign(VIEWS.viewSitekey(data.key)); + } else { + const err = await res.json(); + createError(err.error); + } +}; + +export default addSubmitEventListener; diff --git a/templates/panel/sitekey/add/novice/ts/index.ts b/templates/panel/sitekey/add/novice/ts/index.ts new file mode 100644 index 00000000..808180c5 --- /dev/null +++ b/templates/panel/sitekey/add/novice/ts/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(); +}; diff --git a/templates/panel/sitekey/add/novice/ts/setupTests.ts b/templates/panel/sitekey/add/novice/ts/setupTests.ts new file mode 100644 index 00000000..b1a789ac --- /dev/null +++ b/templates/panel/sitekey/add/novice/ts/setupTests.ts @@ -0,0 +1,90 @@ +/* + * 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 . + */ +export { trim, fillDescription } from "../../advance/ts/setupTests"; + +const fillField = (id: string, value: number | string) => { + const inputElement = document.getElementById(id); + inputElement.value = value.toString(); +}; + +/** Fill peak sustainable traffic in add captcha form */ +export const fillPeakSustainable = (traffic: number | string): void => + fillField("peak_sustainable_traffic", traffic); + +/** Fill average traffic in add captcha form */ +export const fillAvgTraffic = (traffic: number | string): void => + fillField("avg_traffic", traffic); + +/** Fill broke_my_site_traffic in add captcha form */ +export const fillBrokemySite = (traffic: number | string): void => + fillField("broke_my_site_traffic", traffic); + +export const getAddForm = (): string => + ` +
+

+ Add Sitekey +

+ + + + + + + + +
+`;