Download custom emojis contained in remote posts
This commit is contained in:
parent
7b8a56dd8f
commit
56e75895bd
14 changed files with 366 additions and 25 deletions
16
migrations/V0039__emoji.sql
Normal file
16
migrations/V0039__emoji.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
CREATE TABLE emoji (
|
||||
id UUID PRIMARY KEY,
|
||||
emoji_name VARCHAR(100) NOT NULL,
|
||||
hostname VARCHAR(100) REFERENCES instance (hostname) ON DELETE RESTRICT,
|
||||
image JSONB NOT NULL,
|
||||
object_id VARCHAR(250) UNIQUE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
UNIQUE (emoji_name, hostname),
|
||||
CHECK ((hostname IS NULL) = (object_id IS NULL))
|
||||
);
|
||||
|
||||
CREATE TABLE post_emoji (
|
||||
post_id UUID NOT NULL REFERENCES post (id) ON DELETE CASCADE,
|
||||
emoji_id UUID NOT NULL REFERENCES emoji (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (post_id, emoji_id)
|
||||
);
|
|
@ -136,6 +136,23 @@ CREATE TABLE post_link (
|
|||
PRIMARY KEY (source_id, target_id)
|
||||
);
|
||||
|
||||
CREATE TABLE emoji (
|
||||
id UUID PRIMARY KEY,
|
||||
emoji_name VARCHAR(100) NOT NULL,
|
||||
hostname VARCHAR(100) REFERENCES instance (hostname) ON DELETE RESTRICT,
|
||||
image JSONB NOT NULL,
|
||||
object_id VARCHAR(250) UNIQUE,
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL,
|
||||
UNIQUE (emoji_name, hostname),
|
||||
CHECK ((hostname IS NULL) = (object_id IS NULL))
|
||||
);
|
||||
|
||||
CREATE TABLE post_emoji (
|
||||
post_id UUID NOT NULL REFERENCES post (id) ON DELETE CASCADE,
|
||||
emoji_id UUID NOT NULL REFERENCES emoji (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (post_id, emoji_id)
|
||||
);
|
||||
|
||||
CREATE TABLE notification (
|
||||
id SERIAL PRIMARY KEY,
|
||||
sender_id UUID NOT NULL REFERENCES actor_profile (id) ON DELETE CASCADE,
|
||||
|
|
|
@ -23,6 +23,11 @@ use crate::config::{Config, Instance};
|
|||
use crate::database::DatabaseError;
|
||||
use crate::errors::{ConversionError, ValidationError};
|
||||
use crate::models::attachments::queries::create_attachment;
|
||||
use crate::models::emojis::queries::{
|
||||
create_emoji,
|
||||
get_emoji_by_remote_object_id,
|
||||
update_emoji,
|
||||
};
|
||||
use crate::models::posts::{
|
||||
hashtags::normalize_hashtag,
|
||||
helpers::get_post_by_object_id,
|
||||
|
@ -33,11 +38,17 @@ use crate::models::posts::{
|
|||
content_allowed_classes,
|
||||
ATTACHMENTS_MAX_NUM,
|
||||
CONTENT_MAX_SIZE,
|
||||
EMOJI_MAX_SIZE,
|
||||
EMOJI_MEDIA_TYPE,
|
||||
EMOJIS_MAX_NUM,
|
||||
},
|
||||
};
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
use crate::models::users::queries::get_user_by_name;
|
||||
use crate::utils::html::clean_html;
|
||||
use crate::utils::{
|
||||
html::clean_html,
|
||||
urls::get_hostname,
|
||||
};
|
||||
use super::HandlerResult;
|
||||
|
||||
fn get_note_author_id(object: &Object) -> Result<String, ValidationError> {
|
||||
|
@ -221,6 +232,7 @@ pub async fn handle_note(
|
|||
let mut mentions: Vec<Uuid> = Vec::new();
|
||||
let mut hashtags = vec![];
|
||||
let mut links = vec![];
|
||||
let mut emojis = vec![];
|
||||
if let Some(value) = object.tag {
|
||||
let list: Vec<JsonValue> = parse_property_value(&value)
|
||||
.map_err(|_| ValidationError("invalid tag property"))?;
|
||||
|
@ -343,13 +355,84 @@ pub async fn handle_note(
|
|||
links.push(linked.id);
|
||||
};
|
||||
} else if tag_type == EMOJI {
|
||||
let _tag: EmojiTag = match serde_json::from_value(tag_value.clone()) {
|
||||
let tag: EmojiTag = match serde_json::from_value(tag_value) {
|
||||
Ok(tag) => tag,
|
||||
Err(_) => {
|
||||
log::warn!("invalid emoji tag");
|
||||
continue;
|
||||
},
|
||||
};
|
||||
if emojis.len() >= EMOJIS_MAX_NUM {
|
||||
log::warn!("too many emojis");
|
||||
continue;
|
||||
};
|
||||
let tag_name = tag.name.trim_matches(':');
|
||||
let maybe_emoji_id = match get_emoji_by_remote_object_id(
|
||||
db_client,
|
||||
&tag.id,
|
||||
).await {
|
||||
Ok(emoji) => {
|
||||
if emoji.updated_at >= tag.updated {
|
||||
// Emoji already exists and is up to date
|
||||
if !emojis.contains(&emoji.id) {
|
||||
emojis.push(emoji.id);
|
||||
};
|
||||
continue;
|
||||
};
|
||||
if emoji.emoji_name != tag_name {
|
||||
log::warn!("emoji name can't be changed");
|
||||
continue;
|
||||
};
|
||||
Some(emoji.id)
|
||||
},
|
||||
Err(DatabaseError::NotFound("emoji")) => None,
|
||||
Err(other_error) => return Err(other_error.into()),
|
||||
};
|
||||
let (file_name, maybe_media_type) = match fetch_file(
|
||||
instance,
|
||||
&tag.icon.url,
|
||||
tag.icon.media_type.as_deref(),
|
||||
EMOJI_MAX_SIZE,
|
||||
media_dir,
|
||||
).await {
|
||||
Ok(file) => file,
|
||||
Err(error) => {
|
||||
log::warn!("failed to fetch emoji: {}", error);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
let media_type = match maybe_media_type.as_deref() {
|
||||
Some(media_type @ EMOJI_MEDIA_TYPE) => media_type,
|
||||
_ => {
|
||||
log::warn!("unexpected emoji media type: {:?}", maybe_media_type);
|
||||
continue;
|
||||
},
|
||||
};
|
||||
log::info!("downloaded emoji {}", tag.icon.url);
|
||||
let emoji = if let Some(emoji_id) = maybe_emoji_id {
|
||||
update_emoji(
|
||||
db_client,
|
||||
&emoji_id,
|
||||
&file_name,
|
||||
media_type,
|
||||
&tag.updated,
|
||||
).await?
|
||||
} else {
|
||||
let hostname = get_hostname(&tag.id)
|
||||
.map_err(|_| ValidationError("invalid emoji ID"))?;
|
||||
create_emoji(
|
||||
db_client,
|
||||
tag_name,
|
||||
Some(&hostname),
|
||||
&file_name,
|
||||
media_type,
|
||||
Some(&tag.id),
|
||||
&tag.updated,
|
||||
).await?
|
||||
};
|
||||
if !emojis.contains(&emoji.id) {
|
||||
emojis.push(emoji.id);
|
||||
};
|
||||
} else {
|
||||
log::warn!(
|
||||
"skipping tag of type {}",
|
||||
|
@ -417,6 +500,7 @@ pub async fn handle_note(
|
|||
mentions: mentions,
|
||||
tags: hashtags,
|
||||
links: links,
|
||||
emojis: emojis,
|
||||
object_id: Some(object.id),
|
||||
created_at,
|
||||
};
|
||||
|
|
|
@ -57,11 +57,11 @@ pub struct LinkTag {
|
|||
#[allow(dead_code)]
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct EmojiImage {
|
||||
pub struct EmojiImage {
|
||||
#[serde(rename = "type")]
|
||||
object_type: String,
|
||||
url: String,
|
||||
media_type: Option<String>,
|
||||
pub url: String,
|
||||
pub media_type: Option<String>,
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -70,10 +70,10 @@ struct EmojiImage {
|
|||
pub struct EmojiTag {
|
||||
#[serde(rename = "type")]
|
||||
tag_type: String,
|
||||
icon: EmojiImage,
|
||||
id: String,
|
||||
name: String,
|
||||
updated: DateTime<Utc>,
|
||||
pub icon: EmojiImage,
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub updated: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
|
|
@ -155,6 +155,7 @@ async fn create_status(
|
|||
mentions: mentions,
|
||||
tags: tags,
|
||||
links: links,
|
||||
emojis: vec![],
|
||||
object_id: None,
|
||||
created_at: Utc::now(),
|
||||
};
|
||||
|
|
2
src/models/emojis/mod.rs
Normal file
2
src/models/emojis/mod.rs
Normal file
|
@ -0,0 +1,2 @@
|
|||
pub mod queries;
|
||||
pub mod types;
|
135
src/models/emojis/queries.rs
Normal file
135
src/models/emojis/queries.rs
Normal file
|
@ -0,0 +1,135 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use tokio_postgres::GenericClient;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::database::{catch_unique_violation, DatabaseError};
|
||||
use crate::models::{
|
||||
instances::queries::create_instance,
|
||||
profiles::types::ProfileImage,
|
||||
};
|
||||
use crate::utils::id::new_uuid;
|
||||
use super::types::DbEmoji;
|
||||
|
||||
pub async fn create_emoji(
|
||||
db_client: &impl GenericClient,
|
||||
emoji_name: &str,
|
||||
hostname: Option<&str>,
|
||||
file_name: &str,
|
||||
media_type: &str,
|
||||
object_id: Option<&str>,
|
||||
updated_at: &DateTime<Utc>,
|
||||
) -> Result<DbEmoji, DatabaseError> {
|
||||
let emoji_id = new_uuid();
|
||||
if let Some(hostname) = hostname {
|
||||
create_instance(db_client, hostname).await?;
|
||||
};
|
||||
let image = ProfileImage {
|
||||
file_name: file_name.to_string(),
|
||||
media_type: Some(media_type.to_string()),
|
||||
};
|
||||
let row = db_client.query_one(
|
||||
"
|
||||
INSERT INTO emoji (
|
||||
id,
|
||||
emoji_name,
|
||||
hostname,
|
||||
image,
|
||||
object_id,
|
||||
updated_at
|
||||
)
|
||||
VALUES ($1, $2, $3, $4, $5, $6)
|
||||
RETURNING emoji
|
||||
",
|
||||
&[
|
||||
&emoji_id,
|
||||
&emoji_name,
|
||||
&hostname,
|
||||
&image,
|
||||
&object_id,
|
||||
&updated_at,
|
||||
],
|
||||
).await.map_err(catch_unique_violation("emoji"))?;
|
||||
let emoji = row.try_get("emoji")?;
|
||||
Ok(emoji)
|
||||
}
|
||||
|
||||
pub async fn update_emoji(
|
||||
db_client: &impl GenericClient,
|
||||
emoji_id: &Uuid,
|
||||
file_name: &str,
|
||||
media_type: &str,
|
||||
updated_at: &DateTime<Utc>,
|
||||
) -> Result<DbEmoji, DatabaseError> {
|
||||
let image = ProfileImage {
|
||||
file_name: file_name.to_string(),
|
||||
media_type: Some(media_type.to_string()),
|
||||
};
|
||||
let row = db_client.query_one(
|
||||
"
|
||||
UPDATE emoji
|
||||
SET
|
||||
image = $1,
|
||||
updated_at = $2
|
||||
WHERE id = $4
|
||||
RETURNING emoji
|
||||
",
|
||||
&[
|
||||
&image,
|
||||
&updated_at,
|
||||
&emoji_id,
|
||||
],
|
||||
).await?;
|
||||
let emoji = row.try_get("emoji")?;
|
||||
Ok(emoji)
|
||||
}
|
||||
|
||||
pub async fn get_emoji_by_remote_object_id(
|
||||
db_client: &impl GenericClient,
|
||||
object_id: &str,
|
||||
) -> Result<DbEmoji, DatabaseError> {
|
||||
let maybe_row = db_client.query_opt(
|
||||
"
|
||||
SELECT emoji
|
||||
FROM emoji WHERE object_id = $1
|
||||
",
|
||||
&[&object_id],
|
||||
).await?;
|
||||
let row = maybe_row.ok_or(DatabaseError::NotFound("emoji"))?;
|
||||
let emoji = row.try_get("emoji")?;
|
||||
Ok(emoji)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serial_test::serial;
|
||||
use crate::database::test_utils::create_test_database;
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_create_emoji() {
|
||||
let db_client = &create_test_database().await;
|
||||
let emoji_name = "test";
|
||||
let hostname = "example.org";
|
||||
let file_name = "test.png";
|
||||
let media_type = "image/png";
|
||||
let object_id = "https://example.org/emojis/test";
|
||||
let updated_at = Utc::now();
|
||||
let DbEmoji { id: emoji_id, .. } = create_emoji(
|
||||
db_client,
|
||||
emoji_name,
|
||||
Some(hostname),
|
||||
file_name,
|
||||
media_type,
|
||||
Some(object_id),
|
||||
&updated_at,
|
||||
).await.unwrap();
|
||||
let emoji = get_emoji_by_remote_object_id(
|
||||
db_client,
|
||||
object_id,
|
||||
).await.unwrap();
|
||||
assert_eq!(emoji.id, emoji_id);
|
||||
assert_eq!(emoji.emoji_name, emoji_name);
|
||||
assert_eq!(emoji.hostname, Some(hostname.to_string()));
|
||||
}
|
||||
}
|
16
src/models/emojis/types.rs
Normal file
16
src/models/emojis/types.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use chrono::{DateTime, Utc};
|
||||
use postgres_types::FromSql;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::models::profiles::types::ProfileImage;
|
||||
|
||||
#[derive(Clone, FromSql)]
|
||||
#[postgres(name = "emoji")]
|
||||
pub struct DbEmoji {
|
||||
pub id: Uuid,
|
||||
pub emoji_name: String,
|
||||
pub hostname: Option<String>,
|
||||
pub image: ProfileImage,
|
||||
pub object_id: Option<String>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
pub mod attachments;
|
||||
pub mod background_jobs;
|
||||
pub mod cleanup;
|
||||
pub mod emojis;
|
||||
pub mod instances;
|
||||
pub mod invoices;
|
||||
pub mod markers;
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::database::DatabaseError;
|
|||
use crate::models::posts::helpers::{add_related_posts, add_user_actions};
|
||||
use crate::models::posts::queries::{
|
||||
RELATED_ATTACHMENTS,
|
||||
RELATED_EMOJIS,
|
||||
RELATED_LINKS,
|
||||
RELATED_MENTIONS,
|
||||
RELATED_TAGS,
|
||||
|
@ -138,7 +139,8 @@ pub async fn get_notifications(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM notification
|
||||
JOIN actor_profile AS sender
|
||||
ON notification.sender_id = sender.id
|
||||
|
@ -156,6 +158,7 @@ pub async fn get_notifications(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
);
|
||||
let rows = db_client.query(
|
||||
&statement,
|
||||
|
|
|
@ -8,9 +8,12 @@ use crate::database::{
|
|||
DatabaseError,
|
||||
DatabaseTypeError,
|
||||
};
|
||||
use crate::models::attachments::types::DbMediaAttachment;
|
||||
use crate::models::posts::types::{DbPost, Post};
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
use crate::models::{
|
||||
attachments::types::DbMediaAttachment,
|
||||
emojis::types::DbEmoji,
|
||||
posts::types::{DbPost, Post},
|
||||
profiles::types::DbActorProfile,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum EventType {
|
||||
|
@ -102,6 +105,7 @@ impl TryFrom<&Row> for Notification {
|
|||
let db_mentions: Vec<DbActorProfile> = row.try_get("mentions")?;
|
||||
let db_tags: Vec<String> = row.try_get("tags")?;
|
||||
let db_links: Vec<Uuid> = row.try_get("links")?;
|
||||
let db_emojis: Vec<DbEmoji> = row.try_get("emojis")?;
|
||||
let post = Post::new(
|
||||
db_post,
|
||||
db_post_author,
|
||||
|
@ -109,6 +113,7 @@ impl TryFrom<&Row> for Notification {
|
|||
db_mentions,
|
||||
db_tags,
|
||||
db_links,
|
||||
db_emojis,
|
||||
)?;
|
||||
Some(post)
|
||||
},
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::models::cleanup::{
|
|||
find_orphaned_ipfs_objects,
|
||||
DeletionQueue,
|
||||
};
|
||||
use crate::models::emojis::types::DbEmoji;
|
||||
use crate::models::notifications::queries::{
|
||||
create_mention_notification,
|
||||
create_reply_notification,
|
||||
|
@ -157,6 +158,24 @@ pub async fn create_post(
|
|||
let db_links: Vec<Uuid> = links_rows.iter()
|
||||
.map(|row| row.try_get("target_id"))
|
||||
.collect::<Result<_, _>>()?;
|
||||
// Create emojis
|
||||
let emojis_rows = transaction.query(
|
||||
"
|
||||
INSERT INTO post_emoji (post_id, emoji_id)
|
||||
SELECT $1, emoji.id FROM emoji WHERE id = ANY($2)
|
||||
RETURNING (
|
||||
SELECT emoji FROM emoji
|
||||
WHERE emoji.id = emoji_id
|
||||
)
|
||||
",
|
||||
&[&db_post.id, &data.emojis],
|
||||
).await?;
|
||||
if emojis_rows.len() != data.emojis.len() {
|
||||
return Err(DatabaseError::NotFound("emoji"));
|
||||
};
|
||||
let db_emojis: Vec<DbEmoji> = emojis_rows.iter()
|
||||
.map(|row| row.try_get("emoji"))
|
||||
.collect::<Result<_, _>>()?;
|
||||
// Update counters
|
||||
let author = update_post_count(&transaction, &db_post.author_id, 1).await?;
|
||||
let mut notified_users = vec![];
|
||||
|
@ -216,6 +235,7 @@ pub async fn create_post(
|
|||
db_mentions,
|
||||
db_tags,
|
||||
db_links,
|
||||
db_emojis,
|
||||
)?;
|
||||
Ok(post)
|
||||
}
|
||||
|
@ -247,6 +267,14 @@ pub const RELATED_LINKS: &str =
|
|||
WHERE post_link.source_id = post.id
|
||||
) AS links";
|
||||
|
||||
pub const RELATED_EMOJIS: &str =
|
||||
"ARRAY(
|
||||
SELECT emoji
|
||||
FROM post_emoji
|
||||
JOIN emoji ON post_emoji.emoji_id = emoji.id
|
||||
WHERE post_emoji.post_id = post.id
|
||||
) AS emojis";
|
||||
|
||||
fn build_visibility_filter() -> String {
|
||||
format!(
|
||||
"(
|
||||
|
@ -287,7 +315,8 @@ pub async fn get_home_timeline(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE
|
||||
|
@ -353,6 +382,7 @@ pub async fn get_home_timeline(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
relationship_follow=i16::from(&RelationshipType::Follow),
|
||||
relationship_subscription=i16::from(&RelationshipType::Subscription),
|
||||
relationship_hide_reposts=i16::from(&RelationshipType::HideReposts),
|
||||
|
@ -386,7 +416,8 @@ pub async fn get_local_timeline(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE
|
||||
|
@ -400,6 +431,7 @@ pub async fn get_local_timeline(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
visibility_public=i16::from(&Visibility::Public),
|
||||
);
|
||||
let limit: i64 = limit.into();
|
||||
|
@ -427,7 +459,8 @@ pub async fn get_related_posts(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE post.id IN (
|
||||
|
@ -449,6 +482,7 @@ pub async fn get_related_posts(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
);
|
||||
let rows = db_client.query(
|
||||
&statement,
|
||||
|
@ -488,7 +522,8 @@ pub async fn get_posts_by_author(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE {condition}
|
||||
|
@ -499,6 +534,7 @@ pub async fn get_posts_by_author(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
condition=condition,
|
||||
);
|
||||
let limit: i64 = limit.into();
|
||||
|
@ -531,7 +567,8 @@ pub async fn get_posts_by_tag(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE
|
||||
|
@ -548,6 +585,7 @@ pub async fn get_posts_by_tag(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
visibility_filter=build_visibility_filter(),
|
||||
);
|
||||
let limit: i64 = limit.into();
|
||||
|
@ -576,7 +614,8 @@ pub async fn get_post_by_id(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE post.id = $1
|
||||
|
@ -585,6 +624,7 @@ pub async fn get_post_by_id(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
);
|
||||
let maybe_row = db_client.query_opt(
|
||||
&statement,
|
||||
|
@ -629,7 +669,8 @@ pub async fn get_thread(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN thread ON post.id = thread.id
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
|
@ -640,6 +681,7 @@ pub async fn get_thread(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
visibility_filter=build_visibility_filter(),
|
||||
);
|
||||
let query = query!(
|
||||
|
@ -668,7 +710,8 @@ pub async fn get_post_by_remote_object_id(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE post.object_id = $1
|
||||
|
@ -677,6 +720,7 @@ pub async fn get_post_by_remote_object_id(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
);
|
||||
let maybe_row = db_client.query_opt(
|
||||
&statement,
|
||||
|
@ -698,7 +742,8 @@ pub async fn get_post_by_ipfs_cid(
|
|||
{related_attachments},
|
||||
{related_mentions},
|
||||
{related_tags},
|
||||
{related_links}
|
||||
{related_links},
|
||||
{related_emojis}
|
||||
FROM post
|
||||
JOIN actor_profile ON post.author_id = actor_profile.id
|
||||
WHERE post.ipfs_cid = $1
|
||||
|
@ -707,6 +752,7 @@ pub async fn get_post_by_ipfs_cid(
|
|||
related_mentions=RELATED_MENTIONS,
|
||||
related_tags=RELATED_TAGS,
|
||||
related_links=RELATED_LINKS,
|
||||
related_emojis=RELATED_EMOJIS,
|
||||
);
|
||||
let result = db_client.query_opt(
|
||||
&statement,
|
||||
|
|
|
@ -9,8 +9,11 @@ use crate::database::{
|
|||
DatabaseError,
|
||||
DatabaseTypeError,
|
||||
};
|
||||
use crate::models::attachments::types::DbMediaAttachment;
|
||||
use crate::models::profiles::types::DbActorProfile;
|
||||
use crate::models::{
|
||||
attachments::types::DbMediaAttachment,
|
||||
emojis::types::DbEmoji,
|
||||
profiles::types::DbActorProfile,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Visibility {
|
||||
|
@ -95,6 +98,7 @@ pub struct Post {
|
|||
pub mentions: Vec<DbActorProfile>,
|
||||
pub tags: Vec<String>,
|
||||
pub links: Vec<Uuid>,
|
||||
pub emojis: Vec<DbEmoji>,
|
||||
pub object_id: Option<String>,
|
||||
pub ipfs_cid: Option<String>,
|
||||
pub token_id: Option<i32>,
|
||||
|
@ -118,6 +122,7 @@ impl Post {
|
|||
db_mentions: Vec<DbActorProfile>,
|
||||
db_tags: Vec<String>,
|
||||
db_links: Vec<Uuid>,
|
||||
db_emojis: Vec<DbEmoji>,
|
||||
) -> Result<Self, DatabaseTypeError> {
|
||||
// Consistency checks
|
||||
if db_post.author_id != db_author.id {
|
||||
|
@ -135,7 +140,8 @@ impl Post {
|
|||
!db_attachments.is_empty() ||
|
||||
!db_mentions.is_empty() ||
|
||||
!db_tags.is_empty() ||
|
||||
!db_links.is_empty()
|
||||
!db_links.is_empty() ||
|
||||
!db_emojis.is_empty()
|
||||
) {
|
||||
return Err(DatabaseTypeError);
|
||||
};
|
||||
|
@ -153,6 +159,7 @@ impl Post {
|
|||
mentions: db_mentions,
|
||||
tags: db_tags,
|
||||
links: db_links,
|
||||
emojis: db_emojis,
|
||||
object_id: db_post.object_id,
|
||||
ipfs_cid: db_post.ipfs_cid,
|
||||
token_id: db_post.token_id,
|
||||
|
@ -200,6 +207,7 @@ impl Default for Post {
|
|||
mentions: vec![],
|
||||
tags: vec![],
|
||||
links: vec![],
|
||||
emojis: vec![],
|
||||
object_id: None,
|
||||
ipfs_cid: None,
|
||||
token_id: None,
|
||||
|
@ -225,6 +233,7 @@ impl TryFrom<&Row> for Post {
|
|||
let db_mentions: Vec<DbActorProfile> = row.try_get("mentions")?;
|
||||
let db_tags: Vec<String> = row.try_get("tags")?;
|
||||
let db_links: Vec<Uuid> = row.try_get("links")?;
|
||||
let db_emojis: Vec<DbEmoji> = row.try_get("emojis")?;
|
||||
let post = Self::new(
|
||||
db_post,
|
||||
db_profile,
|
||||
|
@ -232,6 +241,7 @@ impl TryFrom<&Row> for Post {
|
|||
db_mentions,
|
||||
db_tags,
|
||||
db_links,
|
||||
db_emojis,
|
||||
)?;
|
||||
Ok(post)
|
||||
}
|
||||
|
@ -247,6 +257,7 @@ pub struct PostCreateData {
|
|||
pub mentions: Vec<Uuid>,
|
||||
pub tags: Vec<String>,
|
||||
pub links: Vec<Uuid>,
|
||||
pub emojis: Vec<Uuid>,
|
||||
pub object_id: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -2,6 +2,10 @@ use crate::errors::ValidationError;
|
|||
use crate::utils::html::clean_html_strict;
|
||||
|
||||
pub const ATTACHMENTS_MAX_NUM: usize = 15;
|
||||
pub const EMOJI_MAX_SIZE: u64 = 100 * 1000; // 100 kB
|
||||
pub const EMOJI_MEDIA_TYPE: &str = "image/png";
|
||||
pub const EMOJIS_MAX_NUM: usize = 20;
|
||||
|
||||
pub const CONTENT_MAX_SIZE: usize = 100000;
|
||||
const CONTENT_ALLOWED_TAGS: [&str; 8] = [
|
||||
"a",
|
||||
|
|
Loading…
Reference in a new issue