Implement easy edit view
When user tries to visit this view without an easy configuration available, i.e, user had created the CAPTCHA using advance view and no TrafficPattern is available in database, the user will be automatically redirected to the advance edit page. But the default edit link everywhere is to the easy edit view.
This commit is contained in:
parent
ebde9775fc
commit
c46b3f4f4c
9 changed files with 332 additions and 9 deletions
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,13 +15,15 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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<String>,
|
||||
data: AppData,
|
||||
id: Identity,
|
||||
) -> PageResult<impl Responder> {
|
||||
let username = id.identity().unwrap();
|
||||
let key = path.into_inner();
|
||||
|
||||
struct Traffic {
|
||||
peak_sustainable_traffic: i32,
|
||||
avg_traffic: i32,
|
||||
broke_my_site_traffic: Option<i32>,
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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));
|
||||
|
|
67
templates/panel/sitekey/edit/easy/form.html
Normal file
67
templates/panel/sitekey/edit/easy/form.html
Normal file
|
@ -0,0 +1,67 @@
|
|||
<form class="sitekey-form" action="<.= crate::V1_API_ROUTES.captcha.easy.update .>" method="post">
|
||||
<div class="sitekey-form__advance-options-container">
|
||||
<h1 class="sitekey-form__advance-options-form-title">
|
||||
<.= form_title .>
|
||||
</h1>
|
||||
<a
|
||||
class="sitekey-form__advance-options-link"
|
||||
href="<.= crate::PAGES.panel.sitekey.get_edit_advance(&key) .>">
|
||||
Advance Options
|
||||
</a>
|
||||
</div>
|
||||
<label class="sitekey-form__label" for="description">
|
||||
Description
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
type="text"
|
||||
name="description"
|
||||
id="description"
|
||||
required
|
||||
value="<.= pattern.description .>"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="sitekey-form__label" for="avg_traffic">
|
||||
Average Traffic of your website
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
type="number"
|
||||
name="avg_traffic"
|
||||
id="avg_traffic"
|
||||
required
|
||||
value="<.= pattern.avg_traffic .>"
|
||||
/>
|
||||
</label>
|
||||
|
||||
|
||||
|
||||
<label class="sitekey-form__label" for="avg_traffic">
|
||||
Maximum traffic that your website can handle
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
type="number"
|
||||
name="peak_sustainable_traffic"
|
||||
id="peak_sustainable_traffic"
|
||||
required
|
||||
value="<.= pattern.peak_sustainable_traffic .>"
|
||||
/>
|
||||
</label>
|
||||
|
||||
<label class="sitekey-form__label" for="avg_traffic">
|
||||
Traffic that broke your website(Optional)
|
||||
<input
|
||||
class="sitekey-form__input"
|
||||
type="number"
|
||||
name="broke_my_site_traffic"
|
||||
id="broke_my_site_traffic"
|
||||
<. if let Some(broke_my_site_traffic) = pattern.broke_my_site_traffic { .>
|
||||
value="<.= broke_my_site_traffic .>"
|
||||
<. } .>
|
||||
|
||||
/>
|
||||
</label>
|
||||
|
||||
<button data-sitekey="<.= key .>" class="sitekey-form__submit" type="submit">
|
||||
Submit
|
||||
</button>
|
||||
</form>
|
51
templates/panel/sitekey/edit/easy/form.ts
Normal file
51
templates/panel/sitekey/edit/easy/form.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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 = <HTMLButtonElement>(
|
||||
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;
|
15
templates/panel/sitekey/edit/easy/index.html
Normal file
15
templates/panel/sitekey/edit/easy/index.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<. include!("../../../../components/headers/index.html"); .>
|
||||
<. include!("../../../navbar/index.html"); .>
|
||||
<div class="tmp-layout">
|
||||
<. include!("../../../header/index.html"); .>
|
||||
|
||||
<main class="panel-main">
|
||||
<. include!("../../../help-banner/index.html"); .>
|
||||
<!-- Main content container -->
|
||||
<div class="inner-container">
|
||||
<!-- Main menu/ important actions roaster -->
|
||||
|
||||
<. include!("./form.html"); .>
|
||||
</div>
|
||||
<!-- end of container -->
|
||||
<. include!("../../../../components/footers.html"); .>
|
22
templates/panel/sitekey/edit/easy/index.ts
Normal file
22
templates/panel/sitekey/edit/easy/index.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (C) 2021 Aravinth Manivannan <realaravinth@batsense.net>
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import addSubmitEventListener from "./form";
|
||||
|
||||
export const index = (): void => {
|
||||
addSubmitEventListener();
|
||||
};
|
Loading…
Reference in a new issue