Compare commits
No commits in common. "c60e4e08d79f4dc2c3f08897475e0ad1dba03b16" and "1cc4bc5aebb9a1096e6fc87a25e81a22f8d266f8" have entirely different histories.
c60e4e08d7
...
1cc4bc5aeb
12
Makefile
12
Makefile
@ -3,13 +3,13 @@
|
||||
build:
|
||||
@$(MAKE) build-editor
|
||||
@$(MAKE) highlight-css
|
||||
@tailwindcss -i input.css -o public/style.css --minify
|
||||
@npx tailwindcss -i input.css -o public/style.css --minify
|
||||
@dx build --release --debug-symbols=false
|
||||
|
||||
build-linux:
|
||||
@$(MAKE) build-editor
|
||||
@$(MAKE) highlight-css
|
||||
@tailwindcss -i input.css -o public/style.css --minify
|
||||
@npx tailwindcss -i input.css -o public/style.css --minify
|
||||
@dx build --release --debug-symbols=false --target x86_64-unknown-linux-musl
|
||||
|
||||
highlight-css:
|
||||
@ -17,21 +17,21 @@ highlight-css:
|
||||
|
||||
build-editor:
|
||||
@echo "Building Tiptap editor..."
|
||||
@cd libs/tiptap-editor && npm ci --include=dev && npm run build
|
||||
@cd libs/tiptap-editor && npm ci && npx vite build
|
||||
@echo "Tiptap editor built."
|
||||
|
||||
dev:
|
||||
@echo "Starting tailwindcss watch and dx serve..."
|
||||
@tailwindcss -i input.css -o public/style.css --watch & \
|
||||
@npx tailwindcss -i input.css -o public/style.css --watch & \
|
||||
TAILWIND_PID=$$!; \
|
||||
trap 'kill $$TAILWIND_PID 2>/dev/null; exit' INT TERM EXIT; \
|
||||
dx serve --addr 0.0.0.0
|
||||
|
||||
css:
|
||||
@tailwindcss -i input.css -o public/style.css
|
||||
@npx tailwindcss -i input.css -o public/style.css
|
||||
|
||||
css-watch:
|
||||
@tailwindcss -i input.css -o public/style.css --watch
|
||||
@npx tailwindcss -i input.css -o public/style.css --watch
|
||||
|
||||
test:
|
||||
@cargo test
|
||||
|
||||
@ -15,13 +15,9 @@ use http::header::{HeaderValue, SET_COOKIE};
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::auth::session::get_session_from_ctx;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::auth::{password, session};
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::models::user::{User, UserRole};
|
||||
use crate::models::user::PublicUser;
|
||||
use crate::models::user::{PublicUser, User, UserRole};
|
||||
|
||||
#[allow(dead_code)]
|
||||
fn validate_username(username: &str) -> Result<(), String> {
|
||||
|
||||
@ -68,7 +68,6 @@ pub fn row_to_admin_comment(row: &tokio_postgres::Row) -> AdminComment {
|
||||
}
|
||||
|
||||
/// 将 UTC 时间格式化为相对时间(刚刚 / N 分钟前 / N 小时前 / N 天前 / 日期)。
|
||||
#[cfg(feature = "server")]
|
||||
pub fn format_relative_time(dt: chrono::DateTime<chrono::Utc>) -> String {
|
||||
let now = chrono::Utc::now();
|
||||
let diff = now.signed_duration_since(dt);
|
||||
@ -140,7 +139,6 @@ pub fn validate_comment_content(content: &str) -> Result<(), String> {
|
||||
}
|
||||
|
||||
/// 计算评论内容哈希,用于检测短时间内的重复提交。
|
||||
#[cfg(feature = "server")]
|
||||
pub fn compute_content_hash(
|
||||
post_id: i32,
|
||||
parent_id: Option<i64>,
|
||||
|
||||
@ -52,7 +52,6 @@ pub struct CommentTreeResponse {
|
||||
|
||||
/// 评论计数响应。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct CommentCountResponse {
|
||||
/// 评论数量。
|
||||
pub count: i64,
|
||||
@ -60,7 +59,6 @@ pub struct CommentCountResponse {
|
||||
|
||||
/// 待审核评论列表响应。
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
#[allow(dead_code)]
|
||||
pub struct PendingCommentsResponse {
|
||||
/// 待审核评论列表。
|
||||
pub comments: Vec<AdminComment>,
|
||||
|
||||
@ -14,9 +14,7 @@ use super::helpers::{clean_tags, get_current_admin_user, sync_tags};
|
||||
use super::types::CreatePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::models::post::PostStatus;
|
||||
|
||||
/// 创建一篇新文章。
|
||||
|
||||
@ -12,7 +12,6 @@ use super::helpers::get_current_admin_user;
|
||||
use super::types::CreatePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
|
||||
/// 删除指定文章。
|
||||
|
||||
@ -12,14 +12,12 @@ use super::helpers::{get_current_admin_user, row_to_post_list};
|
||||
use super::types::PostListResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
|
||||
/// 单页允许的最大文章数。
|
||||
///
|
||||
/// 公开的 `list_published_posts` 接口无需认证,若不对 `per_page` 设上限,
|
||||
/// 攻击者可传入巨大值迫使数据库扫描并实例化超大 Vec,造成内存放大与拒绝服务。
|
||||
#[cfg(feature = "server")]
|
||||
const MAX_PER_PAGE: i32 = 50;
|
||||
|
||||
/// 允许的最大页码。
|
||||
@ -27,13 +25,11 @@ const MAX_PER_PAGE: i32 = 50;
|
||||
/// `page` 无上限时,攻击者可用海量不同 `page` 值撑大缓存键空间(缓存污染),
|
||||
/// 并触发无意义的超大 `OFFSET` 扫描。10_000 对任何实际博客都足够宽裕
|
||||
/// (配合 `MAX_PER_PAGE` 最多覆盖 50 万篇文章),同时把缓存键空间限制在有限范围。
|
||||
#[cfg(feature = "server")]
|
||||
const MAX_PAGE: i32 = 10_000;
|
||||
|
||||
/// 将分页参数钳制到安全范围:页码 1–`MAX_PAGE`,每页 1–`MAX_PER_PAGE`。
|
||||
///
|
||||
/// 注意:返回值必须同时用于缓存键与 SQL 查询,避免同一逻辑页落入不同缓存条目。
|
||||
#[cfg(feature = "server")]
|
||||
fn clamp_pagination(page: i32, per_page: i32) -> (i32, i32) {
|
||||
(page.clamp(1, MAX_PAGE), per_page.clamp(1, MAX_PER_PAGE))
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ use super::helpers::{get_current_admin_user, row_to_post_full, row_to_post_list}
|
||||
use super::types::SinglePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
|
||||
/// 根据文章 id 获取详情。
|
||||
|
||||
@ -12,14 +12,11 @@ use super::helpers::get_current_admin_user;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
use crate::api::posts::RebuildResult;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
|
||||
/// 单次重建批处理数量上限。
|
||||
#[cfg(feature = "server")]
|
||||
const REBUILD_BATCH_LIMIT: i64 = 500;
|
||||
/// 返回给前端展示的最大错误条数。
|
||||
#[cfg(feature = "server")]
|
||||
const MAX_DISPLAY_ERRORS: usize = 5;
|
||||
|
||||
/// 批量重建文章 content_html 与 toc_html。
|
||||
|
||||
@ -12,7 +12,6 @@ use super::helpers::row_to_post_list;
|
||||
use super::types::PostListResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
|
||||
/// 搜索已发布文章。
|
||||
|
||||
@ -11,9 +11,7 @@ use super::helpers::get_current_admin_user;
|
||||
use super::types::PostStatsResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::models::post::PostStats;
|
||||
|
||||
/// 获取文章统计信息。
|
||||
|
||||
@ -9,9 +9,7 @@ use dioxus::prelude::*;
|
||||
use super::types::TagListResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::models::post::Tag;
|
||||
|
||||
/// 获取全部标签列表。
|
||||
|
||||
@ -14,9 +14,7 @@ use super::helpers::{clean_tags, get_current_admin_user, sync_tags};
|
||||
use super::types::CreatePostResponse;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::api::error::AppError;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::db::pool::get_conn;
|
||||
#[cfg(feature = "server")]
|
||||
use crate::models::post::PostStatus;
|
||||
|
||||
/// 更新指定文章。
|
||||
|
||||
@ -6,7 +6,6 @@
|
||||
|
||||
#![allow(clippy::unused_unit, deprecated)]
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
use dioxus::prelude::*;
|
||||
|
||||
#[cfg(feature = "server")]
|
||||
|
||||
@ -22,7 +22,6 @@ pub enum CommentStatus {
|
||||
|
||||
impl CommentStatus {
|
||||
/// 将数据库或 API 中的状态字符串解析为 CommentStatus,未知值默认回退到 Pending。
|
||||
#[cfg(feature = "server")]
|
||||
pub fn from_str(s: &str) -> Self {
|
||||
match s {
|
||||
"approved" => Self::Approved,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user