pull/1478/head
Dessalines 2021-03-02 11:52:46 -05:00
commit ca3c1269f5
44 changed files with 327 additions and 269 deletions

4
Cargo.lock generated
View File

@ -3655,9 +3655,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "url" name = "url"
version = "2.2.0" version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
dependencies = [ dependencies = [
"form_urlencoded", "form_urlencoded",
"idna", "idna",

View File

@ -45,7 +45,7 @@ actix-web = { version = "3.3.2", default-features = false, features = ["rustls"]
log = "0.4.14" log = "0.4.14"
env_logger = "0.8.2" env_logger = "0.8.2"
strum = "0.20.0" strum = "0.20.0"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
openssl = "0.10.32" openssl = "0.10.32"
http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] } http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] }
tokio = "0.3.6" tokio = "0.3.6"

View File

@ -32,7 +32,7 @@ rand = "0.8.3"
strum = "0.20.0" strum = "0.20.0"
strum_macros = "0.20.1" strum_macros = "0.20.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
openssl = "0.10.32" openssl = "0.10.32"
http = "0.2.3" http = "0.2.3"
http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] } http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] }

View File

@ -1,6 +1,5 @@
use crate::{ use crate::{
check_community_ban, check_community_ban,
check_optional_url,
get_user_from_jwt, get_user_from_jwt,
get_user_from_jwt_opt, get_user_from_jwt_opt,
is_admin, is_admin,
@ -19,7 +18,7 @@ use lemmy_apub::{
EndpointType, EndpointType,
}; };
use lemmy_db_queries::{ use lemmy_db_queries::{
diesel_option_overwrite, diesel_option_overwrite_to_url,
source::{ source::{
comment::Comment_, comment::Comment_,
community::{CommunityModerator_, Community_}, community::{CommunityModerator_, Community_},
@ -155,11 +154,8 @@ impl Perform for CreateCommunity {
} }
// Check to make sure the icon and banners are urls // Check to make sure the icon and banners are urls
let icon = diesel_option_overwrite(&data.icon); let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
check_optional_url(&icon)?;
check_optional_url(&banner)?;
// When you create a community, make sure the user becomes a moderator and a follower // When you create a community, make sure the user becomes a moderator and a follower
let keypair = generate_actor_keypair()?; let keypair = generate_actor_keypair()?;
@ -260,11 +256,8 @@ impl Perform for EditCommunity {
}) })
.await??; .await??;
let icon = diesel_option_overwrite(&data.icon); let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
check_optional_url(&icon)?;
check_optional_url(&banner)?;
let community_form = CommunityForm { let community_form = CommunityForm {
name: read_community.name, name: read_community.name,

View File

@ -186,15 +186,6 @@ pub(crate) async fn collect_moderated_communities(
} }
} }
pub(crate) fn check_optional_url(item: &Option<Option<String>>) -> Result<(), LemmyError> {
if let Some(Some(item)) = &item {
if Url::parse(item).is_err() {
return Err(ApiError::err("invalid_url").into());
}
}
Ok(())
}
pub(crate) async fn build_federated_instances( pub(crate) async fn build_federated_instances(
pool: &DbPool, pool: &DbPool,
) -> Result<Option<FederatedInstances>, LemmyError> { ) -> Result<Option<FederatedInstances>, LemmyError> {
@ -474,6 +465,15 @@ pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
Ok(base64) Ok(base64)
} }
/// Checks the password length
pub(crate) fn password_length_check(pass: &str) -> Result<(), LemmyError> {
if pass.len() > 60 {
Err(ApiError::err("invalid_password").into())
} else {
Ok(())
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::captcha_espeak_wav_base64; use crate::captcha_espeak_wav_base64;

View File

@ -1,7 +1,6 @@
use crate::{ use crate::{
check_community_ban, check_community_ban,
check_downvotes_enabled, check_downvotes_enabled,
check_optional_url,
collect_moderated_communities, collect_moderated_communities,
get_user_from_jwt, get_user_from_jwt,
get_user_from_jwt_opt, get_user_from_jwt_opt,
@ -72,15 +71,14 @@ impl Perform for CreatePost {
check_community_ban(user.id, data.community_id, context.pool()).await?; check_community_ban(user.id, data.community_id, context.pool()).await?;
check_optional_url(&Some(data.url.to_owned()))?;
// Fetch Iframely and pictrs cached image // Fetch Iframely and pictrs cached image
let data_url = data.url.as_ref();
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(context.client(), data.url.to_owned()).await; fetch_iframely_and_pictrs_data(context.client(), data_url).await;
let post_form = PostForm { let post_form = PostForm {
name: data.name.trim().to_owned(), name: data.name.trim().to_owned(),
url: data.url.to_owned(), url: data_url.map(|u| u.to_owned().into()),
body: data.body.to_owned(), body: data.body.to_owned(),
community_id: data.community_id, community_id: data.community_id,
creator_id: user.id, creator_id: user.id,
@ -93,7 +91,7 @@ impl Perform for CreatePost {
embed_title: iframely_title, embed_title: iframely_title,
embed_description: iframely_description, embed_description: iframely_description,
embed_html: iframely_html, embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail, thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: None, ap_id: None,
local: true, local: true,
published: None, published: None,
@ -385,12 +383,13 @@ impl Perform for EditPost {
} }
// Fetch Iframely and Pictrs cached image // Fetch Iframely and Pictrs cached image
let data_url = data.url.as_ref();
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
fetch_iframely_and_pictrs_data(context.client(), data.url.to_owned()).await; fetch_iframely_and_pictrs_data(context.client(), data_url).await;
let post_form = PostForm { let post_form = PostForm {
name: data.name.trim().to_owned(), name: data.name.trim().to_owned(),
url: data.url.to_owned(), url: data_url.map(|u| u.to_owned().into()),
body: data.body.to_owned(), body: data.body.to_owned(),
nsfw: data.nsfw, nsfw: data.nsfw,
creator_id: orig_post.creator_id.to_owned(), creator_id: orig_post.creator_id.to_owned(),
@ -403,7 +402,7 @@ impl Perform for EditPost {
embed_title: iframely_title, embed_title: iframely_title,
embed_description: iframely_description, embed_description: iframely_description,
embed_html: iframely_html, embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail, thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: Some(orig_post.ap_id), ap_id: Some(orig_post.ap_id),
local: orig_post.local, local: orig_post.local,
published: None, published: None,

View File

@ -11,7 +11,13 @@ use actix_web::web::Data;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::{blocking, site::*, user::Register}; use lemmy_api_structs::{blocking, site::*, user::Register};
use lemmy_apub::fetcher::search::search_by_apub_id; use lemmy_apub::fetcher::search::search_by_apub_id;
use lemmy_db_queries::{diesel_option_overwrite, source::site::Site_, Crud, SearchType, SortType}; use lemmy_db_queries::{
diesel_option_overwrite_to_url,
source::site::Site_,
Crud,
SearchType,
SortType,
};
use lemmy_db_schema::{ use lemmy_db_schema::{
naive_now, naive_now,
source::{ source::{
@ -157,8 +163,8 @@ impl Perform for CreateSite {
let site_form = SiteForm { let site_form = SiteForm {
name: data.name.to_owned(), name: data.name.to_owned(),
description: data.description.to_owned(), description: data.description.to_owned(),
icon: Some(data.icon.to_owned()), icon: Some(data.icon.to_owned().map(|url| url.into())),
banner: Some(data.banner.to_owned()), banner: Some(data.banner.to_owned().map(|url| url.into())),
creator_id: user.id, creator_id: user.id,
enable_downvotes: data.enable_downvotes, enable_downvotes: data.enable_downvotes,
open_registration: data.open_registration, open_registration: data.open_registration,
@ -196,8 +202,8 @@ impl Perform for EditSite {
let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??; let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
let icon = diesel_option_overwrite(&data.icon); let icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
let site_form = SiteForm { let site_form = SiteForm {
name: data.name.to_owned(), name: data.name.to_owned(),

View File

@ -1,10 +1,10 @@
use crate::{ use crate::{
captcha_espeak_wav_base64, captcha_espeak_wav_base64,
check_optional_url,
collect_moderated_communities, collect_moderated_communities,
get_user_from_jwt, get_user_from_jwt,
get_user_from_jwt_opt, get_user_from_jwt_opt,
is_admin, is_admin,
password_length_check,
Perform, Perform,
}; };
use actix_web::web::Data; use actix_web::web::Data;
@ -23,6 +23,7 @@ use lemmy_apub::{
}; };
use lemmy_db_queries::{ use lemmy_db_queries::{
diesel_option_overwrite, diesel_option_overwrite,
diesel_option_overwrite_to_url,
source::{ source::{
comment::Comment_, comment::Comment_,
community::Community_, community::Community_,
@ -144,10 +145,7 @@ impl Perform for Register {
} }
} }
// Password length check password_length_check(&data.password)?;
if data.password.len() > 60 {
return Err(ApiError::err("invalid_password").into());
}
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
@ -366,17 +364,13 @@ impl Perform for SaveUserSettings {
let data: &SaveUserSettings = &self; let data: &SaveUserSettings = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?; let user = get_user_from_jwt(&data.auth, context.pool()).await?;
let avatar = diesel_option_overwrite(&data.avatar); let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
let banner = diesel_option_overwrite(&data.banner); let banner = diesel_option_overwrite_to_url(&data.banner)?;
let email = diesel_option_overwrite(&data.email); let email = diesel_option_overwrite(&data.email);
let bio = diesel_option_overwrite(&data.bio); let bio = diesel_option_overwrite(&data.bio);
let preferred_username = diesel_option_overwrite(&data.preferred_username); let preferred_username = diesel_option_overwrite(&data.preferred_username);
let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id); let matrix_user_id = diesel_option_overwrite(&data.matrix_user_id);
// Check to make sure the avatar and banners are urls
check_optional_url(&avatar)?;
check_optional_url(&banner)?;
if let Some(Some(bio)) = &bio { if let Some(Some(bio)) = &bio {
if bio.chars().count() > 300 { if bio.chars().count() > 300 {
return Err(ApiError::err("bio_length_overflow").into()); return Err(ApiError::err("bio_length_overflow").into());
@ -394,6 +388,8 @@ impl Perform for SaveUserSettings {
Some(new_password) => { Some(new_password) => {
match &data.new_password_verify { match &data.new_password_verify {
Some(new_password_verify) => { Some(new_password_verify) => {
password_length_check(&new_password)?;
// Make sure passwords match // Make sure passwords match
if new_password != new_password_verify { if new_password != new_password_verify {
return Err(ApiError::err("passwords_dont_match").into()); return Err(ApiError::err("passwords_dont_match").into());
@ -993,6 +989,8 @@ impl Perform for PasswordChange {
}) })
.await??; .await??;
password_length_check(&data.password)?;
// Make sure passwords match // Make sure passwords match
if data.password != data.password_verify { if data.password != data.password_verify {
return Err(ApiError::err("passwords_dont_match").into()); return Err(ApiError::err("passwords_dont_match").into());

View File

@ -21,4 +21,4 @@ diesel = "1.4.5"
actix-web = "3.3.2" actix-web = "3.3.2"
chrono = { version = "0.4.19", features = ["serde"] } chrono = { version = "0.4.19", features = ["serde"] }
serde_json = { version = "1.0.61", features = ["preserve_order"] } serde_json = { version = "1.0.61", features = ["preserve_order"] }
url = "2.2.0" url = "2.2.1"

View File

@ -8,11 +8,12 @@ use lemmy_db_views_actor::{
community_view::CommunityView, community_view::CommunityView,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct CreatePost { pub struct CreatePost {
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<Url>,
pub body: Option<String>, pub body: Option<String>,
pub nsfw: bool, pub nsfw: bool,
pub community_id: i32, pub community_id: i32,
@ -66,7 +67,7 @@ pub struct CreatePostLike {
pub struct EditPost { pub struct EditPost {
pub post_id: i32, pub post_id: i32,
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<Url>,
pub body: Option<String>, pub body: Option<String>,
pub nsfw: bool, pub nsfw: bool,
pub auth: String, pub auth: String,

View File

@ -13,6 +13,7 @@ use lemmy_db_views_moderator::{
mod_sticky_post_view::ModStickyPostView, mod_sticky_post_view::ModStickyPostView,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
pub struct Search { pub struct Search {
@ -60,8 +61,8 @@ pub struct GetModlogResponse {
pub struct CreateSite { pub struct CreateSite {
pub name: String, pub name: String,
pub description: Option<String>, pub description: Option<String>,
pub icon: Option<String>, pub icon: Option<Url>,
pub banner: Option<String>, pub banner: Option<Url>,
pub enable_downvotes: bool, pub enable_downvotes: bool,
pub open_registration: bool, pub open_registration: bool,
pub enable_nsfw: bool, pub enable_nsfw: bool,

View File

@ -32,7 +32,7 @@ rand = "0.8.3"
strum = "0.20.0" strum = "0.20.0"
strum_macros = "0.20.1" strum_macros = "0.20.1"
lazy_static = "1.4.0" lazy_static = "1.4.0"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
percent-encoding = "2.1.0" percent-encoding = "2.1.0"
openssl = "0.10.32" openssl = "0.10.32"
http = "0.2.3" http = "0.2.3"

View File

@ -7,6 +7,7 @@ use lemmy_db_schema::source::activity::Activity;
use lemmy_utils::{settings::structs::Settings, LemmyError}; use lemmy_utils::{settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use url::Url;
pub mod comment; pub mod comment;
pub mod community; pub mod community;
@ -46,12 +47,13 @@ pub async fn get_activity(
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> { ) -> Result<HttpResponse<Body>, LemmyError> {
let settings = Settings::get(); let settings = Settings::get();
let activity_id = format!( let activity_id = Url::parse(&format!(
"{}/activities/{}/{}", "{}/activities/{}/{}",
settings.get_protocol_and_hostname(), settings.get_protocol_and_hostname(),
info.type_, info.type_,
info.id info.id
); ))?
.into();
let activity = blocking(context.pool(), move |conn| { let activity = blocking(context.pool(), move |conn| {
Activity::read_from_apub_id(&conn, &activity_id) Activity::read_from_apub_id(&conn, &activity_id)
}) })

View File

@ -45,7 +45,7 @@ pub(crate) async fn is_activity_already_known(
pool: &DbPool, pool: &DbPool,
activity_id: &Url, activity_id: &Url,
) -> Result<bool, LemmyError> { ) -> Result<bool, LemmyError> {
let activity_id = activity_id.to_string(); let activity_id = activity_id.to_owned().into();
let existing = blocking(pool, move |conn| { let existing = blocking(pool, move |conn| {
Activity::read_from_apub_id(&conn, &activity_id) Activity::read_from_apub_id(&conn, &activity_id)
}) })

View File

@ -120,9 +120,9 @@ pub(in crate::inbox) async fn receive_like_for_community(
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => receive_like_post(like, post, context, request_counter).await, PostOrComment::Post(post) => receive_like_post(like, *post, context, request_counter).await,
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_like_comment(like, comment, context, request_counter).await receive_like_comment(like, *comment, context, request_counter).await
} }
} }
} }
@ -152,10 +152,10 @@ pub(in crate::inbox) async fn receive_dislike_for_community(
.context(location_info!())?; .context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => { PostOrComment::Post(post) => {
receive_dislike_post(dislike, post, context, request_counter).await receive_dislike_post(dislike, *post, context, request_counter).await
} }
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_dislike_comment(dislike, comment, context, request_counter).await receive_dislike_comment(dislike, *comment, context, request_counter).await
} }
} }
} }
@ -177,8 +177,8 @@ pub(in crate::inbox) async fn receive_delete_for_community(
.context(location_info!())?; .context(location_info!())?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_delete_post(context, p).await, Ok(PostOrComment::Post(p)) => receive_delete_post(context, *p).await,
Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, c).await, Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -215,8 +215,8 @@ pub(in crate::inbox) async fn receive_remove_for_community(
remove.id(community_id.domain().context(location_info!())?)?; remove.id(community_id.domain().context(location_info!())?)?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, p).await, Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await,
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, c).await, Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -276,8 +276,8 @@ pub(in crate::inbox) async fn receive_undo_delete_for_community(
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_undo_delete_post(context, p).await, Ok(PostOrComment::Post(p)) => receive_undo_delete_post(context, *p).await,
Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, c).await, Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -300,8 +300,8 @@ pub(in crate::inbox) async fn receive_undo_remove_for_community(
.single_xsd_any_uri() .single_xsd_any_uri()
.context(location_info!())?; .context(location_info!())?;
match find_post_or_comment_by_id(context, object).await { match find_post_or_comment_by_id(context, object).await {
Ok(PostOrComment::Post(p)) => receive_undo_remove_post(context, p).await, Ok(PostOrComment::Post(p)) => receive_undo_remove_post(context, *p).await,
Ok(PostOrComment::Comment(c)) => receive_undo_remove_comment(context, c).await, Ok(PostOrComment::Comment(c)) => receive_undo_remove_comment(context, *c).await,
// if we dont have the object, no need to do anything // if we dont have the object, no need to do anything
Err(_) => Ok(()), Err(_) => Ok(()),
} }
@ -325,10 +325,10 @@ pub(in crate::inbox) async fn receive_undo_like_for_community(
.context(location_info!())?; .context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => { PostOrComment::Post(post) => {
receive_undo_like_post(&like, post, context, request_counter).await receive_undo_like_post(&like, *post, context, request_counter).await
} }
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_undo_like_comment(&like, comment, context, request_counter).await receive_undo_like_comment(&like, *comment, context, request_counter).await
} }
} }
} }
@ -351,10 +351,10 @@ pub(in crate::inbox) async fn receive_undo_dislike_for_community(
.context(location_info!())?; .context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? { match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => { PostOrComment::Post(post) => {
receive_undo_dislike_post(&dislike, post, context, request_counter).await receive_undo_dislike_post(&dislike, *post, context, request_counter).await
} }
PostOrComment::Comment(comment) => { PostOrComment::Comment(comment) => {
receive_undo_dislike_comment(&dislike, comment, context, request_counter).await receive_undo_dislike_comment(&dislike, *comment, context, request_counter).await
} }
} }
} }
@ -365,11 +365,11 @@ async fn fetch_post_or_comment_by_id(
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<PostOrComment, LemmyError> { ) -> Result<PostOrComment, LemmyError> {
if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await { if let Ok(post) = get_or_fetch_and_insert_post(apub_id, context, request_counter).await {
return Ok(PostOrComment::Post(post)); return Ok(PostOrComment::Post(Box::new(post)));
} }
if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await { if let Ok(comment) = get_or_fetch_and_insert_comment(apub_id, context, request_counter).await {
return Ok(PostOrComment::Comment(comment)); return Ok(PostOrComment::Comment(Box::new(comment)));
} }
Err(NotFound.into()) Err(NotFound.into())

View File

@ -26,13 +26,16 @@ use anyhow::{anyhow, Context};
use diesel::NotFound; use diesel::NotFound;
use lemmy_api_structs::blocking; use lemmy_api_structs::blocking;
use lemmy_db_queries::{source::activity::Activity_, ApubObject, DbPool}; use lemmy_db_queries::{source::activity::Activity_, ApubObject, DbPool};
use lemmy_db_schema::source::{ use lemmy_db_schema::{
activity::Activity, source::{
comment::Comment, activity::Activity,
community::Community, comment::Comment,
post::Post, community::Community,
private_message::PrivateMessage, post::Post,
user::User_, private_message::PrivateMessage,
user::User_,
},
DbUrl,
}; };
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError}; use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -216,7 +219,7 @@ pub enum EndpointType {
pub fn generate_apub_endpoint( pub fn generate_apub_endpoint(
endpoint_type: EndpointType, endpoint_type: EndpointType,
name: &str, name: &str,
) -> Result<lemmy_db_schema::Url, ParseError> { ) -> Result<DbUrl, ParseError> {
let point = match endpoint_type { let point = match endpoint_type {
EndpointType::Community => "c", EndpointType::Community => "c",
EndpointType::User => "u", EndpointType::User => "u",
@ -236,21 +239,15 @@ pub fn generate_apub_endpoint(
) )
} }
pub fn generate_followers_url( pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
actor_id: &lemmy_db_schema::Url,
) -> Result<lemmy_db_schema::Url, ParseError> {
Ok(Url::parse(&format!("{}/followers", actor_id))?.into()) Ok(Url::parse(&format!("{}/followers", actor_id))?.into())
} }
pub fn generate_inbox_url( pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
actor_id: &lemmy_db_schema::Url,
) -> Result<lemmy_db_schema::Url, ParseError> {
Ok(Url::parse(&format!("{}/inbox", actor_id))?.into()) Ok(Url::parse(&format!("{}/inbox", actor_id))?.into())
} }
pub fn generate_shared_inbox_url( pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
actor_id: &lemmy_db_schema::Url,
) -> Result<lemmy_db_schema::Url, LemmyError> {
let actor_id = actor_id.clone().into_inner(); let actor_id = actor_id.clone().into_inner();
let url = format!( let url = format!(
"{}://{}{}/inbox", "{}://{}{}/inbox",
@ -277,7 +274,7 @@ pub(crate) async fn insert_activity<T>(
where where
T: Serialize + std::fmt::Debug + Send + 'static, T: Serialize + std::fmt::Debug + Send + 'static,
{ {
let ap_id = ap_id.to_string(); let ap_id = ap_id.to_owned().into();
blocking(pool, move |conn| { blocking(pool, move |conn| {
Activity::insert(conn, ap_id, &activity, local, sensitive) Activity::insert(conn, ap_id, &activity, local, sensitive)
}) })
@ -286,8 +283,8 @@ where
} }
pub(crate) enum PostOrComment { pub(crate) enum PostOrComment {
Comment(Comment), Comment(Box<Comment>),
Post(Post), Post(Box<Post>),
} }
/// Tries to find a post or comment in the local database, without any network requests. /// Tries to find a post or comment in the local database, without any network requests.
@ -303,7 +300,7 @@ pub(crate) async fn find_post_or_comment_by_id(
}) })
.await?; .await?;
if let Ok(p) = post { if let Ok(p) = post {
return Ok(PostOrComment::Post(p)); return Ok(PostOrComment::Post(Box::new(p)));
} }
let ap_id = apub_id.clone(); let ap_id = apub_id.clone();
@ -312,7 +309,7 @@ pub(crate) async fn find_post_or_comment_by_id(
}) })
.await?; .await?;
if let Ok(c) = comment { if let Ok(c) = comment {
return Ok(PostOrComment::Comment(c)); return Ok(PostOrComment::Comment(Box::new(c)));
} }
Err(NotFound.into()) Err(NotFound.into())
@ -333,8 +330,8 @@ pub(crate) async fn find_object_by_id(
let ap_id = apub_id.clone(); let ap_id = apub_id.clone();
if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await { if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await {
return Ok(match pc { return Ok(match pc {
PostOrComment::Post(p) => Object::Post(p), PostOrComment::Post(p) => Object::Post(*p),
PostOrComment::Comment(c) => Object::Comment(c), PostOrComment::Comment(c) => Object::Comment(*c),
}); });
} }

View File

@ -73,13 +73,13 @@ impl ToApub for Community {
if let Some(icon_url) = &self.icon { if let Some(icon_url) = &self.icon {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(icon_url)?); image.set_url::<Url>(icon_url.to_owned().into());
group.set_icon(image.into_any_base()?); group.set_icon(image.into_any_base()?);
} }
if let Some(banner_url) = &self.banner { if let Some(banner_url) = &self.banner {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(banner_url)?); image.set_url::<Url>(banner_url.to_owned().into());
group.set_image(image.into_any_base()?); group.set_image(image.into_any_base()?);
} }
@ -173,7 +173,7 @@ impl FromApubToForm<GroupExt> for CommunityForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|u| u.to_owned().into()),
), ),
None => None, None => None,
}; };
@ -185,7 +185,7 @@ impl FromApubToForm<GroupExt> for CommunityForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|u| u.to_owned().into()),
), ),
None => None, None => None,
}; };

View File

@ -14,7 +14,7 @@ use chrono::NaiveDateTime;
use diesel::result::Error::NotFound; use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking; use lemmy_api_structs::blocking;
use lemmy_db_queries::{ApubObject, Crud, DbPool}; use lemmy_db_queries::{ApubObject, Crud, DbPool};
use lemmy_db_schema::source::community::Community; use lemmy_db_schema::{source::community::Community, DbUrl};
use lemmy_utils::{ use lemmy_utils::{
location_info, location_info,
settings::structs::Settings, settings::structs::Settings,
@ -96,7 +96,7 @@ where
pub(in crate::objects) fn check_object_domain<T, Kind>( pub(in crate::objects) fn check_object_domain<T, Kind>(
apub: &T, apub: &T,
expected_domain: Url, expected_domain: Url,
) -> Result<lemmy_db_schema::Url, LemmyError> ) -> Result<DbUrl, LemmyError>
where where
T: Base + AsBase<Kind>, T: Base + AsBase<Kind>,
{ {

View File

@ -24,10 +24,13 @@ use activitystreams_ext::Ext1;
use anyhow::Context; use anyhow::Context;
use lemmy_api_structs::blocking; use lemmy_api_structs::blocking;
use lemmy_db_queries::{Crud, DbPool}; use lemmy_db_queries::{Crud, DbPool};
use lemmy_db_schema::source::{ use lemmy_db_schema::{
community::Community, self,
post::{Post, PostForm}, source::{
user::User_, community::Community,
post::{Post, PostForm},
user::User_,
},
}; };
use lemmy_utils::{ use lemmy_utils::{
location_info, location_info,
@ -70,16 +73,13 @@ impl ToApub for Post {
set_content_and_source(&mut page, &body)?; set_content_and_source(&mut page, &body)?;
} }
// TODO: hacky code because we get self.url == Some("") if let Some(url) = &self.url {
// https://github.com/LemmyNet/lemmy/issues/602 page.set_url::<Url>(url.to_owned().into());
let url = self.url.as_ref().filter(|u| !u.is_empty());
if let Some(u) = url {
page.set_url(Url::parse(u)?);
} }
if let Some(thumbnail_url) = &self.thumbnail_url { if let Some(thumbnail_url) = &self.thumbnail_url {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(thumbnail_url)?); image.set_url::<Url>(thumbnail_url.to_owned().into());
page.set_image(image.into_any_base()?); page.set_image(image.into_any_base()?);
} }
@ -146,7 +146,7 @@ impl FromApubToForm<PageExt> for PostForm {
let community = get_to_community(page, context, request_counter).await?; let community = get_to_community(page, context, request_counter).await?;
let thumbnail_url = match &page.inner.image() { let thumbnail_url: Option<Url> = match &page.inner.image() {
Some(any_image) => Image::from_any_base( Some(any_image) => Image::from_any_base(
any_image any_image
.to_owned() .to_owned()
@ -158,7 +158,7 @@ impl FromApubToForm<PageExt> for PostForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|url| url.to_owned()),
None => None, None => None,
}; };
let url = page let url = page
@ -166,11 +166,11 @@ impl FromApubToForm<PageExt> for PostForm {
.url() .url()
.map(|u| u.as_single_xsd_any_uri()) .map(|u| u.as_single_xsd_any_uri())
.flatten() .flatten()
.map(|s| s.to_string()); .map(|u| u.to_owned());
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) = let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
if let Some(url) = &url { if let Some(url) = &url {
fetch_iframely_and_pictrs_data(context.client(), Some(url.to_owned())).await fetch_iframely_and_pictrs_data(context.client(), Some(url)).await
} else { } else {
(None, None, None, thumbnail_url) (None, None, None, thumbnail_url)
}; };
@ -192,7 +192,7 @@ impl FromApubToForm<PageExt> for PostForm {
let body_slurs_removed = body.map(|b| remove_slurs(&b)); let body_slurs_removed = body.map(|b| remove_slurs(&b));
Ok(PostForm { Ok(PostForm {
name, name,
url, url: url.map(|u| u.into()),
body: body_slurs_removed, body: body_slurs_removed,
creator_id: creator.id, creator_id: creator.id,
community_id: community.id, community_id: community.id,
@ -214,7 +214,7 @@ impl FromApubToForm<PageExt> for PostForm {
embed_title: iframely_title, embed_title: iframely_title,
embed_description: iframely_description, embed_description: iframely_description,
embed_html: iframely_html, embed_html: iframely_html,
thumbnail_url: pictrs_thumbnail, thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: Some(check_object_domain(page, expected_domain)?), ap_id: Some(check_object_domain(page, expected_domain)?),
local: false, local: false,
}) })

View File

@ -50,13 +50,13 @@ impl ToApub for User_ {
if let Some(avatar_url) = &self.avatar { if let Some(avatar_url) = &self.avatar {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(avatar_url)?); image.set_url::<Url>(avatar_url.to_owned().into());
person.set_icon(image.into_any_base()?); person.set_icon(image.into_any_base()?);
} }
if let Some(banner_url) = &self.banner { if let Some(banner_url) = &self.banner {
let mut image = Image::new(); let mut image = Image::new();
image.set_url(Url::parse(banner_url)?); image.set_url::<Url>(banner_url.to_owned().into());
person.set_image(image.into_any_base()?); person.set_image(image.into_any_base()?);
} }
@ -126,7 +126,7 @@ impl FromApubToForm<PersonExt> for UserForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|url| url.to_owned()),
), ),
None => None, None => None,
}; };
@ -139,7 +139,7 @@ impl FromApubToForm<PersonExt> for UserForm {
.url() .url()
.context(location_info!())? .context(location_info!())?
.as_single_xsd_any_uri() .as_single_xsd_any_uri()
.map(|u| u.to_string()), .map(|url| url.to_owned()),
), ),
None => None, None => None,
}; };
@ -174,8 +174,8 @@ impl FromApubToForm<PersonExt> for UserForm {
admin: false, admin: false,
banned: None, banned: None,
email: None, email: None,
avatar, avatar: avatar.map(|o| o.map(|i| i.into())),
banner, banner: banner.map(|o| o.map(|i| i.into())),
published: person.inner.published().map(|u| u.to_owned().naive_local()), published: person.inner.published().map(|u| u.to_owned().naive_local()),
updated: person.updated().map(|u| u.to_owned().naive_local()), updated: person.updated().map(|u| u.to_owned().naive_local()),
show_nsfw: false, show_nsfw: false,

View File

@ -20,7 +20,7 @@ strum = "0.20.0"
strum_macros = "0.20.1" strum_macros = "0.20.1"
log = "0.4.14" log = "0.4.14"
sha2 = "0.9.3" sha2 = "0.9.3"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
lazy_static = "1.4.0" lazy_static = "1.4.0"
regex = "1.4.3" regex = "1.4.3"
bcrypt = "0.9.0" bcrypt = "0.9.0"

View File

@ -13,10 +13,12 @@ extern crate diesel_migrations;
extern crate serial_test; extern crate serial_test;
use diesel::{result::Error, *}; use diesel::{result::Error, *};
use lemmy_db_schema::Url; use lemmy_db_schema::DbUrl;
use lemmy_utils::ApiError;
use regex::Regex; use regex::Regex;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{env, env::VarError}; use std::{env, env::VarError};
use url::Url;
pub mod aggregates; pub mod aggregates;
pub mod source; pub mod source;
@ -112,7 +114,7 @@ pub trait Reportable<T> {
} }
pub trait ApubObject<T> { pub trait ApubObject<T> {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error>
where where
Self: Sized; Self: Sized;
fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error> fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error>
@ -219,6 +221,20 @@ pub fn diesel_option_overwrite(opt: &Option<String>) -> Option<Option<String>> {
} }
} }
pub fn diesel_option_overwrite_to_url(
opt: &Option<String>,
) -> Result<Option<Option<DbUrl>>, ApiError> {
match opt.as_ref().map(|s| s.as_str()) {
// An empty string is an erase
Some("") => Ok(Some(None)),
Some(str_url) => match Url::parse(str_url) {
Ok(url) => Ok(Some(Some(url.into()))),
Err(_) => Err(ApiError::err("invalid_url")),
},
None => Ok(None),
}
}
embed_migrations!(); embed_migrations!();
pub fn establish_unpooled_connection() -> PgConnection { pub fn establish_unpooled_connection() -> PgConnection {
@ -251,7 +267,7 @@ pub mod functions {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::fuzzy_search; use super::{fuzzy_search, *};
use crate::is_email_regex; use crate::is_email_regex;
#[test] #[test]
@ -265,4 +281,32 @@ mod tests {
assert!(is_email_regex("gush@gmail.com")); assert!(is_email_regex("gush@gmail.com"));
assert!(!is_email_regex("nada_neutho")); assert!(!is_email_regex("nada_neutho"));
} }
#[test]
fn test_diesel_option_overwrite() {
assert_eq!(diesel_option_overwrite(&None), None);
assert_eq!(diesel_option_overwrite(&Some("".to_string())), Some(None));
assert_eq!(
diesel_option_overwrite(&Some("test".to_string())),
Some(Some("test".to_string()))
);
}
#[test]
fn test_diesel_option_overwrite_to_url() {
assert!(matches!(diesel_option_overwrite_to_url(&None), Ok(None)));
assert!(matches!(
diesel_option_overwrite_to_url(&Some("".to_string())),
Ok(Some(None))
));
assert!(matches!(
diesel_option_overwrite_to_url(&Some("invalid_url".to_string())),
Err(_)
));
let example_url = "https://example.com";
assert!(matches!(
diesel_option_overwrite_to_url(&Some(example_url.to_string())),
Ok(Some(Some(url))) if url == Url::parse(&example_url).unwrap().into()
));
}
} }

View File

@ -1,6 +1,6 @@
use crate::Crud; use crate::Crud;
use diesel::{dsl::*, result::Error, sql_types::Text, *}; use diesel::{dsl::*, result::Error, sql_types::Text, *};
use lemmy_db_schema::{source::activity::*, Url}; use lemmy_db_schema::{source::activity::*, DbUrl};
use log::debug; use log::debug;
use serde::Serialize; use serde::Serialize;
use serde_json::Value; use serde_json::Value;
@ -41,7 +41,7 @@ impl Crud<ActivityForm> for Activity {
pub trait Activity_ { pub trait Activity_ {
fn insert<T>( fn insert<T>(
conn: &PgConnection, conn: &PgConnection,
ap_id: String, ap_id: DbUrl,
data: &T, data: &T,
local: bool, local: bool,
sensitive: bool, sensitive: bool,
@ -49,20 +49,20 @@ pub trait Activity_ {
where where
T: Serialize + Debug; T: Serialize + Debug;
fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Activity, Error>; fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error>;
fn delete_olds(conn: &PgConnection) -> Result<usize, Error>; fn delete_olds(conn: &PgConnection) -> Result<usize, Error>;
/// Returns up to 20 activities of type `Announce/Create/Page` from the community /// Returns up to 20 activities of type `Announce/Create/Page` from the community
fn read_community_outbox( fn read_community_outbox(
conn: &PgConnection, conn: &PgConnection,
community_actor_id: &Url, community_actor_id: &DbUrl,
) -> Result<Vec<Value>, Error>; ) -> Result<Vec<Value>, Error>;
} }
impl Activity_ for Activity { impl Activity_ for Activity {
fn insert<T>( fn insert<T>(
conn: &PgConnection, conn: &PgConnection,
ap_id: String, ap_id: DbUrl,
data: &T, data: &T,
local: bool, local: bool,
sensitive: bool, sensitive: bool,
@ -88,7 +88,7 @@ impl Activity_ for Activity {
} }
} }
fn read_from_apub_id(conn: &PgConnection, object_id: &str) -> Result<Activity, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Activity, Error> {
use lemmy_db_schema::schema::activity::dsl::*; use lemmy_db_schema::schema::activity::dsl::*;
activity.filter(ap_id.eq(object_id)).first::<Self>(conn) activity.filter(ap_id.eq(object_id)).first::<Self>(conn)
} }
@ -100,7 +100,7 @@ impl Activity_ for Activity {
fn read_community_outbox( fn read_community_outbox(
conn: &PgConnection, conn: &PgConnection,
community_actor_id: &Url, community_actor_id: &DbUrl,
) -> Result<Vec<Value>, Error> { ) -> Result<Vec<Value>, Error> {
use lemmy_db_schema::schema::activity::dsl::*; use lemmy_db_schema::schema::activity::dsl::*;
let res: Vec<Value> = activity let res: Vec<Value> = activity
@ -121,6 +121,7 @@ impl Activity_ for Activity {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*;
use crate::{ use crate::{
establish_unpooled_connection, establish_unpooled_connection,
source::activity::Activity_, source::activity::Activity_,
@ -134,6 +135,7 @@ mod tests {
}; };
use serde_json::Value; use serde_json::Value;
use serial_test::serial; use serial_test::serial;
use url::Url;
#[test] #[test]
#[serial] #[serial]
@ -171,8 +173,11 @@ mod tests {
let inserted_creator = User_::create(&conn, &creator_form).unwrap(); let inserted_creator = User_::create(&conn, &creator_form).unwrap();
let ap_id = let ap_id: DbUrl = Url::parse(
"https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c"; "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
)
.unwrap()
.into();
let test_json: Value = serde_json::from_str( let test_json: Value = serde_json::from_str(
r#"{ r#"{
"@context": "https://www.w3.org/ns/activitystreams", "@context": "https://www.w3.org/ns/activitystreams",
@ -188,7 +193,7 @@ mod tests {
) )
.unwrap(); .unwrap();
let activity_form = ActivityForm { let activity_form = ActivityForm {
ap_id: ap_id.to_string(), ap_id: ap_id.clone(),
data: test_json.to_owned(), data: test_json.to_owned(),
local: true, local: true,
sensitive: false, sensitive: false,
@ -198,7 +203,7 @@ mod tests {
let inserted_activity = Activity::create(&conn, &activity_form).unwrap(); let inserted_activity = Activity::create(&conn, &activity_form).unwrap();
let expected_activity = Activity { let expected_activity = Activity {
ap_id: Some(ap_id.to_string()), ap_id: Some(ap_id.clone()),
id: inserted_activity.id, id: inserted_activity.id,
data: test_json, data: test_json,
local: true, local: true,
@ -208,7 +213,7 @@ mod tests {
}; };
let read_activity = Activity::read(&conn, inserted_activity.id).unwrap(); let read_activity = Activity::read(&conn, inserted_activity.id).unwrap();
let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, ap_id).unwrap(); let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, &ap_id).unwrap();
User_::delete(&conn, inserted_creator.id).unwrap(); User_::delete(&conn, inserted_creator.id).unwrap();
Activity::delete(&conn, inserted_activity.id).unwrap(); Activity::delete(&conn, inserted_activity.id).unwrap();

View File

@ -10,11 +10,11 @@ use lemmy_db_schema::{
CommentSaved, CommentSaved,
CommentSavedForm, CommentSavedForm,
}, },
Url, DbUrl,
}; };
pub trait Comment_ { pub trait Comment_ {
fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: Url) -> Result<Comment, Error>; fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: DbUrl) -> Result<Comment, Error>;
fn permadelete_for_creator( fn permadelete_for_creator(
conn: &PgConnection, conn: &PgConnection,
for_creator_id: i32, for_creator_id: i32,
@ -43,7 +43,7 @@ pub trait Comment_ {
} }
impl Comment_ for Comment { impl Comment_ for Comment {
fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: Url) -> Result<Self, Error> { fn update_ap_id(conn: &PgConnection, comment_id: i32, apub_id: DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::comment::dsl::*; use lemmy_db_schema::schema::comment::dsl::*;
diesel::update(comment.find(comment_id)) diesel::update(comment.find(comment_id))
@ -145,7 +145,7 @@ impl Crud<CommentForm> for Comment {
} }
impl ApubObject<CommentForm> for Comment { impl ApubObject<CommentForm> for Comment {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::comment::dsl::*; use lemmy_db_schema::schema::comment::dsl::*;
comment.filter(ap_id.eq(object_id)).first::<Self>(conn) comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
} }

View File

@ -12,7 +12,7 @@ use lemmy_db_schema::{
CommunityUserBan, CommunityUserBan,
CommunityUserBanForm, CommunityUserBanForm,
}, },
Url, DbUrl,
}; };
mod safe_type { mod safe_type {
@ -90,7 +90,7 @@ impl Crud<CommunityForm> for Community {
} }
impl ApubObject<CommunityForm> for Community { impl ApubObject<CommunityForm> for Community {
fn read_from_apub_id(conn: &PgConnection, for_actor_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, for_actor_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::community::dsl::*; use lemmy_db_schema::schema::community::dsl::*;
community community
.filter(actor_id.eq(for_actor_id)) .filter(actor_id.eq(for_actor_id))
@ -131,7 +131,10 @@ pub trait Community_ {
new_creator_id: i32, new_creator_id: i32,
) -> Result<Community, Error>; ) -> Result<Community, Error>;
fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>; fn distinct_federated_communities(conn: &PgConnection) -> Result<Vec<String>, Error>;
fn read_from_followers_url(conn: &PgConnection, followers_url: &Url) -> Result<Community, Error>; fn read_from_followers_url(
conn: &PgConnection,
followers_url: &DbUrl,
) -> Result<Community, Error>;
} }
impl Community_ for Community { impl Community_ for Community {
@ -194,7 +197,7 @@ impl Community_ for Community {
fn read_from_followers_url( fn read_from_followers_url(
conn: &PgConnection, conn: &PgConnection,
followers_url_: &Url, followers_url_: &DbUrl,
) -> Result<Community, Error> { ) -> Result<Community, Error> {
use lemmy_db_schema::schema::community::dsl::*; use lemmy_db_schema::schema::community::dsl::*;
community community

View File

@ -12,7 +12,7 @@ use lemmy_db_schema::{
PostSaved, PostSaved,
PostSavedForm, PostSavedForm,
}, },
Url, DbUrl,
}; };
impl Crud<PostForm> for Post { impl Crud<PostForm> for Post {
@ -42,7 +42,7 @@ impl Crud<PostForm> for Post {
pub trait Post_ { pub trait Post_ {
//fn read(conn: &PgConnection, post_id: i32) -> Result<Post, Error>; //fn read(conn: &PgConnection, post_id: i32) -> Result<Post, Error>;
fn list_for_community(conn: &PgConnection, the_community_id: i32) -> Result<Vec<Post>, Error>; fn list_for_community(conn: &PgConnection, the_community_id: i32) -> Result<Vec<Post>, Error>;
fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: Url) -> Result<Post, Error>; fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: DbUrl) -> Result<Post, Error>;
fn permadelete_for_creator(conn: &PgConnection, for_creator_id: i32) -> Result<Vec<Post>, Error>; fn permadelete_for_creator(conn: &PgConnection, for_creator_id: i32) -> Result<Vec<Post>, Error>;
fn update_deleted(conn: &PgConnection, post_id: i32, new_deleted: bool) -> Result<Post, Error>; fn update_deleted(conn: &PgConnection, post_id: i32, new_deleted: bool) -> Result<Post, Error>;
fn update_removed(conn: &PgConnection, post_id: i32, new_removed: bool) -> Result<Post, Error>; fn update_removed(conn: &PgConnection, post_id: i32, new_removed: bool) -> Result<Post, Error>;
@ -68,7 +68,7 @@ impl Post_ for Post {
.load::<Self>(conn) .load::<Self>(conn)
} }
fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: Url) -> Result<Self, Error> { fn update_ap_id(conn: &PgConnection, post_id: i32, apub_id: DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::post::dsl::*; use lemmy_db_schema::schema::post::dsl::*;
diesel::update(post.find(post_id)) diesel::update(post.find(post_id))
@ -147,7 +147,7 @@ impl Post_ for Post {
} }
impl ApubObject<PostForm> for Post { impl ApubObject<PostForm> for Post {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::post::dsl::*; use lemmy_db_schema::schema::post::dsl::*;
post.filter(ap_id.eq(object_id)).first::<Self>(conn) post.filter(ap_id.eq(object_id)).first::<Self>(conn)
} }

View File

@ -1,6 +1,6 @@
use crate::{ApubObject, Crud}; use crate::{ApubObject, Crud};
use diesel::{dsl::*, result::Error, *}; use diesel::{dsl::*, result::Error, *};
use lemmy_db_schema::{naive_now, source::private_message::*, Url}; use lemmy_db_schema::{naive_now, source::private_message::*, DbUrl};
impl Crud<PrivateMessageForm> for PrivateMessage { impl Crud<PrivateMessageForm> for PrivateMessage {
fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> { fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> {
@ -28,7 +28,7 @@ impl Crud<PrivateMessageForm> for PrivateMessage {
} }
impl ApubObject<PrivateMessageForm> for PrivateMessage { impl ApubObject<PrivateMessageForm> for PrivateMessage {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error>
where where
Self: Sized, Self: Sized,
{ {
@ -53,7 +53,7 @@ pub trait PrivateMessage_ {
fn update_ap_id( fn update_ap_id(
conn: &PgConnection, conn: &PgConnection,
private_message_id: i32, private_message_id: i32,
apub_id: Url, apub_id: DbUrl,
) -> Result<PrivateMessage, Error>; ) -> Result<PrivateMessage, Error>;
fn update_content( fn update_content(
conn: &PgConnection, conn: &PgConnection,
@ -80,7 +80,7 @@ impl PrivateMessage_ for PrivateMessage {
fn update_ap_id( fn update_ap_id(
conn: &PgConnection, conn: &PgConnection,
private_message_id: i32, private_message_id: i32,
apub_id: Url, apub_id: DbUrl,
) -> Result<PrivateMessage, Error> { ) -> Result<PrivateMessage, Error> {
use lemmy_db_schema::schema::private_message::dsl::*; use lemmy_db_schema::schema::private_message::dsl::*;

View File

@ -5,7 +5,7 @@ use lemmy_db_schema::{
naive_now, naive_now,
schema::user_::dsl::*, schema::user_::dsl::*,
source::user::{UserForm, UserSafeSettings, User_}, source::user::{UserForm, UserSafeSettings, User_},
Url, DbUrl,
}; };
use lemmy_utils::settings::structs::Settings; use lemmy_utils::settings::structs::Settings;
@ -242,7 +242,7 @@ impl Crud<UserForm> for User_ {
} }
impl ApubObject<UserForm> for User_ { impl ApubObject<UserForm> for User_ {
fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> { fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
use lemmy_db_schema::schema::user_::dsl::*; use lemmy_db_schema::schema::user_::dsl::*;
user_ user_
.filter(deleted.eq(false)) .filter(deleted.eq(false))

View File

@ -12,4 +12,4 @@ chrono = { version = "0.4.19", features = ["serde"] }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
serde_json = { version = "1.0.61", features = ["preserve_order"] } serde_json = { version = "1.0.61", features = ["preserve_order"] }
log = "0.4.14" log = "0.4.14"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }

View File

@ -8,21 +8,22 @@ use diesel::{
serialize::{Output, ToSql}, serialize::{Output, ToSql},
sql_types::Text, sql_types::Text,
}; };
use serde::Serialize; use serde::{Deserialize, Serialize};
use std::{ use std::{
fmt::{Display, Formatter}, fmt::{Display, Formatter},
io::Write, io::Write,
}; };
use url::Url;
pub mod schema; pub mod schema;
pub mod source; pub mod source;
#[repr(transparent)] #[repr(transparent)]
#[derive(Clone, PartialEq, Serialize, Debug, AsExpression, FromSqlRow)] #[derive(Clone, PartialEq, Serialize, Deserialize, Debug, AsExpression, FromSqlRow)]
#[sql_type = "Text"] #[sql_type = "Text"]
pub struct Url(url::Url); pub struct DbUrl(Url);
impl<DB: Backend> ToSql<Text, DB> for Url impl<DB: Backend> ToSql<Text, DB> for DbUrl
where where
String: ToSql<Text, DB>, String: ToSql<Text, DB>,
{ {
@ -31,37 +32,37 @@ where
} }
} }
impl<DB: Backend> FromSql<Text, DB> for Url impl<DB: Backend> FromSql<Text, DB> for DbUrl
where where
String: FromSql<Text, DB>, String: FromSql<Text, DB>,
{ {
fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result<Self> { fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result<Self> {
let str = String::from_sql(bytes)?; let str = String::from_sql(bytes)?;
Ok(Url(url::Url::parse(&str)?)) Ok(DbUrl(Url::parse(&str)?))
} }
} }
impl Url { impl DbUrl {
pub fn into_inner(self) -> url::Url { pub fn into_inner(self) -> Url {
self.0 self.0
} }
} }
impl Display for Url { impl Display for DbUrl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.to_owned().into_inner().fmt(f) self.to_owned().into_inner().fmt(f)
} }
} }
impl From<Url> for url::Url { impl From<DbUrl> for Url {
fn from(url: Url) -> Self { fn from(url: DbUrl) -> Self {
url.0 url.0
} }
} }
impl From<url::Url> for Url { impl From<Url> for DbUrl {
fn from(url: url::Url) -> Self { fn from(url: Url) -> Self {
Url(url) DbUrl(url)
} }
} }

View File

@ -1,4 +1,4 @@
use crate::schema::activity; use crate::{schema::activity, DbUrl};
use serde_json::Value; use serde_json::Value;
use std::fmt::Debug; use std::fmt::Debug;
@ -10,7 +10,7 @@ pub struct Activity {
pub local: bool, pub local: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: Option<String>, pub ap_id: Option<DbUrl>,
pub sensitive: Option<bool>, pub sensitive: Option<bool>,
} }
@ -20,6 +20,6 @@ pub struct ActivityForm {
pub data: Value, pub data: Value,
pub local: bool, pub local: bool,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: String, pub ap_id: DbUrl,
pub sensitive: bool, pub sensitive: bool,
} }

View File

@ -1,7 +1,7 @@
use crate::{ use crate::{
schema::{comment, comment_alias_1, comment_like, comment_saved}, schema::{comment, comment_alias_1, comment_like, comment_saved},
source::post::Post, source::post::Post,
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -26,7 +26,7 @@ pub struct Comment {
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -44,7 +44,7 @@ pub struct CommentAlias1 {
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -60,7 +60,7 @@ pub struct CommentForm {
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>, pub deleted: Option<bool>,
pub ap_id: Option<Url>, pub ap_id: Option<DbUrl>,
pub local: bool, pub local: bool,
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
schema::{community, community_follower, community_moderator, community_user_ban}, schema::{community, community_follower, community_moderator, community_user_ban},
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -17,16 +17,16 @@ pub struct Community {
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub nsfw: bool, pub nsfw: bool,
pub actor_id: Url, pub actor_id: DbUrl,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub icon: Option<String>, pub icon: Option<DbUrl>,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub followers_url: Url, pub followers_url: DbUrl,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
/// A safe representation of community, without the sensitive info /// A safe representation of community, without the sensitive info
@ -43,10 +43,10 @@ pub struct CommunitySafe {
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool, pub deleted: bool,
pub nsfw: bool, pub nsfw: bool,
pub actor_id: Url, pub actor_id: DbUrl,
pub local: bool, pub local: bool,
pub icon: Option<String>, pub icon: Option<DbUrl>,
pub banner: Option<String>, pub banner: Option<DbUrl>,
} }
#[derive(Insertable, AsChangeset, Debug)] #[derive(Insertable, AsChangeset, Debug)]
@ -61,16 +61,16 @@ pub struct CommunityForm {
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>, pub deleted: Option<bool>,
pub nsfw: bool, pub nsfw: bool,
pub actor_id: Option<Url>, pub actor_id: Option<DbUrl>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: Option<chrono::NaiveDateTime>, pub last_refreshed_at: Option<chrono::NaiveDateTime>,
pub icon: Option<Option<String>>, pub icon: Option<Option<DbUrl>>,
pub banner: Option<Option<String>>, pub banner: Option<Option<DbUrl>>,
pub followers_url: Option<Url>, pub followers_url: Option<DbUrl>,
pub inbox_url: Option<Url>, pub inbox_url: Option<DbUrl>,
pub shared_inbox_url: Option<Option<Url>>, pub shared_inbox_url: Option<Option<DbUrl>>,
} }
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
schema::{post, post_like, post_read, post_saved}, schema::{post, post_like, post_read, post_saved},
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -9,7 +9,7 @@ use serde::Serialize;
pub struct Post { pub struct Post {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<DbUrl>,
pub body: Option<String>, pub body: Option<String>,
pub creator_id: i32, pub creator_id: i32,
pub community_id: i32, pub community_id: i32,
@ -23,8 +23,8 @@ pub struct Post {
pub embed_title: Option<String>, pub embed_title: Option<String>,
pub embed_description: Option<String>, pub embed_description: Option<String>,
pub embed_html: Option<String>, pub embed_html: Option<String>,
pub thumbnail_url: Option<String>, pub thumbnail_url: Option<DbUrl>,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -32,7 +32,7 @@ pub struct Post {
#[table_name = "post"] #[table_name = "post"]
pub struct PostForm { pub struct PostForm {
pub name: String, pub name: String,
pub url: Option<String>, pub url: Option<DbUrl>,
pub body: Option<String>, pub body: Option<String>,
pub creator_id: i32, pub creator_id: i32,
pub community_id: i32, pub community_id: i32,
@ -46,8 +46,8 @@ pub struct PostForm {
pub embed_title: Option<String>, pub embed_title: Option<String>,
pub embed_description: Option<String>, pub embed_description: Option<String>,
pub embed_html: Option<String>, pub embed_html: Option<String>,
pub thumbnail_url: Option<String>, pub thumbnail_url: Option<DbUrl>,
pub ap_id: Option<Url>, pub ap_id: Option<DbUrl>,
pub local: bool, pub local: bool,
} }

View File

@ -1,4 +1,4 @@
use crate::{schema::post_report, source::post::Post}; use crate::{schema::post_report, source::post::Post, DbUrl};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive( #[derive(
@ -11,7 +11,7 @@ pub struct PostReport {
pub creator_id: i32, pub creator_id: i32,
pub post_id: i32, pub post_id: i32,
pub original_post_name: String, pub original_post_name: String,
pub original_post_url: Option<String>, pub original_post_url: Option<DbUrl>,
pub original_post_body: Option<String>, pub original_post_body: Option<String>,
pub reason: String, pub reason: String,
pub resolved: bool, pub resolved: bool,
@ -26,7 +26,7 @@ pub struct PostReportForm {
pub creator_id: i32, pub creator_id: i32,
pub post_id: i32, pub post_id: i32,
pub original_post_name: String, pub original_post_name: String,
pub original_post_url: Option<String>, pub original_post_url: Option<DbUrl>,
pub original_post_body: Option<String>, pub original_post_body: Option<String>,
pub reason: String, pub reason: String,
} }

View File

@ -1,4 +1,4 @@
use crate::{schema::private_message, Url}; use crate::{schema::private_message, DbUrl};
use serde::Serialize; use serde::Serialize;
#[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)] #[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)]
@ -12,7 +12,7 @@ pub struct PrivateMessage {
pub read: bool, pub read: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: Url, pub ap_id: DbUrl,
pub local: bool, pub local: bool,
} }
@ -26,6 +26,6 @@ pub struct PrivateMessageForm {
pub read: Option<bool>, pub read: Option<bool>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub ap_id: Option<Url>, pub ap_id: Option<DbUrl>,
pub local: bool, pub local: bool,
} }

View File

@ -1,4 +1,4 @@
use crate::schema::site; use crate::{schema::site, DbUrl};
use serde::Serialize; use serde::Serialize;
#[derive(Queryable, Identifiable, PartialEq, Debug, Clone, Serialize)] #[derive(Queryable, Identifiable, PartialEq, Debug, Clone, Serialize)]
@ -13,8 +13,8 @@ pub struct Site {
pub enable_downvotes: bool, pub enable_downvotes: bool,
pub open_registration: bool, pub open_registration: bool,
pub enable_nsfw: bool, pub enable_nsfw: bool,
pub icon: Option<String>, pub icon: Option<DbUrl>,
pub banner: Option<String>, pub banner: Option<DbUrl>,
} }
#[derive(Insertable, AsChangeset)] #[derive(Insertable, AsChangeset)]
@ -28,6 +28,6 @@ pub struct SiteForm {
pub open_registration: bool, pub open_registration: bool,
pub enable_nsfw: bool, pub enable_nsfw: bool,
// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column. // when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
pub icon: Option<Option<String>>, pub icon: Option<Option<DbUrl>>,
pub banner: Option<Option<String>>, pub banner: Option<Option<DbUrl>>,
} }

View File

@ -1,6 +1,6 @@
use crate::{ use crate::{
schema::{user_, user_alias_1, user_alias_2}, schema::{user_, user_alias_1, user_alias_2},
Url, DbUrl,
}; };
use serde::Serialize; use serde::Serialize;
@ -12,7 +12,7 @@ pub struct User_ {
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub password_encrypted: String, pub password_encrypted: String,
pub email: Option<String>, pub email: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -25,16 +25,16 @@ pub struct User_ {
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
/// A safe representation of user, without the sensitive info /// A safe representation of user, without the sensitive info
@ -44,19 +44,19 @@ pub struct UserSafe {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
pub inbox_url: Url, pub inbox_url: DbUrl,
pub shared_inbox_url: Option<Url>, pub shared_inbox_url: Option<DbUrl>,
} }
/// A safe user view with only settings /// A safe user view with only settings
@ -67,7 +67,7 @@ pub struct UserSafeSettings {
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub email: Option<String>, pub email: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -80,11 +80,11 @@ pub struct UserSafeSettings {
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
} }
@ -96,7 +96,7 @@ pub struct UserAlias1 {
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub password_encrypted: String, pub password_encrypted: String,
pub email: Option<String>, pub email: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -109,13 +109,13 @@ pub struct UserAlias1 {
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
} }
@ -125,16 +125,16 @@ pub struct UserSafeAlias1 {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
} }
@ -146,7 +146,7 @@ pub struct UserAlias2 {
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub password_encrypted: String, pub password_encrypted: String,
pub email: Option<String>, pub email: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
@ -159,13 +159,13 @@ pub struct UserAlias2 {
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime, pub last_refreshed_at: chrono::NaiveDateTime,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
} }
@ -175,16 +175,16 @@ pub struct UserSafeAlias2 {
pub id: i32, pub id: i32,
pub name: String, pub name: String,
pub preferred_username: Option<String>, pub preferred_username: Option<String>,
pub avatar: Option<String>, pub avatar: Option<DbUrl>,
pub admin: bool, pub admin: bool,
pub banned: bool, pub banned: bool,
pub published: chrono::NaiveDateTime, pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>, pub matrix_user_id: Option<String>,
pub actor_id: Url, pub actor_id: DbUrl,
pub bio: Option<String>, pub bio: Option<String>,
pub local: bool, pub local: bool,
pub banner: Option<String>, pub banner: Option<DbUrl>,
pub deleted: bool, pub deleted: bool,
} }
@ -197,7 +197,7 @@ pub struct UserForm {
pub admin: bool, pub admin: bool,
pub banned: Option<bool>, pub banned: Option<bool>,
pub email: Option<Option<String>>, pub email: Option<Option<String>>,
pub avatar: Option<Option<String>>, pub avatar: Option<Option<DbUrl>>,
pub published: Option<chrono::NaiveDateTime>, pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>, pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool, pub show_nsfw: bool,
@ -208,13 +208,13 @@ pub struct UserForm {
pub show_avatars: bool, pub show_avatars: bool,
pub send_notifications_to_email: bool, pub send_notifications_to_email: bool,
pub matrix_user_id: Option<Option<String>>, pub matrix_user_id: Option<Option<String>>,
pub actor_id: Option<Url>, pub actor_id: Option<DbUrl>,
pub bio: Option<Option<String>>, pub bio: Option<Option<String>>,
pub local: bool, pub local: bool,
pub private_key: Option<String>, pub private_key: Option<String>,
pub public_key: Option<String>, pub public_key: Option<String>,
pub last_refreshed_at: Option<chrono::NaiveDateTime>, pub last_refreshed_at: Option<chrono::NaiveDateTime>,
pub banner: Option<Option<String>>, pub banner: Option<Option<DbUrl>>,
pub inbox_url: Option<Url>, pub inbox_url: Option<DbUrl>,
pub shared_inbox_url: Option<Option<Url>>, pub shared_inbox_url: Option<Option<DbUrl>>,
} }

View File

@ -12,7 +12,7 @@ lemmy_db_schema = { path = "../db_schema" }
diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] } diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] }
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
log = "0.4.14" log = "0.4.14"
url = "2.2.0" url = "2.2.1"
[dev-dependencies] [dev-dependencies]
serial_test = "0.5.1" serial_test = "0.5.1"

View File

@ -25,6 +25,6 @@ chrono = { version = "0.4.19", features = ["serde"] }
rss = "1.10.0" rss = "1.10.0"
serde = { version = "1.0.123", features = ["derive"] } serde = { version = "1.0.123", features = ["derive"] }
awc = { version = "2.0.3", default-features = false } awc = { version = "2.0.3", default-features = false }
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
strum = "0.20.0" strum = "0.20.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"

View File

@ -22,7 +22,7 @@ thiserror = "1.0.23"
comrak = { version = "0.9.0", default-features = false } comrak = { version = "0.9.0", default-features = false }
lazy_static = "1.4.0" lazy_static = "1.4.0"
openssl = "0.10.32" openssl = "0.10.32"
url = { version = "2.2.0", features = ["serde"] } url = { version = "2.2.1", features = ["serde"] }
actix-web = { version = "3.3.2", default-features = false, features = ["rustls"] } actix-web = { version = "3.3.2", default-features = false, features = ["rustls"] }
actix-rt = { version = "1.1.1", default-features = false } actix-rt = { version = "1.1.1", default-features = false }
anyhow = "1.0.38" anyhow = "1.0.38"

View File

@ -6,6 +6,7 @@ use reqwest::Client;
use serde::Deserialize; use serde::Deserialize;
use std::future::Future; use std::future::Future;
use thiserror::Error; use thiserror::Error;
use url::Url;
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
#[error("Error sending request, {0}")] #[error("Error sending request, {0}")]
@ -50,13 +51,13 @@ where
pub(crate) struct IframelyResponse { pub(crate) struct IframelyResponse {
title: Option<String>, title: Option<String>,
description: Option<String>, description: Option<String>,
thumbnail_url: Option<String>, thumbnail_url: Option<Url>,
html: Option<String>, html: Option<String>,
} }
pub(crate) async fn fetch_iframely( pub(crate) async fn fetch_iframely(
client: &Client, client: &Client,
url: &str, url: &Url,
) -> Result<IframelyResponse, LemmyError> { ) -> Result<IframelyResponse, LemmyError> {
let fetch_url = format!("{}/oembed?url={}", Settings::get().iframely_url(), url); let fetch_url = format!("{}/oembed?url={}", Settings::get().iframely_url(), url);
@ -83,14 +84,14 @@ pub(crate) struct PictrsFile {
pub(crate) async fn fetch_pictrs( pub(crate) async fn fetch_pictrs(
client: &Client, client: &Client,
image_url: &str, image_url: &Url,
) -> Result<PictrsResponse, LemmyError> { ) -> Result<PictrsResponse, LemmyError> {
is_image_content_type(client, image_url).await?; is_image_content_type(client, image_url).await?;
let fetch_url = format!( let fetch_url = format!(
"{}/image/download?url={}", "{}/image/download?url={}",
Settings::get().pictrs_url(), Settings::get().pictrs_url(),
utf8_percent_encode(image_url, NON_ALPHANUMERIC) // TODO this might not be needed utf8_percent_encode(image_url.as_str(), NON_ALPHANUMERIC) // TODO this might not be needed
); );
let response = retry(|| client.get(&fetch_url).send()).await?; let response = retry(|| client.get(&fetch_url).send()).await?;
@ -109,13 +110,8 @@ pub(crate) async fn fetch_pictrs(
pub async fn fetch_iframely_and_pictrs_data( pub async fn fetch_iframely_and_pictrs_data(
client: &Client, client: &Client,
url: Option<String>, url: Option<&Url>,
) -> ( ) -> (Option<String>, Option<String>, Option<String>, Option<Url>) {
Option<String>,
Option<String>,
Option<String>,
Option<String>,
) {
match &url { match &url {
Some(url) => { Some(url) => {
// Fetch iframely data // Fetch iframely data
@ -149,11 +145,19 @@ pub async fn fetch_iframely_and_pictrs_data(
// The full urls are necessary for federation // The full urls are necessary for federation
let pictrs_thumbnail = if let Some(pictrs_hash) = pictrs_hash { let pictrs_thumbnail = if let Some(pictrs_hash) = pictrs_hash {
Some(format!( let url = Url::parse(&format!(
"{}/pictrs/image/{}", "{}/pictrs/image/{}",
Settings::get().get_protocol_and_hostname(), Settings::get().get_protocol_and_hostname(),
pictrs_hash pictrs_hash
)) ));
match url {
Ok(parsed_url) => Some(parsed_url),
Err(e) => {
// This really shouldn't happen unless the settings or hash are malformed
error!("Unexpected error constructing pictrs thumbnail URL: {}", e);
None
}
}
} else { } else {
None None
}; };
@ -169,9 +173,8 @@ pub async fn fetch_iframely_and_pictrs_data(
} }
} }
async fn is_image_content_type(client: &Client, test: &str) -> Result<(), LemmyError> { async fn is_image_content_type(client: &Client, test: &Url) -> Result<(), LemmyError> {
let response = retry(|| client.get(test).send()).await?; let response = retry(|| client.get(test.to_owned()).send()).await?;
if response if response
.headers() .headers()
.get("Content-Type") .get("Content-Type")

View File

@ -0,0 +1,4 @@
-- This is a clean-up migration that cannot be undone,
-- but Diesel requires a non-empty script so run a no-op.
SELECT 1;

View File

@ -0,0 +1 @@
UPDATE post SET url = NULL where url = '';