feat: record and show systemlog, admin delete log
This commit is contained in:
@@ -2,6 +2,8 @@ use crate::api::{APIError, CurrentUser, PolicyError::*, API, UGC};
|
|||||||
use crate::db_conn::Db;
|
use crate::db_conn::Db;
|
||||||
use crate::models::*;
|
use crate::models::*;
|
||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
|
use crate::rds_models::*;
|
||||||
|
use chrono::offset::Local;
|
||||||
use rocket::form::Form;
|
use rocket::form::Form;
|
||||||
use rocket::serde::json::{json, Value};
|
use rocket::serde::json::{json, Value};
|
||||||
|
|
||||||
@@ -21,9 +23,11 @@ pub async fn delete(
|
|||||||
rconn: RdsConn,
|
rconn: RdsConn,
|
||||||
) -> API<Value> {
|
) -> API<Value> {
|
||||||
let mut p: Post;
|
let mut p: Post;
|
||||||
|
let mut c: Comment;
|
||||||
|
let author_hash: &str;
|
||||||
match di.id_type.as_str() {
|
match di.id_type.as_str() {
|
||||||
"cid" => {
|
"cid" => {
|
||||||
let mut c = Comment::get(&db, di.id).await?;
|
c = Comment::get(&db, di.id).await?;
|
||||||
c.soft_delete(&user, &db).await?;
|
c.soft_delete(&user, &db).await?;
|
||||||
p = Post::get(&db, &rconn, c.post_id).await?;
|
p = Post::get(&db, &rconn, c.post_id).await?;
|
||||||
p.change_n_comments(&db, -1).await?;
|
p.change_n_comments(&db, -1).await?;
|
||||||
@@ -31,16 +35,32 @@ pub async fn delete(
|
|||||||
|
|
||||||
p.refresh_cache(&rconn, false).await;
|
p.refresh_cache(&rconn, false).await;
|
||||||
p.clear_comments_cache(&rconn).await;
|
p.clear_comments_cache(&rconn).await;
|
||||||
|
|
||||||
|
author_hash = &c.author_hash;
|
||||||
}
|
}
|
||||||
"pid" => {
|
"pid" => {
|
||||||
p = Post::get(&db, &rconn, di.id).await?;
|
p = Post::get(&db, &rconn, di.id).await?;
|
||||||
p.soft_delete(&user, &db).await?;
|
p.soft_delete(&user, &db).await?;
|
||||||
// 如果是删除,需要也从0号缓存队列中去掉
|
// 如果是删除,需要也从0号缓存队列中去掉
|
||||||
p.refresh_cache(&rconn, true).await;
|
p.refresh_cache(&rconn, true).await;
|
||||||
|
|
||||||
|
author_hash = &p.author_hash;
|
||||||
}
|
}
|
||||||
_ => return Err(APIError::PcError(NotAllowed)),
|
_ => return Err(APIError::PcError(NotAllowed)),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.is_admin && author_hash != user.namehash {
|
||||||
|
Systemlog {
|
||||||
|
user_hash: user.namehash,
|
||||||
|
action_type: LogType::AdminDelete,
|
||||||
|
target: format!("#{}, {}={}", p.id, di.id_type, di.id),
|
||||||
|
detail: di.note.clone(),
|
||||||
|
time: Local::now(),
|
||||||
|
}
|
||||||
|
.create(&rconn)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"code": 0
|
"code": 0
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ pub async fn ps2outputs(
|
|||||||
|
|
||||||
#[get("/getone?<pid>")]
|
#[get("/getone?<pid>")]
|
||||||
pub async fn get_one(pid: i32, user: CurrentUser, db: Db, rconn: RdsConn) -> JsonAPI {
|
pub async fn get_one(pid: i32, user: CurrentUser, db: Db, rconn: RdsConn) -> JsonAPI {
|
||||||
// let p = Post::get(&db, pid).await?;
|
|
||||||
let p = Post::get(&db, &rconn, pid).await?;
|
let p = Post::get(&db, &rconn, pid).await?;
|
||||||
p.check_permission(&user, "ro")?;
|
p.check_permission(&user, "ro")?;
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
|
|||||||
@@ -1,16 +1,26 @@
|
|||||||
use crate::api::{CurrentUser, API};
|
use crate::api::{CurrentUser, JsonAPI};
|
||||||
use crate::db_conn::Db;
|
|
||||||
use crate::random_hasher::RandomHasher;
|
use crate::random_hasher::RandomHasher;
|
||||||
|
use crate::rds_conn::RdsConn;
|
||||||
|
use crate::rds_models::Systemlog;
|
||||||
use rocket::serde::json::{json, Value};
|
use rocket::serde::json::{json, Value};
|
||||||
use rocket::State;
|
use rocket::State;
|
||||||
|
|
||||||
#[get("/systemlog")]
|
#[get("/systemlog")]
|
||||||
pub async fn get_systemlog(user: CurrentUser, rh: &State<RandomHasher>, db: Db) -> API<Value> {
|
pub async fn get_systemlog(user: CurrentUser, rh: &State<RandomHasher>, rconn: RdsConn) -> JsonAPI {
|
||||||
|
let logs = Systemlog::get_list(&rconn, 50).await?;
|
||||||
|
|
||||||
Ok(json!({
|
Ok(json!({
|
||||||
"tmp_token": rh.get_tmp_token(),
|
"tmp_token": rh.get_tmp_token(),
|
||||||
"salt": look!(rh.salt),
|
"salt": look!(rh.salt),
|
||||||
"start_time": rh.start_time.timestamp(),
|
"start_time": rh.start_time.timestamp(),
|
||||||
"custom_title": user.custom_title,
|
"custom_title": user.custom_title,
|
||||||
"data": [],
|
"data": logs.into_iter().map(|log|
|
||||||
|
json!({
|
||||||
|
"type": log.action_type,
|
||||||
|
"user": look!(log.user_hash),
|
||||||
|
"timestamp": log.time.timestamp(),
|
||||||
|
"detail": format!("{}\n{}", &log.target, &log.detail)
|
||||||
|
})
|
||||||
|
).collect::<Vec<Value>>(),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -179,11 +179,9 @@ impl Post {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get(db: &Db, rconn: &RdsConn, id: i32) -> QueryResult<Self> {
|
pub async fn get(db: &Db, rconn: &RdsConn, id: i32) -> QueryResult<Self> {
|
||||||
|
// 注意即使is_deleted也应该缓存和返回
|
||||||
let mut cacher = PostCache::init(&rconn);
|
let mut cacher = PostCache::init(&rconn);
|
||||||
if let Some(p) = cacher.get(&id).await {
|
if let Some(p) = cacher.get(&id).await {
|
||||||
if p.is_deleted {
|
|
||||||
return Err(diesel::result::Error::NotFound);
|
|
||||||
}
|
|
||||||
Ok(p)
|
Ok(p)
|
||||||
} else {
|
} else {
|
||||||
let p = Self::_get(db, id).await?;
|
let p = Self::_get(db, id).await?;
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
use crate::rds_conn::RdsConn;
|
use crate::rds_conn::RdsConn;
|
||||||
|
use chrono::{offset::Local, DateTime};
|
||||||
use redis::{AsyncCommands, RedisResult};
|
use redis::{AsyncCommands, RedisResult};
|
||||||
|
use rocket::serde::json::serde_json;
|
||||||
|
use rocket::serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
const KEY_SYSTEMLOG: &str = "hole_v2:systemlog_list";
|
||||||
|
const SYSTEMLOG_MAX_LEN: isize = 1000;
|
||||||
|
|
||||||
pub struct Attention {
|
pub struct Attention {
|
||||||
key: String,
|
key: String,
|
||||||
@@ -30,3 +36,45 @@ impl Attention {
|
|||||||
self.rconn.smembers(&self.key).await
|
self.rconn.smembers(&self.key).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub enum LogType {
|
||||||
|
AdminDelete,
|
||||||
|
Report,
|
||||||
|
Ban
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(crate = "rocket::serde")]
|
||||||
|
pub struct Systemlog {
|
||||||
|
pub user_hash: String,
|
||||||
|
pub action_type: LogType,
|
||||||
|
pub target: String,
|
||||||
|
pub detail: String,
|
||||||
|
pub time: DateTime<Local>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Systemlog {
|
||||||
|
pub async fn create(&self, rconn: &RdsConn) -> RedisResult<()> {
|
||||||
|
let mut rconn = rconn.clone();
|
||||||
|
if rconn.llen::<&str, isize>(KEY_SYSTEMLOG).await? > SYSTEMLOG_MAX_LEN {
|
||||||
|
rconn.ltrim(KEY_SYSTEMLOG, 0, SYSTEMLOG_MAX_LEN - 1).await?;
|
||||||
|
}
|
||||||
|
rconn
|
||||||
|
.lpush(KEY_SYSTEMLOG, serde_json::to_string(&self).unwrap())
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn get_list(rconn: &RdsConn, limit: isize) -> RedisResult<Vec<Self>> {
|
||||||
|
let rds_result = rconn
|
||||||
|
.clone()
|
||||||
|
.lrange::<&str, Vec<String>>(KEY_SYSTEMLOG, 0, limit)
|
||||||
|
.await?;
|
||||||
|
Ok(rds_result
|
||||||
|
.iter()
|
||||||
|
.map(|s| serde_json::from_str(s).unwrap())
|
||||||
|
.collect())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user