From 4627eda849f09bb99948aa57cb8afbe67569a3fd Mon Sep 17 00:00:00 2001 From: hole-thu Date: Fri, 1 Apr 2022 01:19:45 +0800 Subject: [PATCH] feat: support upload files & adjust output --- .gitignore | 3 +++ Rocket.toml | 2 ++ src/api/comment.rs | 14 +++++--------- src/api/mod.rs | 28 ++++++++++++++++++---------- src/api/post.rs | 20 ++++++++------------ src/api/upload.rs | 37 +++++++++++++++++++++++++++++++++++++ src/main.rs | 1 + 7 files changed, 74 insertions(+), 31 deletions(-) create mode 100644 Rocket.toml create mode 100644 src/api/upload.rs diff --git a/.gitignore b/.gitignore index 10fcc03..0563df4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # --> sqlite3 *.db +# user files +/user_files + # ---> Rust # Generated by Cargo # will have compiled files and executables diff --git a/Rocket.toml b/Rocket.toml new file mode 100644 index 0000000..1759e3e --- /dev/null +++ b/Rocket.toml @@ -0,0 +1,2 @@ +[default] +limits = { file = "200MB", files = "200MB" } diff --git a/src/api/comment.rs b/src/api/comment.rs index cbdabd2..cd1cc9c 100644 --- a/src/api/comment.rs +++ b/src/api/comment.rs @@ -5,7 +5,6 @@ use crate::models::*; use crate::rds_conn::RdsConn; use crate::rds_models::*; use crate::schema; -use chrono::{offset::Utc, DateTime}; use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl}; use rocket::form::Form; use rocket::futures::future; @@ -30,7 +29,7 @@ pub struct CommentOutput { can_del: bool, name_id: i32, is_tmp: bool, - create_time: DateTime, + create_time: i64, is_blocked: bool, blocked_count: Option, // for old version frontend @@ -61,19 +60,16 @@ pub async fn c2output<'r>( BlockedUsers::check_blocked(rconn, user.id, &user.namehash, &c.author_hash) .await .unwrap_or_default(); - let can_view = !is_blocked && user.id.is_some() || user.namehash.eq(&c.author_hash); + let can_view = user.is_admin + || (!is_blocked && user.id.is_some() || user.namehash.eq(&c.author_hash)); Some(CommentOutput { cid: c.id, - text: format!( - "{}{}", - if c.is_tmp { "[tmp]\n" } else { "" }, - if can_view { &c.content } else { "" } - ), + text: (if can_view { &c.content } else { "" }).to_string(), author_title: c.author_title.to_string(), can_del: c.check_permission(user, "wd").is_ok(), name_id: name_id, is_tmp: c.is_tmp, - create_time: c.create_time, + create_time: c.create_time.timestamp(), is_blocked: is_blocked, blocked_count: if user.is_admin { BlockCounter::get_count(rconn, &c.author_hash).await.ok() diff --git a/src/api/mod.rs b/src/api/mod.rs index 5aa704a..ad656ab 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -25,6 +25,13 @@ macro_rules! code0 { ); } +macro_rules! e2s { + ($e:expr) => (json!({ + "code": -1, + "msg": $e.to_string() + })); +} + #[catch(401)] pub fn catch_401_error() -> &'static str { "未登录或token过期" @@ -100,21 +107,15 @@ pub enum APIError { DbError(diesel::result::Error), RdsError(redis::RedisError), PcError(PolicyError), + IoError(std::io::Error), } impl<'r> Responder<'r, 'static> for APIError { fn respond_to(self, req: &'r Request<'_>) -> response::Result<'static> { match self { - APIError::DbError(e) => json!({ - "code": -1, - "msg": e.to_string() - }) - .respond_to(req), - APIError::RdsError(e) => json!({ - "code": -1, - "msg": e.to_string() - }) - .respond_to(req), + APIError::DbError(e) => e2s!(e).respond_to(req), + APIError::RdsError(e) => e2s!(e).respond_to(req), + APIError::IoError(e) => e2s!(e).respond_to(req), APIError::PcError(e) => json!({ "code": -1, "msg": match e { @@ -142,6 +143,12 @@ impl From for APIError { } } +impl From for APIError { + fn from(err: std::io::Error) -> APIError { + APIError::IoError(err) + } +} + impl From for APIError { fn from(err: PolicyError) -> APIError { APIError::PcError(err) @@ -237,4 +244,5 @@ pub mod operation; pub mod post; pub mod search; pub mod systemlog; +pub mod upload; pub mod vote; diff --git a/src/api/post.rs b/src/api/post.rs index a64a963..b7015bd 100644 --- a/src/api/post.rs +++ b/src/api/post.rs @@ -1,13 +1,12 @@ use crate::api::comment::{c2output, CommentOutput}; use crate::api::vote::get_poll_dict; -use crate::api::{CurrentUser, JsonAPI, UGC, PolicyError::*}; +use crate::api::{CurrentUser, JsonAPI, PolicyError::*, UGC}; use crate::db_conn::Db; use crate::libs::diesel_logger::LoggingConnection; use crate::models::*; use crate::rds_conn::RdsConn; use crate::rds_models::*; use crate::schema; -use chrono::{offset::Utc, DateTime}; use diesel::{ExpressionMethods, QueryDsl, RunQueryDsl}; use rocket::form::Form; use rocket::futures::future; @@ -38,8 +37,8 @@ pub struct PostOutput { is_tmp: bool, n_attentions: i32, n_comments: i32, - create_time: DateTime, - last_comment_time: DateTime, + create_time: i64, + last_comment_time: i64, allow_search: bool, is_reported: Option, comments: Option>, @@ -67,19 +66,16 @@ async fn p2output(p: &Post, user: &CurrentUser, db: &Db, rconn: &RdsConn) -> Pos let is_blocked = BlockedUsers::check_blocked(rconn, user.id, &user.namehash, &p.author_hash) .await .unwrap_or_default(); - let can_view = !is_blocked && user.id.is_some() || user.namehash.eq(&p.author_hash); + let can_view = + user.is_admin || (!is_blocked && user.id.is_some() || user.namehash.eq(&p.author_hash)); PostOutput { pid: p.id, - text: format!( - "{}{}", - if p.is_tmp { "[tmp]\n" } else { "" }, - if can_view { &p.content } else { "" } - ), + text: (if can_view { &p.content } else { "" }).to_string(), cw: (!p.cw.is_empty()).then(|| p.cw.to_string()), n_attentions: p.n_attentions, n_comments: p.n_comments, - create_time: p.create_time, - last_comment_time: p.last_comment_time, + create_time: p.create_time.timestamp(), + last_comment_time: p.last_comment_time.timestamp(), allow_search: p.allow_search, author_title: (!p.author_title.is_empty()).then(|| p.author_title.to_string()), is_tmp: p.is_tmp, diff --git a/src/api/upload.rs b/src/api/upload.rs new file mode 100644 index 0000000..8b95a0d --- /dev/null +++ b/src/api/upload.rs @@ -0,0 +1,37 @@ +use crate::api::{CurrentUser, JsonAPI}; +use rocket::form::Form; +use rocket::fs::TempFile; +use rocket::serde::json::json; +use std::fs; +use std::path::Path; +use std::process::Command; + +#[derive(FromForm)] +pub struct Upload<'f> { + file: TempFile<'f>, +} + +#[post("/upload", data = "
")] +pub async fn ipfs_upload(user: CurrentUser, mut form: Form>) -> JsonAPI { + let file_dir = Path::new("user_files").join(user.namehash); + fs::create_dir_all(&file_dir)?; + let file = &mut form.file; + // dbg!(&file); + let filename = file.name().unwrap_or("file").to_string() + + "." + + &file.content_type().map_or("", |ct| ct.sub().as_str()); + file.persist_to(file_dir.with_file_name(&filename)).await?; + let output = Command::new("ipfs") + .args(["add", "-q", "-r", "-cid-version=1", "user_files"]) + .output()?; + // dbg!(&output); + let hash = std::str::from_utf8(&output.stdout) + .unwrap() + .split_terminator("\n") + .last() + .unwrap(); + code0!(json!({ + "hash": hash, + "filename": filename, + })) +} diff --git a/src/main.rs b/src/main.rs index 1b45fb1..ae76a9c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -71,6 +71,7 @@ async fn main() -> Result<(), rocket::Error> { api::operation::set_title, api::operation::block, api::vote::vote, + api::upload::ipfs_upload, ], ) .mount(