Refactor create_view()
This commit is contained in:
parent
20e965a655
commit
748521b5ce
2 changed files with 72 additions and 102 deletions
|
@ -2,12 +2,10 @@ use chrono::{DateTime, Utc};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::errors::ValidationError;
|
|
||||||
use crate::mastodon_api::accounts::types::Account;
|
use crate::mastodon_api::accounts::types::Account;
|
||||||
use crate::mastodon_api::media::types::Attachment;
|
use crate::mastodon_api::media::types::Attachment;
|
||||||
use crate::models::posts::types::{Post, PostCreateData, Visibility};
|
use crate::models::posts::types::{Post, Visibility};
|
||||||
use crate::models::profiles::types::DbActorProfile;
|
use crate::models::profiles::types::DbActorProfile;
|
||||||
use crate::utils::markdown::markdown_lite_to_html;
|
|
||||||
|
|
||||||
/// https://docs.joinmastodon.org/entities/mention/
|
/// https://docs.joinmastodon.org/entities/mention/
|
||||||
#[derive(Serialize)]
|
#[derive(Serialize)]
|
||||||
|
@ -151,68 +149,7 @@ pub struct StatusData {
|
||||||
pub content_type: String,
|
pub content_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<StatusData> for PostCreateData {
|
|
||||||
|
|
||||||
type Error = ValidationError;
|
|
||||||
|
|
||||||
fn try_from(status_data: StatusData) -> Result<Self, Self::Error> {
|
|
||||||
let visibility = match status_data.visibility.as_deref() {
|
|
||||||
Some("public") => Visibility::Public,
|
|
||||||
Some("direct") => Visibility::Direct,
|
|
||||||
Some("private") => Visibility::Followers,
|
|
||||||
Some("subscribers") => Visibility::Subscribers,
|
|
||||||
Some(_) => return Err(ValidationError("invalid visibility parameter")),
|
|
||||||
None => Visibility::Public,
|
|
||||||
};
|
|
||||||
let content = match status_data.content_type.as_str() {
|
|
||||||
"text/html" => status_data.status,
|
|
||||||
"text/markdown" => {
|
|
||||||
markdown_lite_to_html(&status_data.status)
|
|
||||||
.map_err(|_| ValidationError("invalid markdown"))?
|
|
||||||
},
|
|
||||||
_ => return Err(ValidationError("unsupported content type")),
|
|
||||||
};
|
|
||||||
let post_data = Self {
|
|
||||||
content: content,
|
|
||||||
in_reply_to_id: status_data.in_reply_to_id,
|
|
||||||
repost_of_id: None,
|
|
||||||
visibility: visibility,
|
|
||||||
attachments: status_data.media_ids.unwrap_or(vec![]),
|
|
||||||
mentions: status_data.mentions.unwrap_or(vec![]),
|
|
||||||
tags: vec![],
|
|
||||||
links: vec![],
|
|
||||||
object_id: None,
|
|
||||||
created_at: Utc::now(),
|
|
||||||
};
|
|
||||||
Ok(post_data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct TransactionData {
|
pub struct TransactionData {
|
||||||
pub transaction_id: String,
|
pub transaction_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_status_data_into_post_data() {
|
|
||||||
let status_content = "<p>test</p>";
|
|
||||||
let status_data = StatusData {
|
|
||||||
status: status_content.to_string(),
|
|
||||||
media_ids: None,
|
|
||||||
in_reply_to_id: None,
|
|
||||||
visibility: Some("public".to_string()),
|
|
||||||
mentions: None,
|
|
||||||
content_type: "text/html".to_string(),
|
|
||||||
};
|
|
||||||
let post_data = PostCreateData::try_from(status_data).unwrap();
|
|
||||||
assert_eq!(post_data.content, status_content);
|
|
||||||
assert_eq!(post_data.visibility, Visibility::Public);
|
|
||||||
assert_eq!(post_data.attachments, vec![]);
|
|
||||||
assert_eq!(post_data.mentions, vec![]);
|
|
||||||
assert_eq!(post_data.links, vec![]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/// https://docs.joinmastodon.org/methods/statuses/
|
/// https://docs.joinmastodon.org/methods/statuses/
|
||||||
use actix_web::{delete, get, post, web, HttpResponse, Scope};
|
use actix_web::{delete, get, post, web, HttpResponse, Scope};
|
||||||
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
use actix_web_httpauth::extractors::bearer::BearerAuth;
|
||||||
|
use chrono::Utc;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::activitypub::builders::{
|
use crate::activitypub::builders::{
|
||||||
|
@ -39,7 +40,10 @@ use crate::models::reactions::queries::{
|
||||||
delete_reaction,
|
delete_reaction,
|
||||||
};
|
};
|
||||||
use crate::models::relationships::queries::get_subscribers;
|
use crate::models::relationships::queries::get_subscribers;
|
||||||
use crate::utils::currencies::Currency;
|
use crate::utils::{
|
||||||
|
currencies::Currency,
|
||||||
|
markdown::markdown_lite_to_html,
|
||||||
|
};
|
||||||
use super::helpers::{
|
use super::helpers::{
|
||||||
build_status,
|
build_status,
|
||||||
build_status_list,
|
build_status_list,
|
||||||
|
@ -56,58 +60,78 @@ async fn create_status(
|
||||||
let db_client = &mut **get_database_client(&db_pool).await?;
|
let db_client = &mut **get_database_client(&db_pool).await?;
|
||||||
let current_user = get_current_user(db_client, auth.token()).await?;
|
let current_user = get_current_user(db_client, auth.token()).await?;
|
||||||
let instance = config.instance();
|
let instance = config.instance();
|
||||||
let mut post_data = PostCreateData::try_from(status_data.into_inner())?;
|
let status_data = status_data.into_inner();
|
||||||
|
let visibility = match status_data.visibility.as_deref() {
|
||||||
|
Some("public") => Visibility::Public,
|
||||||
|
Some("direct") => Visibility::Direct,
|
||||||
|
Some("private") => Visibility::Followers,
|
||||||
|
Some("subscribers") => Visibility::Subscribers,
|
||||||
|
Some(_) => return Err(ValidationError("invalid visibility parameter").into()),
|
||||||
|
None => Visibility::Public,
|
||||||
|
};
|
||||||
|
let mut content = match status_data.content_type.as_str() {
|
||||||
|
"text/html" => status_data.status,
|
||||||
|
"text/markdown" => {
|
||||||
|
markdown_lite_to_html(&status_data.status)
|
||||||
|
.map_err(|_| ValidationError("invalid markdown"))?
|
||||||
|
},
|
||||||
|
_ => return Err(ValidationError("unsupported content type").into()),
|
||||||
|
};
|
||||||
|
let mut mentions = status_data.mentions.unwrap_or(vec![]);
|
||||||
// Mentions
|
// Mentions
|
||||||
let mention_map = find_mentioned_profiles(
|
let mention_map = find_mentioned_profiles(
|
||||||
db_client,
|
db_client,
|
||||||
&instance.hostname(),
|
&instance.hostname(),
|
||||||
&post_data.content,
|
&content,
|
||||||
).await?;
|
).await?;
|
||||||
post_data.content = replace_mentions(
|
content = replace_mentions(
|
||||||
&mention_map,
|
&mention_map,
|
||||||
&instance.hostname(),
|
&instance.hostname(),
|
||||||
&instance.url(),
|
&instance.url(),
|
||||||
&post_data.content,
|
&content,
|
||||||
);
|
);
|
||||||
post_data.mentions.extend(mention_map.values()
|
mentions.extend(mention_map.values().map(|profile| profile.id));
|
||||||
.map(|profile| profile.id));
|
// Hashtags
|
||||||
if post_data.visibility == Visibility::Subscribers {
|
let tags = find_hashtags(&content);
|
||||||
|
content = replace_hashtags(
|
||||||
|
&instance.url(),
|
||||||
|
&content,
|
||||||
|
&tags,
|
||||||
|
);
|
||||||
|
// Links
|
||||||
|
let link_map = find_linked_posts(
|
||||||
|
db_client,
|
||||||
|
&instance.url(),
|
||||||
|
&content,
|
||||||
|
).await?;
|
||||||
|
content = replace_object_links(
|
||||||
|
&link_map,
|
||||||
|
&content,
|
||||||
|
);
|
||||||
|
let links: Vec<_> = link_map.values().map(|post| post.id).collect();
|
||||||
|
let linked = link_map.into_values().collect();
|
||||||
|
// Clean content
|
||||||
|
content = clean_content(&content)?;
|
||||||
|
|
||||||
|
if visibility == Visibility::Subscribers {
|
||||||
// Mention all subscribers.
|
// Mention all subscribers.
|
||||||
// This makes post accessible only to active subscribers
|
// This makes post accessible only to active subscribers
|
||||||
// and is required for sending activities to subscribers
|
// and is required for sending activities to subscribers
|
||||||
// on other instances.
|
// on other instances.
|
||||||
let subscribers = get_subscribers(db_client, ¤t_user.id).await?
|
let subscribers = get_subscribers(db_client, ¤t_user.id).await?
|
||||||
.into_iter().map(|profile| profile.id);
|
.into_iter().map(|profile| profile.id);
|
||||||
post_data.mentions.extend(subscribers);
|
mentions.extend(subscribers);
|
||||||
};
|
};
|
||||||
// Hashtags
|
// Remove duplicate mentions
|
||||||
post_data.tags = find_hashtags(&post_data.content);
|
mentions.sort();
|
||||||
post_data.content = replace_hashtags(
|
mentions.dedup();
|
||||||
&instance.url(),
|
|
||||||
&post_data.content,
|
|
||||||
&post_data.tags,
|
|
||||||
);
|
|
||||||
// Links
|
|
||||||
let link_map = find_linked_posts(
|
|
||||||
db_client,
|
|
||||||
&instance.url(),
|
|
||||||
&post_data.content,
|
|
||||||
).await?;
|
|
||||||
post_data.content = replace_object_links(
|
|
||||||
&link_map,
|
|
||||||
&post_data.content,
|
|
||||||
);
|
|
||||||
post_data.links.extend(link_map.values().map(|post| post.id));
|
|
||||||
let linked = link_map.into_values().collect();
|
|
||||||
// Clean content
|
|
||||||
post_data.content = clean_content(&post_data.content)?;
|
|
||||||
|
|
||||||
// Links validation
|
// Links validation
|
||||||
if post_data.links.len() > 0 && post_data.visibility != Visibility::Public {
|
if links.len() > 0 && visibility != Visibility::Public {
|
||||||
return Err(ValidationError("can't add links to non-public posts").into());
|
return Err(ValidationError("can't add links to non-public posts").into());
|
||||||
};
|
};
|
||||||
// Reply validation
|
// Reply validation
|
||||||
let maybe_in_reply_to = if let Some(in_reply_to_id) = post_data.in_reply_to_id.as_ref() {
|
let maybe_in_reply_to = if let Some(in_reply_to_id) = status_data.in_reply_to_id.as_ref() {
|
||||||
let in_reply_to = match get_post_by_id(db_client, in_reply_to_id).await {
|
let in_reply_to = match get_post_by_id(db_client, in_reply_to_id).await {
|
||||||
Ok(post) => post,
|
Ok(post) => post,
|
||||||
Err(DatabaseError::NotFound(_)) => {
|
Err(DatabaseError::NotFound(_)) => {
|
||||||
|
@ -119,14 +143,14 @@ async fn create_status(
|
||||||
return Err(ValidationError("can't reply to repost").into());
|
return Err(ValidationError("can't reply to repost").into());
|
||||||
};
|
};
|
||||||
if in_reply_to.visibility != Visibility::Public &&
|
if in_reply_to.visibility != Visibility::Public &&
|
||||||
post_data.visibility != Visibility::Direct {
|
visibility != Visibility::Direct {
|
||||||
return Err(ValidationError("reply must have direct visibility").into());
|
return Err(ValidationError("reply must have direct visibility").into());
|
||||||
};
|
};
|
||||||
if post_data.visibility != Visibility::Public {
|
if visibility != Visibility::Public {
|
||||||
let mut in_reply_to_audience: Vec<_> = in_reply_to.mentions.iter()
|
let mut in_reply_to_audience: Vec<_> = in_reply_to.mentions.iter()
|
||||||
.map(|profile| profile.id).collect();
|
.map(|profile| profile.id).collect();
|
||||||
in_reply_to_audience.push(in_reply_to.author.id);
|
in_reply_to_audience.push(in_reply_to.author.id);
|
||||||
if !post_data.mentions.iter().all(|id| in_reply_to_audience.contains(id)) {
|
if !mentions.iter().all(|id| in_reply_to_audience.contains(id)) {
|
||||||
return Err(ValidationError("audience can't be expanded").into());
|
return Err(ValidationError("audience can't be expanded").into());
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -134,11 +158,20 @@ async fn create_status(
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
// Remove duplicate mentions
|
|
||||||
post_data.mentions.sort();
|
|
||||||
post_data.mentions.dedup();
|
|
||||||
|
|
||||||
// Create post
|
// Create post
|
||||||
|
let post_data = PostCreateData {
|
||||||
|
content: content,
|
||||||
|
in_reply_to_id: status_data.in_reply_to_id,
|
||||||
|
repost_of_id: None,
|
||||||
|
visibility: visibility,
|
||||||
|
attachments: status_data.media_ids.unwrap_or(vec![]),
|
||||||
|
mentions: mentions,
|
||||||
|
tags: tags,
|
||||||
|
links: links,
|
||||||
|
object_id: None,
|
||||||
|
created_at: Utc::now(),
|
||||||
|
};
|
||||||
let mut post = create_post(db_client, ¤t_user.id, post_data).await?;
|
let mut post = create_post(db_client, ¤t_user.id, post_data).await?;
|
||||||
post.in_reply_to = maybe_in_reply_to.map(|mut in_reply_to| {
|
post.in_reply_to = maybe_in_reply_to.map(|mut in_reply_to| {
|
||||||
in_reply_to.reply_count += 1;
|
in_reply_to.reply_count += 1;
|
||||||
|
|
Loading…
Reference in a new issue