From e46d858545394d6524128edafc740f84c14925ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sn=C3=ADda=C5=88ov=C3=BD=20Mistr?= Date: Fri, 8 Mar 2024 22:05:21 +0100 Subject: [PATCH] =?UTF-8?q?S=C3=A9r=C4=8D=20bar=20a=20aktu=C3=A1ln=C3=AD?= =?UTF-8?q?=20po=C4=8Det=20hl=C3=A1=C5=A1en=C3=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.rs | 2 +- src/db/local_stats.rs | 4 +- src/db/post.rs | 35 +++++++++++ src/main.rs | 3 +- src/web/actions/create_post.rs | 2 + src/web/mod.rs | 1 + src/web/search.rs | 84 +++++++++++++++++++++++++ src/web/tcx.rs | 17 +++++ static/js/autofill.js | 78 ++++++++++++----------- static/js/post-form.js | 9 +-- templates/base.html | 3 + templates/board-catalog.html | 14 +++++ templates/ip-posts.html | 7 ++- templates/macros/post-form.html | 2 +- templates/macros/static-pagination.html | 17 +++-- templates/news.html | 5 +- templates/overboard-catalog.html | 13 ++++ templates/search.html | 47 ++++++++++++++ templates/staff/reports.html | 2 +- 19 files changed, 290 insertions(+), 55 deletions(-) create mode 100644 src/web/search.rs create mode 100644 templates/search.html diff --git a/build.rs b/build.rs index 49224eb..0dcc6d4 100755 --- a/build.rs +++ b/build.rs @@ -30,7 +30,7 @@ fn main() -> Result<(), Error> { } let html = read_to_string(&path)?; - let minified = minify(html)?.replace('\n', ""); + let minified = minify(html)?.replace('\n', "").replace(" ", " "); File::create(path)?.write_all(minified.as_bytes())?; } diff --git a/src/db/local_stats.rs b/src/db/local_stats.rs index 1f04679..136ee99 100644 --- a/src/db/local_stats.rs +++ b/src/db/local_stats.rs @@ -6,13 +6,13 @@ use crate::{ctx::Ctx, error::NekrochanError}; impl LocalStats { pub async fn read(ctx: &Ctx) -> Result { let (post_count,) = query_as( - "SELECT coalesce(sum(last_value)::bigint, 0) FROM pg_sequences WHERE sequencename LIKE 'posts_%_id_seq'", + "SELECT COALESCE(SUM(last_value)::bigint, 0) FROM pg_sequences WHERE sequencename LIKE 'posts_%_id_seq'", ) .fetch_one(ctx.db()) .await?; let (file_count, file_size) = query_as( - r#"SELECT count(files), coalesce(sum((files->>'size')::bigint)::bigint, 0) FROM ( + r#"SELECT COUNT(files), COALESCE(SUM((files->>'size')::bigint)::bigint, 0) FROM ( SELECT jsonb_array_elements(files) AS files FROM overboard ) flatten"#, ) diff --git a/src/db/post.rs b/src/db/post.rs index 3116d2a..379b6e6 100755 --- a/src/db/post.rs +++ b/src/db/post.rs @@ -277,6 +277,41 @@ impl Post { Ok(posts) } + pub async fn read_by_query( + ctx: &Ctx, + board: &Board, + query: String, + page: i64, + ) -> Result, NekrochanError> { + let posts = query_as(&format!( + "SELECT * FROM posts_{} WHERE LOWER(content_nomarkup) LIKE LOWER($1) LIMIT $2 OFFSET $3", + board.id + )) + .bind(format!("%{query}%")) + .bind(board.config.0.page_size) + .bind((page - 1) * board.config.0.page_size) + .fetch_all(ctx.db()) + .await?; + + Ok(posts) + } + + pub async fn read_by_query_overboard( + ctx: &Ctx, + query: String, + page: i64, + ) -> Result, NekrochanError> { + let posts = + query_as("SELECT * FROM overboard WHERE LOWER(content_nomarkup) LIKE LOWER($1) LIMIT $2 OFFSET $3") + .bind(format!("%{query}%")) + .bind(GENERIC_PAGE_SIZE) + .bind((page - 1) * GENERIC_PAGE_SIZE) + .fetch_all(ctx.db()) + .await?; + + Ok(posts) + } + pub async fn update_user_id(&self, ctx: &Ctx, user_id: String) -> Result<(), NekrochanError> { let post = query_as(&format!( "UPDATE posts_{} SET user_id = $1 WHERE id = $2 RETURNING *", diff --git a/src/main.rs b/src/main.rs index 5bba90a..0d03e90 100755 --- a/src/main.rs +++ b/src/main.rs @@ -74,8 +74,9 @@ async fn run() -> Result<(), Error> { .service(web::overboard::overboard) .service(web::overboard_catalog::overboard_catalog) .service(web::page::page) - .service(web::thread_json::thread_json) + .service(web::search::search) .service(web::thread::thread) + .service(web::thread_json::thread_json) .service(web::actions::appeal_ban::appeal_ban) .service(web::actions::create_post::create_post) .service(web::actions::edit_posts::edit_posts) diff --git a/src/web/actions/create_post.rs b/src/web/actions/create_post.rs index bc11d3f..b138249 100644 --- a/src/web/actions/create_post.rs +++ b/src/web/actions/create_post.rs @@ -262,9 +262,11 @@ pub async fn create_post( let name_cookie = Cookie::build("name", name_raw).path("/").finish(); let password_cookie = Cookie::build("password", password_raw).path("/").finish(); + let email_cookie = Cookie::build("email", email_raw).path("/").finish(); res.cookie(name_cookie); res.cookie(password_cookie); + res.cookie(email_cookie); let res = if noko { res.append_header(("Location", post.post_url().as_str())) diff --git a/src/web/mod.rs b/src/web/mod.rs index 88586ad..25a4240 100755 --- a/src/web/mod.rs +++ b/src/web/mod.rs @@ -12,6 +12,7 @@ pub mod news; pub mod overboard; pub mod overboard_catalog; pub mod page; +pub mod search; pub mod staff; pub mod tcx; pub mod thread; diff --git a/src/web/search.rs b/src/web/search.rs new file mode 100644 index 0000000..720cf9f --- /dev/null +++ b/src/web/search.rs @@ -0,0 +1,84 @@ +use actix_web::{ + get, + web::{Data, Query}, + HttpRequest, HttpResponse, +}; +use askama::Template; +use serde::Deserialize; +use std::collections::HashMap; + +use super::tcx::TemplateCtx; +use crate::{ + ctx::Ctx, + db::models::{Board, Post}, + error::NekrochanError, + filters, web::template_response, +}; + +#[derive(Template)] +#[template(path = "search.html")] +struct SearchTemplate { + tcx: TemplateCtx, + board_opt: Option, + boards: HashMap, + query: String, + posts: Vec, + page: i64, +} + +#[derive(Deserialize)] +pub struct SearchQuery { + board: Option, + query: String, + page: Option, +} + +#[get("/search")] +pub async fn search( + ctx: Data, + req: HttpRequest, + Query(query): Query, +) -> Result { + let tcx = TemplateCtx::new(&ctx, &req).await?; + + let board_opt = if let Some(board) = query.board { + let board = Board::read(&ctx, board.clone()) + .await? + .ok_or(NekrochanError::BoardNotFound(board))?; + + Some(board) + } else { + None + }; + + let boards = if board_opt.is_none() { + Board::read_all_map(&ctx).await? + } else { + HashMap::new() + }; + + let page = query.page.unwrap_or(1); + + if page <= 0 { + return Err(NekrochanError::InvalidPageError); + } + + let query = query.query; + + let posts = if let Some(board) = &board_opt { + Post::read_by_query(&ctx, board, query.clone(), page).await? + } else { + Post::read_by_query_overboard(&ctx, query.clone(), page).await? + }; + + let template = SearchTemplate { + tcx, + board_opt, + boards, + query, + posts, + page, + }; + + template_response(&template) +} diff --git a/src/web/tcx.rs b/src/web/tcx.rs index b435c3c..e721af0 100755 --- a/src/web/tcx.rs +++ b/src/web/tcx.rs @@ -1,5 +1,6 @@ use actix_web::HttpRequest; use redis::{AsyncCommands, Commands, Connection}; +use sqlx::query_as; use std::{ collections::HashSet, net::{IpAddr, Ipv4Addr}, @@ -18,6 +19,7 @@ pub struct TemplateCtx { pub perms: PermissionWrapper, pub ip: IpAddr, pub yous: HashSet, + pub report_count: Option, } impl TemplateCtx { @@ -37,6 +39,20 @@ impl TemplateCtx { let account = account.map(|account| account.username); + let report_count = if perms.owner() || perms.reports() { + let count: Option> = query_as("SELECT SUM(jsonb_array_length(reports)) FROM overboard WHERE reports != '[]'::jsonb") + .fetch_optional(ctx.db()) + .await + .ok(); + + match count { + Some(Some((count,))) if count != 0 => Some(count), + _ => None, + } + } else { + None + }; + let tcx = Self { cfg, boards, @@ -44,6 +60,7 @@ impl TemplateCtx { ip, yous, account, + report_count, }; Ok(tcx) diff --git a/static/js/autofill.js b/static/js/autofill.js index 9aad955..cb3a8b4 100644 --- a/static/js/autofill.js +++ b/static/js/autofill.js @@ -1,6 +1,7 @@ $(function () { let name = get_cookie("name"); let password = get_cookie("password"); + let email = get_cookie("email"); if (password === "") { password = generate_password(); @@ -9,42 +10,43 @@ $(function () { $('input[name="post_name"]').attr("value", name); $('input[name="post_password"]').attr("value", password); + $('input[name="email"]').attr("value", email); + + function generate_password() { + let chars = + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + let password_length = 8; + let password = ""; + + for (let i = 0; i <= password_length; i++) { + let random_number = Math.floor(Math.random() * chars.length); + password += chars.substring(random_number, random_number + 1); + } + + return password; + } + + function get_cookie(cname) { + let name = cname + "="; + let decodedCookie = decodeURIComponent(document.cookie); + let ca = decodedCookie.split(";"); + + for (let i = 0; i < ca.length; i++) { + let c = ca[i]; + + while (c.charAt(0) == " ") { + c = c.substring(1); + } + + if (c.indexOf(name) == 0) { + return c.substring(name.length, c.length); + } + } + + return ""; + } + + function set_cookie(cname, cvalue) { + document.cookie = `${cname}=${cvalue};path=/`; + } }); - -function generate_password() { - let chars = - "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; - let password_length = 8; - let password = ""; - - for (let i = 0; i <= password_length; i++) { - let random_number = Math.floor(Math.random() * chars.length); - password += chars.substring(random_number, random_number + 1); - } - - return password; -} - -function get_cookie(cname) { - let name = cname + "="; - let decodedCookie = decodeURIComponent(document.cookie); - let ca = decodedCookie.split(";"); - - for (let i = 0; i < ca.length; i++) { - let c = ca[i]; - - while (c.charAt(0) == " ") { - c = c.substring(1); - } - - if (c.indexOf(name) == 0) { - return c.substring(name.length, c.length); - } - } - - return ""; -} - -function set_cookie(cname, cvalue) { - document.cookie = `${cname}=${cvalue};path=/`; -} diff --git a/static/js/post-form.js b/static/js/post-form.js index b3dbb57..b58324c 100644 --- a/static/js/post-form.js +++ b/static/js/post-form.js @@ -30,10 +30,10 @@ $(function () { if (saved_top) { form.css("top", saved_top); } - + if (saved_left) { form.css("left", saved_left); - form.css("right", "auto") + form.css("right", "auto"); } handle.on("mousedown", start); @@ -139,11 +139,12 @@ $(function () { return; } - if (rect.right > document.documentElement.clientWidth) { + if (Math.floor(rect.right) > document.documentElement.clientWidth) { form.css("left", 0); + form.css("right", "auto"); } - if (rect.bottom > document.documentElement.clientHeight) { + if (Math.floor(rect.bottom) > document.documentElement.clientHeight) { form.css("top", 0); } diff --git a/templates/base.html b/templates/base.html index bf0b901..9209776 100755 --- a/templates/base.html +++ b/templates/base.html @@ -39,6 +39,9 @@ {% endfor %} + {% if let Some(report_count) = tcx.report_count %} + Hlášení: {{ report_count }} + {% endif %} {% if tcx.account.is_some() %} odhlásit se diff --git a/templates/board-catalog.html b/templates/board-catalog.html index fad2d10..e4b3657 100755 --- a/templates/board-catalog.html +++ b/templates/board-catalog.html @@ -16,6 +16,20 @@
+
+ + + + + + +
+ + + +
+
+
{% for thread in threads %} diff --git a/templates/ip-posts.html b/templates/ip-posts.html index 06580ae..9753ea4 100644 --- a/templates/ip-posts.html +++ b/templates/ip-posts.html @@ -7,7 +7,10 @@ {% block title %}Příspěvky od [{{ ip }}]{% endblock %} {% block content %} -

Příspěvky od [{{ ip }}]

+
+ +

Příspěvky od [{{ ip }}]

+

@@ -19,7 +22,7 @@ {% endfor %}

- {% call static_pagination::static_pagination("/ip-posts/{}"|format(ip), page) %} + {% call static_pagination::static_pagination("/ip-posts/{}"|format(ip), page, false) %}
{% call post_actions::post_actions() %} diff --git a/templates/macros/post-form.html b/templates/macros/post-form.html index fa85bf8..1d1e3ae 100644 --- a/templates/macros/post-form.html +++ b/templates/macros/post-form.html @@ -24,7 +24,7 @@ Email - + diff --git a/templates/macros/static-pagination.html b/templates/macros/static-pagination.html index 98e2276..4ef0118 100644 --- a/templates/macros/static-pagination.html +++ b/templates/macros/static-pagination.html @@ -1,13 +1,22 @@ -{% macro static_pagination(base, current) %} +{% macro static_pagination(base, current, chain) %} {% endmacro %} diff --git a/templates/news.html b/templates/news.html index 4b7e93e..36a6a6e 100644 --- a/templates/news.html +++ b/templates/news.html @@ -4,7 +4,10 @@ {% block content %}
-

Novinky

+
+ +

Novinky

+
{% for newspost in news %}

diff --git a/templates/overboard-catalog.html b/templates/overboard-catalog.html index 6139abe..0ac4dd6 100755 --- a/templates/overboard-catalog.html +++ b/templates/overboard-catalog.html @@ -15,6 +15,19 @@


+
+ + + + + +
+ + + +
+
+
{% for thread in threads %} diff --git a/templates/search.html b/templates/search.html new file mode 100644 index 0000000..b958392 --- /dev/null +++ b/templates/search.html @@ -0,0 +1,47 @@ +{% import "./macros/post-actions.html" as post_actions %} +{% import "./macros/post.html" as post %} +{% import "./macros/static-pagination.html" as static_pagination %} + +{% extends "base.html" %} + +{% block title %} +Vyhledávání ({% if let Some(board) = board_opt %}/{{ board.id }}/{% else %}nadnástěnka{% endif %}) +{% endblock %} + +{% block content %} +
+ +

+ Výsledky pro "{{ query }}" ( + {% if let Some(board) = board_opt %} + /{{ board.id }}/ + {% else %} + nadnástěnka + {% endif %} + ) +

+
+
+ +
+ {% for post in posts %} + {% if let Some(board) = board_opt %} + {% call post::post(board, post, true) %} + {% else %} + Příspěvek z /{{ post.board }}/ +
+ {% call post::post(boards[post.board.as_str()], post, true) %} + {% endif %} +
+ {% endfor %} +
+
+ {% if let Some(board) = board_opt %} + {% call static_pagination::static_pagination("/search?board={}&query={}"|format(board.id, query|urlencode_strict), page, true) %} + {% else %} + {% call static_pagination::static_pagination("/search?query={}"|format(query|urlencode_strict), page, true) %} + {% endif %} +
+ {% call post_actions::post_actions() %} + +{% endblock %} diff --git a/templates/staff/reports.html b/templates/staff/reports.html index 125cc88..bb643e9 100755 --- a/templates/staff/reports.html +++ b/templates/staff/reports.html @@ -33,7 +33,7 @@
{% endfor %} - {% call static_pagination::static_pagination("/staff/reports", page) %} + {% call static_pagination::static_pagination("/staff/reports", page, false) %}
{% call post_actions::post_actions() %}