+ Adding proper file upload support

! Requirements for variable message fetching not yet met
! /message/get?id=<id> needs a way of passing back file contents
! The file upload is sketchy at best and not necessarily guaranteed to sync the
database with the file system
This commit is contained in:
shockrah
2021-03-23 17:16:05 -07:00
parent 92dc4d888f
commit d02084a22c
3 changed files with 111 additions and 43 deletions

View File

@@ -10,4 +10,4 @@ edition = "2018"
mysql_async = "0.23.1"
serde = { version = "1.0.117", features = [ "derive" ] }
tokio = { version = "1", features = ["fs", "io-util"] }

View File

@@ -1,8 +1,14 @@
use std::time::{SystemTime, UNIX_EPOCH};
use mysql_async::{params, Pool};
use tokio::fs;
use tokio::io::AsyncWriteExt;
use mysql_async::params;
use mysql_async::{Pool, Conn};
use mysql_async::prelude::Queryable;
use mysql_async::error::Error as SqlError;
use mysql_async::QueryResult;
use mysql_async::BinaryProtocol;
use crate::{Response, sql_err};
@@ -13,27 +19,7 @@ const MAX_MESSAGES: u64 = 1000;
impl Message {
pub async fn send(p: &Pool, content: &str, content_type: &str, cid: UBigInt, uid: UBigInt) -> Result<Response<Self>, SqlError> {
//! @returns on_sucess -> empty
//! @returns on_failure Err(SqlErr)
let conn = p.get_conn().await?;
let q = "INSERT INTO messages
(time, content, content_type, author_id, channel_id)
VALUES (:time, :content, :ctype, :author, :channel)";
let now: i64 = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("System time `NOW` failed")
.as_millis() as i64;
let res = conn.prep_exec(q, params!{
"time" => now,
"content" => content,
"ctype" => content_type,
"author" => uid,
"channel" => cid
}).await;
fn send_verify_error(res: Result<QueryResult<Conn, BinaryProtocol>, SqlError> ) -> Result<Response<Self>, SqlError> {
if let Err(e) = res {
return match e {
SqlError::Server(err) => {
@@ -52,6 +38,76 @@ impl Message {
else {
return Ok(Response::Empty);
}
}
pub async fn send(p: &Pool, content: &str, content_type: &str, cid: UBigInt, uid: UBigInt) -> Result<Response<Self>, SqlError> {
//! @returns on_sucess -> empty
//! @returns on_failure Err(SqlErr)
let conn = p.get_conn().await?;
let q = "INSERT INTO messages
(time, content, content_type, author_id, channel_id)
VALUES (:time, :content, :ctype, :author, :channel)";
let now: i64 = SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("System time `NOW` failed")
.as_millis() as i64;
match content_type {
"text/plain" => {
if content.len() > 4_000 {
Ok(Response::RestrictedInput("Large text not allowed".into()))
} else {
let res = conn.prep_exec(q, params!{
"time" => now,
"content" => content,
"ctype" => content_type,
"author" => uid,
"channel" => cid
}).await;
Self::send_verify_error(res)
}
},
_ => {
if content.len() > 10_000_000 {
Ok(Response::RestrictedInput("Large data not allowed".into()))
} else {
let extension = match content_type {
"image/png" => "png",
"image/jpeg" | "image/jpg" => "jpg",
"application/webm" => "webm",
"application/mp4" => "mp4",
"application/mp3" => "mp3",
_ => panic!("Bad file type sent to db layer {}", content_type)
};
let content_ref = format!("{cid}-{time}.{ext}", cid=cid, time=now, ext=extension);
let res = conn.prep_exec(q, params!{
"time" => now,
"content" => &content_ref, // store a ref to a file instead of the actual payload
"ctype" => content_type,
"author" => uid,
"channel" => cid
}).await;
if let Ok(ret) = Self::send_verify_error(res) {
// now save the data to disk
match fs::File::create(content_ref).await {
Ok(mut file) => {
file.write_all(content.as_bytes()).await.expect("Failed to write, but the ref is saved");
Ok(ret)
},
Err(_) => {
Ok(Response::Other("Saved ref but couldn't save file data".into()))
},
}
} else {
Ok(Response::Success)
}
}
}
}
}