mirror of
https://github.com/mCaptcha/mCaptcha.git
synced 2024-11-27 11:59:56 +03:00
notifications view
This commit is contained in:
parent
b7ec1bca22
commit
47cca5c9a7
14 changed files with 163 additions and 78 deletions
|
@ -107,6 +107,21 @@
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"45d9e9fb6344fe3a18c2529d50c935d3837bfe25c96595beb6970d6067720578": {
|
||||||
|
"query": "insert into mcaptcha_users \n (name , password, email, secret) values ($1, $2, $3, $4)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Left": [
|
||||||
|
"Varchar",
|
||||||
|
"Text",
|
||||||
|
"Varchar",
|
||||||
|
"Varchar"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
}
|
||||||
|
},
|
||||||
"47fa50aecfb1499b0a18fa9299643017a1a8d69d4e9980032e0d8f745465d14f": {
|
"47fa50aecfb1499b0a18fa9299643017a1a8d69d4e9980032e0d8f745465d14f": {
|
||||||
"query": "SELECT EXISTS (SELECT 1 from mcaptcha_users WHERE email = $1)",
|
"query": "SELECT EXISTS (SELECT 1 from mcaptcha_users WHERE email = $1)",
|
||||||
"describe": {
|
"describe": {
|
||||||
|
@ -506,21 +521,6 @@
|
||||||
"nullable": []
|
"nullable": []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"d64ed8a42ff8d8ff0db5a409c9d2ea7d61ea43c90e548a29a3a5a47679dbcd4b": {
|
|
||||||
"query": "INSERT INTO mcaptcha_users \n (name , password, email, secret) VALUES ($1, $2, $3, $4)",
|
|
||||||
"describe": {
|
|
||||||
"columns": [],
|
|
||||||
"parameters": {
|
|
||||||
"Left": [
|
|
||||||
"Varchar",
|
|
||||||
"Text",
|
|
||||||
"Varchar",
|
|
||||||
"Varchar"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"nullable": []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"d9a097cba4552c17b410fcb8745dd9b2eae5146f7b710006a50ae6aa2add54fa": {
|
"d9a097cba4552c17b410fcb8745dd9b2eae5146f7b710006a50ae6aa2add54fa": {
|
||||||
"query": "SELECT difficulty_factor, visitor_threshold FROM mcaptcha_levels WHERE\n config_id = (\n SELECT config_id FROM mcaptcha_config WHERE key = ($1)\n );",
|
"query": "SELECT difficulty_factor, visitor_threshold FROM mcaptcha_levels WHERE\n config_id = (\n SELECT config_id FROM mcaptcha_config WHERE key = ($1)\n );",
|
||||||
"describe": {
|
"describe": {
|
||||||
|
|
|
@ -154,8 +154,8 @@ pub mod runners {
|
||||||
let res;
|
let res;
|
||||||
if let Some(email) = &payload.email {
|
if let Some(email) = &payload.email {
|
||||||
res = sqlx::query!(
|
res = sqlx::query!(
|
||||||
"INSERT INTO mcaptcha_users
|
"insert into mcaptcha_users
|
||||||
(name , password, email, secret) VALUES ($1, $2, $3, $4)",
|
(name , password, email, secret) values ($1, $2, $3, $4)",
|
||||||
&username,
|
&username,
|
||||||
&hash,
|
&hash,
|
||||||
&email,
|
&email,
|
||||||
|
@ -182,7 +182,7 @@ pub mod runners {
|
||||||
if msg.contains("mcaptcha_users_name_key") {
|
if msg.contains("mcaptcha_users_name_key") {
|
||||||
return Err(ServiceError::UsernameTaken);
|
return Err(ServiceError::UsernameTaken);
|
||||||
} else if msg.contains("mcaptcha_users_email_key") {
|
} else if msg.contains("mcaptcha_users_email_key") {
|
||||||
return Err(ServiceError::EmailTaken);
|
return Err(ServiceError::UsernameTaken);
|
||||||
} else if msg.contains("mcaptcha_users_secret_key") {
|
} else if msg.contains("mcaptcha_users_secret_key") {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub mod account;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod mcaptcha;
|
pub mod mcaptcha;
|
||||||
pub mod meta;
|
pub mod meta;
|
||||||
mod notifications;
|
pub mod notifications;
|
||||||
pub mod pow;
|
pub mod pow;
|
||||||
mod routes;
|
mod routes;
|
||||||
|
|
||||||
|
|
|
@ -63,25 +63,39 @@ pub async fn get_notification(
|
||||||
let receiver = id.identity().unwrap();
|
let receiver = id.identity().unwrap();
|
||||||
// TODO handle error where payload.to doesnt exist
|
// TODO handle error where payload.to doesnt exist
|
||||||
|
|
||||||
let mut notifications = sqlx::query_file_as!(
|
let resp = runner::get_notification(&data, &receiver).await?;
|
||||||
Notification,
|
|
||||||
"src/api/v1/notifications/get_all_unread.sql",
|
|
||||||
&receiver
|
|
||||||
)
|
|
||||||
.fetch_all(&data.db)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let resp: Vec<NotificationResp> = notifications
|
|
||||||
.drain(0..)
|
|
||||||
.map(|x| {
|
|
||||||
let y: NotificationResp = x.into();
|
|
||||||
y
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(resp))
|
Ok(HttpResponse::Ok().json(resp))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod runner {
|
||||||
|
use super::*;
|
||||||
|
pub async fn get_notification(
|
||||||
|
data: &AppData,
|
||||||
|
receiver: &str,
|
||||||
|
) -> ServiceResult<Vec<NotificationResp>> {
|
||||||
|
// TODO handle error where payload.to doesnt exist
|
||||||
|
|
||||||
|
let mut notifications = sqlx::query_file_as!(
|
||||||
|
Notification,
|
||||||
|
"src/api/v1/notifications/get_all_unread.sql",
|
||||||
|
&receiver
|
||||||
|
)
|
||||||
|
.fetch_all(&data.db)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let resp = notifications
|
||||||
|
.drain(0..)
|
||||||
|
.map(|x| {
|
||||||
|
let y: NotificationResp = x.into();
|
||||||
|
y
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Ok(resp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::http::{header, StatusCode};
|
use actix_web::http::{header, StatusCode};
|
||||||
|
|
|
@ -15,9 +15,9 @@
|
||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
mod add;
|
pub mod add;
|
||||||
mod get;
|
pub mod get;
|
||||||
mod mark_read;
|
pub mod mark_read;
|
||||||
|
|
||||||
pub mod routes {
|
pub mod routes {
|
||||||
|
|
||||||
|
|
|
@ -44,11 +44,9 @@ async fn auth_works() {
|
||||||
confirm_password: PASSWORD.into(),
|
confirm_password: PASSWORD.into(),
|
||||||
email: None,
|
email: None,
|
||||||
};
|
};
|
||||||
let resp = test::call_service(
|
let resp =
|
||||||
&app,
|
test::call_service(&app, post_request!(&msg, ROUTES.auth.register).to_request())
|
||||||
post_request!(&msg, ROUTES.auth.register).to_request(),
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
// delete user
|
// delete user
|
||||||
delete_user(NAME, &data).await;
|
delete_user(NAME, &data).await;
|
||||||
|
|
14
src/data.rs
14
src/data.rs
|
@ -143,17 +143,19 @@ pub struct Data {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Data {
|
impl Data {
|
||||||
#[cfg(not(tarpaulin_include))]
|
pub fn get_creds() -> Config {
|
||||||
/// create new instance of app data
|
ConfigBuilder::default()
|
||||||
pub async fn new() -> Arc<Self> {
|
|
||||||
let creds = ConfigBuilder::default()
|
|
||||||
.username_case_mapped(true)
|
.username_case_mapped(true)
|
||||||
.profanity(true)
|
.profanity(true)
|
||||||
.blacklist(true)
|
.blacklist(true)
|
||||||
.password_policy(PasswordPolicy::default())
|
.password_policy(PasswordPolicy::default())
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
}
|
||||||
|
#[cfg(not(tarpaulin_include))]
|
||||||
|
/// create new instance of app data
|
||||||
|
pub async fn new() -> Arc<Self> {
|
||||||
|
let creds = Self::get_creds();
|
||||||
let c = creds.clone();
|
let c = creds.clone();
|
||||||
|
|
||||||
let init = thread::spawn(move || {
|
let init = thread::spawn(move || {
|
||||||
|
|
|
@ -123,11 +123,9 @@ mod tests {
|
||||||
|
|
||||||
let uri = format!("{}{}", DOCS.home, FILE);
|
let uri = format!("{}{}", DOCS.home, FILE);
|
||||||
|
|
||||||
let resp = test::call_service(
|
let resp =
|
||||||
&app,
|
test::call_service(&app, test::TestRequest::get().uri(&uri).to_request())
|
||||||
test::TestRequest::get().uri(&uri).to_request(),
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,14 +60,13 @@ mod tests {
|
||||||
PAGES.home,
|
PAGES.home,
|
||||||
PAGES.panel.sitekey.add,
|
PAGES.panel.sitekey.add,
|
||||||
PAGES.panel.sitekey.list,
|
PAGES.panel.sitekey.list,
|
||||||
|
PAGES.panel.notifications,
|
||||||
];
|
];
|
||||||
|
|
||||||
for url in urls.iter() {
|
for url in urls.iter() {
|
||||||
let resp = test::call_service(
|
let resp =
|
||||||
&app,
|
test::call_service(&app, test::TestRequest::get().uri(url).to_request())
|
||||||
test::TestRequest::get().uri(url).to_request(),
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(resp.status(), StatusCode::FOUND);
|
assert_eq!(resp.status(), StatusCode::FOUND);
|
||||||
|
|
||||||
let authenticated_resp = test::call_service(
|
let authenticated_resp = test::call_service(
|
||||||
|
@ -91,11 +90,9 @@ mod tests {
|
||||||
let urls = vec![PAGES.auth.login, PAGES.auth.join];
|
let urls = vec![PAGES.auth.login, PAGES.auth.join];
|
||||||
|
|
||||||
for url in urls.iter() {
|
for url in urls.iter() {
|
||||||
let resp = test::call_service(
|
let resp =
|
||||||
&app,
|
test::call_service(&app, test::TestRequest::get().uri(url).to_request())
|
||||||
test::TestRequest::get().uri(url).to_request(),
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ use actix_identity::Identity;
|
||||||
use actix_web::{HttpResponse, Responder};
|
use actix_web::{HttpResponse, Responder};
|
||||||
use sailfish::TemplateOnce;
|
use sailfish::TemplateOnce;
|
||||||
|
|
||||||
|
mod notifications;
|
||||||
pub mod sitekey;
|
pub mod sitekey;
|
||||||
|
|
||||||
use crate::errors::PageResult;
|
use crate::errors::PageResult;
|
||||||
|
@ -51,6 +52,7 @@ async fn panel(data: AppData, id: Identity) -> PageResult<impl Responder> {
|
||||||
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
pub fn services(cfg: &mut actix_web::web::ServiceConfig) {
|
||||||
cfg.service(panel);
|
cfg.service(panel);
|
||||||
sitekey::services(cfg);
|
sitekey::services(cfg);
|
||||||
|
cfg.service(notifications::notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub mod routes {
|
pub mod routes {
|
||||||
|
@ -58,6 +60,7 @@ pub mod routes {
|
||||||
pub struct Panel {
|
pub struct Panel {
|
||||||
pub home: &'static str,
|
pub home: &'static str,
|
||||||
pub sitekey: Sitekey,
|
pub sitekey: Sitekey,
|
||||||
|
pub notifications: &'static str,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Panel {
|
impl Panel {
|
||||||
|
@ -65,6 +68,7 @@ pub mod routes {
|
||||||
Panel {
|
Panel {
|
||||||
home: "/",
|
home: "/",
|
||||||
sitekey: Sitekey::new(),
|
sitekey: Sitekey::new(),
|
||||||
|
notifications: "/notifications",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
52
src/pages/panel/notifications.rs
Normal file
52
src/pages/panel/notifications.rs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
use actix_identity::Identity;
|
||||||
|
use actix_web::{HttpResponse, Responder};
|
||||||
|
use sailfish::TemplateOnce;
|
||||||
|
|
||||||
|
use crate::api::v1::notifications::get::{runner, NotificationResp};
|
||||||
|
use crate::errors::PageResult;
|
||||||
|
use crate::AppData;
|
||||||
|
|
||||||
|
#[derive(TemplateOnce)]
|
||||||
|
#[template(path = "panel/notifications/index.html")]
|
||||||
|
pub struct IndexPage {
|
||||||
|
/// notifications
|
||||||
|
n: Vec<NotificationResp>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexPage {
|
||||||
|
fn new(n: Vec<NotificationResp>) -> Self {
|
||||||
|
IndexPage { n }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PAGE: &str = "Notifications";
|
||||||
|
|
||||||
|
#[my_codegen::get(path = "crate::PAGES.panel.notifications", wrap = "crate::CheckLogin")]
|
||||||
|
pub async fn notifications(data: AppData, id: Identity) -> PageResult<impl Responder> {
|
||||||
|
let receiver = id.identity().unwrap();
|
||||||
|
// TODO handle error where payload.to doesnt exist
|
||||||
|
|
||||||
|
let notifications = runner::get_notification(&data, &receiver).await?;
|
||||||
|
|
||||||
|
let body = IndexPage::new(notifications).render_once().unwrap();
|
||||||
|
Ok(HttpResponse::Ok()
|
||||||
|
.content_type("text/html; charset=utf-8")
|
||||||
|
.body(body))
|
||||||
|
}
|
|
@ -51,11 +51,9 @@ macro_rules! post_request {
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! get_works {
|
macro_rules! get_works {
|
||||||
($app:expr,$route:expr ) => {
|
($app:expr,$route:expr ) => {
|
||||||
let list_sitekey_resp = test::call_service(
|
let list_sitekey_resp =
|
||||||
&$app,
|
test::call_service(&$app, test::TestRequest::get().uri($route).to_request())
|
||||||
test::TestRequest::get().uri($route).to_request(),
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(list_sitekey_resp.status(), StatusCode::OK);
|
assert_eq!(list_sitekey_resp.status(), StatusCode::OK);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -116,11 +114,9 @@ pub async fn register(name: &str, email: &str, password: &str) {
|
||||||
confirm_password: password.into(),
|
confirm_password: password.into(),
|
||||||
email: Some(email.into()),
|
email: Some(email.into()),
|
||||||
};
|
};
|
||||||
let resp = test::call_service(
|
let resp =
|
||||||
&app,
|
test::call_service(&app, post_request!(&msg, ROUTES.auth.register).to_request())
|
||||||
post_request!(&msg, ROUTES.auth.register).to_request(),
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,11 +130,9 @@ pub async fn signin(name: &str, password: &str) -> (Arc<Data>, Login, ServiceRes
|
||||||
login: name.into(),
|
login: name.into(),
|
||||||
password: password.into(),
|
password: password.into(),
|
||||||
};
|
};
|
||||||
let signin_resp = test::call_service(
|
let signin_resp =
|
||||||
&app,
|
test::call_service(&app, post_request!(&creds, ROUTES.auth.login).to_request())
|
||||||
post_request!(&creds, ROUTES.auth.login).to_request(),
|
.await;
|
||||||
)
|
|
||||||
.await;
|
|
||||||
assert_eq!(signin_resp.status(), StatusCode::OK);
|
assert_eq!(signin_resp.status(), StatusCode::OK);
|
||||||
(data, creds, signin_resp)
|
(data, creds, signin_resp)
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="taskbar__action">
|
<li class="taskbar__action">
|
||||||
|
<a href="<.= crate::PAGES.panel.notifications .>">
|
||||||
<img class="taskbar__icon" src="<.=
|
<img class="taskbar__icon" src="<.=
|
||||||
crate::FILES.get("./static/cache/img/svg/bell.svg").unwrap() .>"
|
crate::FILES.get("./static/cache/img/svg/bell.svg").unwrap() .>"
|
||||||
alt="Notifications" />
|
alt="Notifications" />
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li class="taskbar__action">
|
<li class="taskbar__action">
|
||||||
|
|
24
templates/panel/notifications/index.html
Normal file
24
templates/panel/notifications/index.html
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
<. include!("../../components/headers/index.html"); .>
|
||||||
|
<. include!("../navbar/index.html"); .>
|
||||||
|
<div class="tmp-layout">
|
||||||
|
<. include!("../header/index.html"); .>
|
||||||
|
|
||||||
|
<main class="panel-main">
|
||||||
|
<!-- Main content container -->
|
||||||
|
<div class="inner-container">
|
||||||
|
<!-- Main menu/ important actions roaster -->
|
||||||
|
|
||||||
|
<ul class="sitekey-list__box">
|
||||||
|
<h1 class="sitekey-list__title">Your Notifications</h1>
|
||||||
|
<. for notification in n.iter() { .>
|
||||||
|
<li class="sitekey-list__item">
|
||||||
|
<h3><.= notification.heading .> </h3>
|
||||||
|
<p>From: <.= notification.name .> </p>
|
||||||
|
<p>Received: <.= notification.received .> </p>
|
||||||
|
<p>Message: <.= notification.message .> </p>
|
||||||
|
<. } .>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<!-- end of container -->
|
||||||
|
<. include!("../../components/footers.html"); .>
|
Loading…
Reference in a new issue