Browse Source

feat: record and show systemlog, admin delete log

master
hole-thu 3 years ago
parent
commit
ce3379c5ae
  1. 22
      src/api/operation.rs
  2. 1
      src/api/post.rs
  3. 18
      src/api/systemlog.rs
  4. 4
      src/models.rs
  5. 48
      src/rds_models.rs

22
src/api/operation.rs

@ -2,6 +2,8 @@ use crate::api::{APIError, CurrentUser, PolicyError::*, API, UGC};
use crate::db_conn::Db;
use crate::models::*;
use crate::rds_conn::RdsConn;
use crate::rds_models::*;
use chrono::offset::Local;
use rocket::form::Form;
use rocket::serde::json::{json, Value};
@ -21,9 +23,11 @@ pub async fn delete(
rconn: RdsConn,
) -> API<Value> {
let mut p: Post;
let mut c: Comment;
let author_hash: &str;
match di.id_type.as_str() {
"cid" => {
let mut c = Comment::get(&db, di.id).await?;
c = Comment::get(&db, di.id).await?;
c.soft_delete(&user, &db).await?;
p = Post::get(&db, &rconn, c.post_id).await?;
p.change_n_comments(&db, -1).await?;
@ -31,16 +35,32 @@ pub async fn delete(
p.refresh_cache(&rconn, false).await;
p.clear_comments_cache(&rconn).await;
author_hash = &c.author_hash;
}
"pid" => {
p = Post::get(&db, &rconn, di.id).await?;
p.soft_delete(&user, &db).await?;
// 如果是删除,需要也从0号缓存队列中去掉
p.refresh_cache(&rconn, true).await;
author_hash = &p.author_hash;
}
_ => 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!({
"code": 0
}))

1
src/api/post.rs

@ -100,7 +100,6 @@ pub async fn ps2outputs(
#[get("/getone?<pid>")]
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?;
p.check_permission(&user, "ro")?;
Ok(json!({

18
src/api/systemlog.rs

@ -1,16 +1,26 @@
use crate::api::{CurrentUser, API};
use crate::db_conn::Db;
use crate::api::{CurrentUser, JsonAPI};
use crate::random_hasher::RandomHasher;
use crate::rds_conn::RdsConn;
use crate::rds_models::Systemlog;
use rocket::serde::json::{json, Value};
use rocket::State;
#[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!({
"tmp_token": rh.get_tmp_token(),
"salt": look!(rh.salt),
"start_time": rh.start_time.timestamp(),
"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>>(),
}))
}

4
src/models.rs

@ -179,11 +179,9 @@ impl Post {
}
pub async fn get(db: &Db, rconn: &RdsConn, id: i32) -> QueryResult<Self> {
// 注意即使is_deleted也应该缓存和返回
let mut cacher = PostCache::init(&rconn);
if let Some(p) = cacher.get(&id).await {
if p.is_deleted {
return Err(diesel::result::Error::NotFound);
}
Ok(p)
} else {
let p = Self::_get(db, id).await?;

48
src/rds_models.rs

@ -1,5 +1,11 @@
use crate::rds_conn::RdsConn;
use chrono::{offset::Local, DateTime};
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 {
key: String,
@ -30,3 +36,45 @@ impl Attention {
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())
}
}

Loading…
Cancel
Save