init
This commit is contained in:
@@ -4,10 +4,7 @@ use crate::services::ServicesContext;
|
||||
use crate::services::media::PendingMediaFile;
|
||||
use sea_orm::{ActiveModelTrait, Set, TransactionTrait};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::{
|
||||
path::Path,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::{path::Path, sync::Arc};
|
||||
use tokio::fs;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -91,10 +88,12 @@ impl EmojiService {
|
||||
let mut pending = PendingMediaFile::begin(root, "emoji", id, extension)
|
||||
.await
|
||||
.map_err(|e| HTTPError::InternalServerError(e.to_string()))?;
|
||||
pending.write(data)
|
||||
pending
|
||||
.write(data)
|
||||
.await
|
||||
.map_err(|e| HTTPError::InternalServerError(e.to_string()))?;
|
||||
let relative = pending.finish()
|
||||
let relative = pending
|
||||
.finish()
|
||||
.await
|
||||
.map_err(|e| HTTPError::InternalServerError(e.to_string()))?;
|
||||
Ok((relative, Self::hash(data)))
|
||||
|
||||
+24
-5
@@ -12,7 +12,12 @@ pub struct PendingMediaFile {
|
||||
}
|
||||
|
||||
impl PendingMediaFile {
|
||||
pub async fn begin(root: &Path, directory: &str, id: Uuid, extension: Option<&str>) -> std::io::Result<Self> {
|
||||
pub async fn begin(
|
||||
root: &Path,
|
||||
directory: &str,
|
||||
id: Uuid,
|
||||
extension: Option<&str>,
|
||||
) -> std::io::Result<Self> {
|
||||
let relative_directory = PathBuf::from(directory);
|
||||
let directory_path = root.join(&relative_directory);
|
||||
fs::create_dir_all(&directory_path).await?;
|
||||
@@ -31,7 +36,10 @@ impl PendingMediaFile {
|
||||
file,
|
||||
temporary_path,
|
||||
final_path,
|
||||
relative_final_path: relative_directory.join(final_name).to_string_lossy().into_owned(),
|
||||
relative_final_path: relative_directory
|
||||
.join(final_name)
|
||||
.to_string_lossy()
|
||||
.into_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -59,8 +67,16 @@ pub fn extension_from_filename(filename: &str) -> Option<String> {
|
||||
.iter()
|
||||
.find(|candidate| lower.ends_with(&format!(".{candidate}")))
|
||||
.map(|candidate| (*candidate).to_string())
|
||||
.or_else(|| Path::new(name).extension().and_then(|value| value.to_str()).map(str::to_ascii_lowercase))?;
|
||||
if extension.chars().all(|character| character.is_ascii_alphanumeric() || character == '.') {
|
||||
.or_else(|| {
|
||||
Path::new(name)
|
||||
.extension()
|
||||
.and_then(|value| value.to_str())
|
||||
.map(str::to_ascii_lowercase)
|
||||
})?;
|
||||
if extension
|
||||
.chars()
|
||||
.all(|character| character.is_ascii_alphanumeric() || character == '.')
|
||||
{
|
||||
Some(extension)
|
||||
} else {
|
||||
None
|
||||
@@ -117,7 +133,10 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn preserves_compound_extensions() {
|
||||
assert_eq!(extension_from_filename("archive.tar.gz").as_deref(), Some("tar.gz"));
|
||||
assert_eq!(
|
||||
extension_from_filename("archive.tar.gz").as_deref(),
|
||||
Some("tar.gz")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+34
-11
@@ -4,7 +4,9 @@ use crate::domain::events::message::{
|
||||
use crate::models::{attachment, channel, message};
|
||||
use crate::services::ServicesContext;
|
||||
use event_bus::Scope;
|
||||
use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QuerySelect, Set, TransactionTrait};
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QuerySelect, Set, TransactionTrait,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -87,14 +89,23 @@ impl MessageService {
|
||||
.await?
|
||||
};
|
||||
if files.len() != file_ids.len()
|
||||
|| files.iter().any(|file| file.channel_id != channel_id || file.user_id != author_id || file.message_id.is_some())
|
||||
|| files.iter().any(|file| {
|
||||
file.channel_id != channel_id
|
||||
|| file.user_id != author_id
|
||||
|| file.message_id.is_some()
|
||||
})
|
||||
{
|
||||
return Err(anyhow::anyhow!("Invalid or already used attachment"));
|
||||
}
|
||||
let msg = message::ActiveModel {
|
||||
channel_id: Set(channel_id), user_id: Set(author_id), content: Set(content),
|
||||
reply_to_id: Set(reply_to_id), ..Default::default()
|
||||
}.insert(&txn).await?;
|
||||
channel_id: Set(channel_id),
|
||||
user_id: Set(author_id),
|
||||
content: Set(content),
|
||||
reply_to_id: Set(reply_to_id),
|
||||
..Default::default()
|
||||
}
|
||||
.insert(&txn)
|
||||
.await?;
|
||||
for file in files {
|
||||
let mut active: attachment::ActiveModel = file.into();
|
||||
active.message_id = Set(Some(msg.id));
|
||||
@@ -102,13 +113,25 @@ impl MessageService {
|
||||
}
|
||||
txn.commit().await?;
|
||||
let server_id = channel::Entity::find_by_id(msg.channel_id)
|
||||
.select_only().column(channel::Column::ServerId)
|
||||
.into_tuple::<Option<Uuid>>().one(db).await?.flatten();
|
||||
.select_only()
|
||||
.column(channel::Column::ServerId)
|
||||
.into_tuple::<Option<Uuid>>()
|
||||
.one(db)
|
||||
.await?
|
||||
.flatten();
|
||||
let mut scopes = vec![Scope::uuid("channel", msg.channel_id)];
|
||||
if let Some(server_id) = server_id { scopes.push(Scope::uuid("server", server_id)); }
|
||||
event_bus.emit_scoped("message_created", scopes, MessageCreatedEvent {
|
||||
server_id, channel_id: msg.channel_id, message: msg.clone(),
|
||||
});
|
||||
if let Some(server_id) = server_id {
|
||||
scopes.push(Scope::uuid("server", server_id));
|
||||
}
|
||||
event_bus.emit_scoped(
|
||||
"message_created",
|
||||
scopes,
|
||||
MessageCreatedEvent {
|
||||
server_id,
|
||||
channel_id: msg.channel_id,
|
||||
message: msg.clone(),
|
||||
},
|
||||
);
|
||||
Ok(msg)
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -16,9 +16,9 @@ use std::sync::{Arc, OnceLock};
|
||||
pub mod category;
|
||||
pub mod channel;
|
||||
pub mod emoji;
|
||||
pub mod media;
|
||||
pub mod message;
|
||||
pub mod message_reaction;
|
||||
pub mod media;
|
||||
mod permission;
|
||||
pub mod permission_sync;
|
||||
pub mod realtime_registry;
|
||||
|
||||
Reference in New Issue
Block a user