[[package]]
name = "url"
-version = "2.2.0"
+version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e"
+checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
dependencies = [
"form_urlencoded",
"idna",
log = "0.4.14"
env_logger = "0.8.2"
strum = "0.20.0"
-url = { version = "2.2.0", features = ["serde"] }
+url = { version = "2.2.1", features = ["serde"] }
openssl = "0.10.32"
http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] }
tokio = "0.3.6"
strum = "0.20.0"
strum_macros = "0.20.1"
lazy_static = "1.4.0"
-url = { version = "2.2.0", features = ["serde"] }
+url = { version = "2.2.1", features = ["serde"] }
openssl = "0.10.32"
http = "0.2.3"
http-signature-normalization-actix = { version = "0.4.1", default-features = false, features = ["sha-2"] }
use crate::{
check_community_ban,
- check_optional_url,
get_user_from_jwt,
get_user_from_jwt_opt,
is_admin,
EndpointType,
};
use lemmy_db_queries::{
- diesel_option_overwrite,
+ diesel_option_overwrite_to_url,
source::{
comment::Comment_,
community::{CommunityModerator_, Community_},
}
// Check to make sure the icon and banners are urls
- let icon = diesel_option_overwrite(&data.icon);
- let banner = diesel_option_overwrite(&data.banner);
-
- check_optional_url(&icon)?;
- check_optional_url(&banner)?;
+ let icon = diesel_option_overwrite_to_url(&data.icon)?;
+ let banner = diesel_option_overwrite_to_url(&data.banner)?;
// When you create a community, make sure the user becomes a moderator and a follower
let keypair = generate_actor_keypair()?;
})
.await??;
- let icon = diesel_option_overwrite(&data.icon);
- let banner = diesel_option_overwrite(&data.banner);
-
- check_optional_url(&icon)?;
- check_optional_url(&banner)?;
+ let icon = diesel_option_overwrite_to_url(&data.icon)?;
+ let banner = diesel_option_overwrite_to_url(&data.banner)?;
let community_form = CommunityForm {
name: read_community.name,
}
}
-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(
pool: &DbPool,
) -> Result<Option<FederatedInstances>, LemmyError> {
use crate::{
check_community_ban,
check_downvotes_enabled,
- check_optional_url,
collect_moderated_communities,
get_user_from_jwt,
get_user_from_jwt_opt,
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
+ let data_url = data.url.as_ref();
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 {
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(),
community_id: data.community_id,
creator_id: user.id,
embed_title: iframely_title,
embed_description: iframely_description,
embed_html: iframely_html,
- thumbnail_url: pictrs_thumbnail,
+ thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: None,
local: true,
published: None,
}
// Fetch Iframely and Pictrs cached image
+ let data_url = data.url.as_ref();
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 {
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(),
nsfw: data.nsfw,
creator_id: orig_post.creator_id.to_owned(),
embed_title: iframely_title,
embed_description: iframely_description,
embed_html: iframely_html,
- thumbnail_url: pictrs_thumbnail,
+ thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
ap_id: Some(orig_post.ap_id),
local: orig_post.local,
published: None,
use anyhow::Context;
use lemmy_api_structs::{blocking, site::*, user::Register};
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::{
naive_now,
source::{
let site_form = SiteForm {
name: data.name.to_owned(),
description: data.description.to_owned(),
- icon: Some(data.icon.to_owned()),
- banner: Some(data.banner.to_owned()),
+ icon: Some(data.icon.to_owned().map(|url| url.into())),
+ banner: Some(data.banner.to_owned().map(|url| url.into())),
creator_id: user.id,
enable_downvotes: data.enable_downvotes,
open_registration: data.open_registration,
let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
- let icon = diesel_option_overwrite(&data.icon);
- let banner = diesel_option_overwrite(&data.banner);
+ let icon = diesel_option_overwrite_to_url(&data.icon)?;
+ let banner = diesel_option_overwrite_to_url(&data.banner)?;
let site_form = SiteForm {
name: data.name.to_owned(),
use crate::{
captcha_espeak_wav_base64,
- check_optional_url,
collect_moderated_communities,
get_user_from_jwt,
get_user_from_jwt_opt,
};
use lemmy_db_queries::{
diesel_option_overwrite,
+ diesel_option_overwrite_to_url,
source::{
comment::Comment_,
community::Community_,
let data: &SaveUserSettings = &self;
let user = get_user_from_jwt(&data.auth, context.pool()).await?;
- let avatar = diesel_option_overwrite(&data.avatar);
- let banner = diesel_option_overwrite(&data.banner);
+ let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
+ let banner = diesel_option_overwrite_to_url(&data.banner)?;
let email = diesel_option_overwrite(&data.email);
let bio = diesel_option_overwrite(&data.bio);
let preferred_username = diesel_option_overwrite(&data.preferred_username);
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 bio.chars().count() > 300 {
return Err(ApiError::err("bio_length_overflow").into());
actix-web = "3.3.2"
chrono = { version = "0.4.19", features = ["serde"] }
serde_json = { version = "1.0.61", features = ["preserve_order"] }
-url = "2.2.0"
+url = "2.2.1"
community_view::CommunityView,
};
use serde::{Deserialize, Serialize};
+use url::Url;
#[derive(Deserialize, Debug)]
pub struct CreatePost {
pub name: String,
- pub url: Option<String>,
+ pub url: Option<Url>,
pub body: Option<String>,
pub nsfw: bool,
pub community_id: i32,
pub struct EditPost {
pub post_id: i32,
pub name: String,
- pub url: Option<String>,
+ pub url: Option<Url>,
pub body: Option<String>,
pub nsfw: bool,
pub auth: String,
mod_sticky_post_view::ModStickyPostView,
};
use serde::{Deserialize, Serialize};
+use url::Url;
#[derive(Deserialize, Debug)]
pub struct Search {
pub struct CreateSite {
pub name: String,
pub description: Option<String>,
- pub icon: Option<String>,
- pub banner: Option<String>,
+ pub icon: Option<Url>,
+ pub banner: Option<Url>,
pub enable_downvotes: bool,
pub open_registration: bool,
pub enable_nsfw: bool,
strum = "0.20.0"
strum_macros = "0.20.1"
lazy_static = "1.4.0"
-url = { version = "2.2.0", features = ["serde"] }
+url = { version = "2.2.1", features = ["serde"] }
percent-encoding = "2.1.0"
openssl = "0.10.32"
http = "0.2.3"
use lemmy_utils::{settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize};
+use url::Url;
pub mod comment;
pub mod community;
context: web::Data<LemmyContext>,
) -> Result<HttpResponse<Body>, LemmyError> {
let settings = Settings::get();
- let activity_id = format!(
+ let activity_id = Url::parse(&format!(
"{}/activities/{}/{}",
settings.get_protocol_and_hostname(),
info.type_,
info.id
- );
+ ))?
+ .into();
let activity = blocking(context.pool(), move |conn| {
Activity::read_from_apub_id(&conn, &activity_id)
})
pool: &DbPool,
activity_id: &Url,
) -> Result<bool, LemmyError> {
- let activity_id = activity_id.to_string();
+ let activity_id = activity_id.to_owned().into();
let existing = blocking(pool, move |conn| {
Activity::read_from_apub_id(&conn, &activity_id)
})
.as_single_xsd_any_uri()
.context(location_info!())?;
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) => {
- receive_like_comment(like, comment, context, request_counter).await
+ receive_like_comment(like, *comment, context, request_counter).await
}
}
}
.context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
PostOrComment::Post(post) => {
- receive_dislike_post(dislike, post, context, request_counter).await
+ receive_dislike_post(dislike, *post, context, request_counter).await
}
PostOrComment::Comment(comment) => {
- receive_dislike_comment(dislike, comment, context, request_counter).await
+ receive_dislike_comment(dislike, *comment, context, request_counter).await
}
}
}
.context(location_info!())?;
match find_post_or_comment_by_id(context, object).await {
- Ok(PostOrComment::Post(p)) => receive_delete_post(context, p).await,
- Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, c).await,
+ Ok(PostOrComment::Post(p)) => receive_delete_post(context, *p).await,
+ Ok(PostOrComment::Comment(c)) => receive_delete_comment(context, *c).await,
// if we dont have the object, no need to do anything
Err(_) => Ok(()),
}
remove.id(community_id.domain().context(location_info!())?)?;
match find_post_or_comment_by_id(context, object).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::Post(p)) => receive_remove_post(context, remove, *p).await,
+ Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
// if we dont have the object, no need to do anything
Err(_) => Ok(()),
}
.single_xsd_any_uri()
.context(location_info!())?;
match find_post_or_comment_by_id(context, object).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::Post(p)) => receive_undo_delete_post(context, *p).await,
+ Ok(PostOrComment::Comment(c)) => receive_undo_delete_comment(context, *c).await,
// if we dont have the object, no need to do anything
Err(_) => Ok(()),
}
.single_xsd_any_uri()
.context(location_info!())?;
match find_post_or_comment_by_id(context, object).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::Post(p)) => receive_undo_remove_post(context, *p).await,
+ Ok(PostOrComment::Comment(c)) => receive_undo_remove_comment(context, *c).await,
// if we dont have the object, no need to do anything
Err(_) => Ok(()),
}
.context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
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) => {
- receive_undo_like_comment(&like, comment, context, request_counter).await
+ receive_undo_like_comment(&like, *comment, context, request_counter).await
}
}
}
.context(location_info!())?;
match fetch_post_or_comment_by_id(&object_id, context, request_counter).await? {
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) => {
- receive_undo_dislike_comment(&dislike, comment, context, request_counter).await
+ receive_undo_dislike_comment(&dislike, *comment, context, request_counter).await
}
}
}
request_counter: &mut i32,
) -> Result<PostOrComment, LemmyError> {
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 {
- return Ok(PostOrComment::Comment(comment));
+ return Ok(PostOrComment::Comment(Box::new(comment)));
}
Err(NotFound.into())
use diesel::NotFound;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{source::activity::Activity_, ApubObject, DbPool};
-use lemmy_db_schema::source::{
- activity::Activity,
- comment::Comment,
- community::Community,
- post::Post,
- private_message::PrivateMessage,
- user::User_,
+use lemmy_db_schema::{
+ source::{
+ activity::Activity,
+ comment::Comment,
+ community::Community,
+ post::Post,
+ private_message::PrivateMessage,
+ user::User_,
+ },
+ DbUrl,
};
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
use lemmy_websocket::LemmyContext;
pub fn generate_apub_endpoint(
endpoint_type: EndpointType,
name: &str,
-) -> Result<lemmy_db_schema::Url, ParseError> {
+) -> Result<DbUrl, ParseError> {
let point = match endpoint_type {
EndpointType::Community => "c",
EndpointType::User => "u",
)
}
-pub fn generate_followers_url(
- actor_id: &lemmy_db_schema::Url,
-) -> Result<lemmy_db_schema::Url, ParseError> {
+pub fn generate_followers_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
Ok(Url::parse(&format!("{}/followers", actor_id))?.into())
}
-pub fn generate_inbox_url(
- actor_id: &lemmy_db_schema::Url,
-) -> Result<lemmy_db_schema::Url, ParseError> {
+pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
Ok(Url::parse(&format!("{}/inbox", actor_id))?.into())
}
-pub fn generate_shared_inbox_url(
- actor_id: &lemmy_db_schema::Url,
-) -> Result<lemmy_db_schema::Url, LemmyError> {
+pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
let actor_id = actor_id.clone().into_inner();
let url = format!(
"{}://{}{}/inbox",
where
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| {
Activity::insert(conn, ap_id, &activity, local, sensitive)
})
}
pub(crate) enum PostOrComment {
- Comment(Comment),
- Post(Post),
+ Comment(Box<Comment>),
+ Post(Box<Post>),
}
/// Tries to find a post or comment in the local database, without any network requests.
})
.await?;
if let Ok(p) = post {
- return Ok(PostOrComment::Post(p));
+ return Ok(PostOrComment::Post(Box::new(p)));
}
let ap_id = apub_id.clone();
})
.await?;
if let Ok(c) = comment {
- return Ok(PostOrComment::Comment(c));
+ return Ok(PostOrComment::Comment(Box::new(c)));
}
Err(NotFound.into())
let ap_id = apub_id.clone();
if let Ok(pc) = find_post_or_comment_by_id(context, ap_id.to_owned()).await {
return Ok(match pc {
- PostOrComment::Post(p) => Object::Post(p),
- PostOrComment::Comment(c) => Object::Comment(c),
+ PostOrComment::Post(p) => Object::Post(*p),
+ PostOrComment::Comment(c) => Object::Comment(*c),
});
}
if let Some(icon_url) = &self.icon {
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()?);
}
if let Some(banner_url) = &self.banner {
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()?);
}
.url()
.context(location_info!())?
.as_single_xsd_any_uri()
- .map(|u| u.to_string()),
+ .map(|u| u.to_owned().into()),
),
None => None,
};
.url()
.context(location_info!())?
.as_single_xsd_any_uri()
- .map(|u| u.to_string()),
+ .map(|u| u.to_owned().into()),
),
None => None,
};
use diesel::result::Error::NotFound;
use lemmy_api_structs::blocking;
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::{
location_info,
settings::structs::Settings,
pub(in crate::objects) fn check_object_domain<T, Kind>(
apub: &T,
expected_domain: Url,
-) -> Result<lemmy_db_schema::Url, LemmyError>
+) -> Result<DbUrl, LemmyError>
where
T: Base + AsBase<Kind>,
{
use anyhow::Context;
use lemmy_api_structs::blocking;
use lemmy_db_queries::{Crud, DbPool};
-use lemmy_db_schema::source::{
- community::Community,
- post::{Post, PostForm},
- user::User_,
+use lemmy_db_schema::{
+ self,
+ source::{
+ community::Community,
+ post::{Post, PostForm},
+ user::User_,
+ },
};
use lemmy_utils::{
location_info,
set_content_and_source(&mut page, &body)?;
}
- // TODO: hacky code because we get self.url == Some("")
- // https://github.com/LemmyNet/lemmy/issues/602
- 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(url) = &self.url {
+ page.set_url::<Url>(url.to_owned().into());
}
if let Some(thumbnail_url) = &self.thumbnail_url {
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()?);
}
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(
any_image
.to_owned()
.url()
.context(location_info!())?
.as_single_xsd_any_uri()
- .map(|u| u.to_string()),
+ .map(|url| url.to_owned()),
None => None,
};
let url = page
.url()
.map(|u| u.as_single_xsd_any_uri())
.flatten()
- .map(|s| s.to_string());
+ .map(|u| u.to_owned());
let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
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 {
(None, None, None, thumbnail_url)
};
let body_slurs_removed = body.map(|b| remove_slurs(&b));
Ok(PostForm {
name,
- url,
+ url: url.map(|u| u.into()),
body: body_slurs_removed,
creator_id: creator.id,
community_id: community.id,
embed_title: iframely_title,
embed_description: iframely_description,
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)?),
local: false,
})
if let Some(avatar_url) = &self.avatar {
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()?);
}
if let Some(banner_url) = &self.banner {
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()?);
}
.url()
.context(location_info!())?
.as_single_xsd_any_uri()
- .map(|u| u.to_string()),
+ .map(|url| url.to_owned()),
),
None => None,
};
.url()
.context(location_info!())?
.as_single_xsd_any_uri()
- .map(|u| u.to_string()),
+ .map(|url| url.to_owned()),
),
None => None,
};
admin: false,
banned: None,
email: None,
- avatar,
- banner,
+ avatar: avatar.map(|o| o.map(|i| i.into())),
+ banner: banner.map(|o| o.map(|i| i.into())),
published: person.inner.published().map(|u| u.to_owned().naive_local()),
updated: person.updated().map(|u| u.to_owned().naive_local()),
show_nsfw: false,
strum_macros = "0.20.1"
log = "0.4.14"
sha2 = "0.9.3"
-url = { version = "2.2.0", features = ["serde"] }
+url = { version = "2.2.1", features = ["serde"] }
lazy_static = "1.4.0"
regex = "1.4.3"
bcrypt = "0.9.0"
extern crate serial_test;
use diesel::{result::Error, *};
-use lemmy_db_schema::Url;
+use lemmy_db_schema::DbUrl;
+use lemmy_utils::ApiError;
use regex::Regex;
use serde::{Deserialize, Serialize};
use std::{env, env::VarError};
+use url::Url;
pub mod aggregates;
pub mod source;
}
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
Self: Sized;
fn upsert(conn: &PgConnection, user_form: &T) -> Result<Self, Error>
}
}
+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!();
pub fn establish_unpooled_connection() -> PgConnection {
#[cfg(test)]
mod tests {
- use super::fuzzy_search;
+ use super::{fuzzy_search, *};
use crate::is_email_regex;
#[test]
assert!(is_email_regex("gush@gmail.com"));
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()
+ ));
+ }
}
use crate::Crud;
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 serde::Serialize;
use serde_json::Value;
pub trait Activity_ {
fn insert<T>(
conn: &PgConnection,
- ap_id: String,
+ ap_id: DbUrl,
data: &T,
local: bool,
sensitive: bool,
where
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>;
/// Returns up to 20 activities of type `Announce/Create/Page` from the community
fn read_community_outbox(
conn: &PgConnection,
- community_actor_id: &Url,
+ community_actor_id: &DbUrl,
) -> Result<Vec<Value>, Error>;
}
impl Activity_ for Activity {
fn insert<T>(
conn: &PgConnection,
- ap_id: String,
+ ap_id: DbUrl,
data: &T,
local: bool,
sensitive: bool,
}
}
- 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::*;
activity.filter(ap_id.eq(object_id)).first::<Self>(conn)
}
fn read_community_outbox(
conn: &PgConnection,
- community_actor_id: &Url,
+ community_actor_id: &DbUrl,
) -> Result<Vec<Value>, Error> {
use lemmy_db_schema::schema::activity::dsl::*;
let res: Vec<Value> = activity
#[cfg(test)]
mod tests {
+ use super::*;
use crate::{
establish_unpooled_connection,
source::activity::Activity_,
};
use serde_json::Value;
use serial_test::serial;
+ use url::Url;
#[test]
#[serial]
let inserted_creator = User_::create(&conn, &creator_form).unwrap();
- let ap_id =
- "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c";
+ let ap_id: DbUrl = Url::parse(
+ "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
+ )
+ .unwrap()
+ .into();
let test_json: Value = serde_json::from_str(
r#"{
"@context": "https://www.w3.org/ns/activitystreams",
)
.unwrap();
let activity_form = ActivityForm {
- ap_id: ap_id.to_string(),
+ ap_id: ap_id.clone(),
data: test_json.to_owned(),
local: true,
sensitive: false,
let inserted_activity = Activity::create(&conn, &activity_form).unwrap();
let expected_activity = Activity {
- ap_id: Some(ap_id.to_string()),
+ ap_id: Some(ap_id.clone()),
id: inserted_activity.id,
data: test_json,
local: true,
};
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();
Activity::delete(&conn, inserted_activity.id).unwrap();
CommentSaved,
CommentSavedForm,
},
- Url,
+ DbUrl,
};
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(
conn: &PgConnection,
for_creator_id: i32,
}
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::*;
diesel::update(comment.find(comment_id))
}
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::*;
comment.filter(ap_id.eq(object_id)).first::<Self>(conn)
}
CommunityUserBan,
CommunityUserBanForm,
},
- Url,
+ DbUrl,
};
mod safe_type {
}
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::*;
community
.filter(actor_id.eq(for_actor_id))
new_creator_id: i32,
) -> Result<Community, 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 {
fn read_from_followers_url(
conn: &PgConnection,
- followers_url_: &Url,
+ followers_url_: &DbUrl,
) -> Result<Community, Error> {
use lemmy_db_schema::schema::community::dsl::*;
community
PostSaved,
PostSavedForm,
},
- Url,
+ DbUrl,
};
impl Crud<PostForm> for Post {
pub trait Post_ {
//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 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 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>;
.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::*;
diesel::update(post.find(post_id))
}
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::*;
post.filter(ap_id.eq(object_id)).first::<Self>(conn)
}
use crate::{ApubObject, Crud};
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 {
fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> {
}
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
Self: Sized,
{
fn update_ap_id(
conn: &PgConnection,
private_message_id: i32,
- apub_id: Url,
+ apub_id: DbUrl,
) -> Result<PrivateMessage, Error>;
fn update_content(
conn: &PgConnection,
fn update_ap_id(
conn: &PgConnection,
private_message_id: i32,
- apub_id: Url,
+ apub_id: DbUrl,
) -> Result<PrivateMessage, Error> {
use lemmy_db_schema::schema::private_message::dsl::*;
naive_now,
schema::user_::dsl::*,
source::user::{UserForm, UserSafeSettings, User_},
- Url,
+ DbUrl,
};
use lemmy_utils::settings::structs::Settings;
}
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::*;
user_
.filter(deleted.eq(false))
serde = { version = "1.0.123", features = ["derive"] }
serde_json = { version = "1.0.61", features = ["preserve_order"] }
log = "0.4.14"
-url = { version = "2.2.0", features = ["serde"] }
+url = { version = "2.2.1", features = ["serde"] }
serialize::{Output, ToSql},
sql_types::Text,
};
-use serde::Serialize;
+use serde::{Deserialize, Serialize};
use std::{
fmt::{Display, Formatter},
io::Write,
};
+use url::Url;
pub mod schema;
pub mod source;
#[repr(transparent)]
-#[derive(Clone, PartialEq, Serialize, Debug, AsExpression, FromSqlRow)]
+#[derive(Clone, PartialEq, Serialize, Deserialize, Debug, AsExpression, FromSqlRow)]
#[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
String: ToSql<Text, DB>,
{
}
}
-impl<DB: Backend> FromSql<Text, DB> for Url
+impl<DB: Backend> FromSql<Text, DB> for DbUrl
where
String: FromSql<Text, DB>,
{
fn from_sql(bytes: Option<&DB::RawValue>) -> diesel::deserialize::Result<Self> {
let str = String::from_sql(bytes)?;
- Ok(Url(url::Url::parse(&str)?))
+ Ok(DbUrl(Url::parse(&str)?))
}
}
-impl Url {
- pub fn into_inner(self) -> url::Url {
+impl DbUrl {
+ pub fn into_inner(self) -> Url {
self.0
}
}
-impl Display for Url {
+impl Display for DbUrl {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
self.to_owned().into_inner().fmt(f)
}
}
-impl From<Url> for url::Url {
- fn from(url: Url) -> Self {
+impl From<DbUrl> for Url {
+ fn from(url: DbUrl) -> Self {
url.0
}
}
-impl From<url::Url> for Url {
- fn from(url: url::Url) -> Self {
- Url(url)
+impl From<Url> for DbUrl {
+ fn from(url: Url) -> Self {
+ DbUrl(url)
}
}
-use crate::schema::activity;
+use crate::{schema::activity, DbUrl};
use serde_json::Value;
use std::fmt::Debug;
pub local: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
- pub ap_id: Option<String>,
+ pub ap_id: Option<DbUrl>,
pub sensitive: Option<bool>,
}
pub data: Value,
pub local: bool,
pub updated: Option<chrono::NaiveDateTime>,
- pub ap_id: String,
+ pub ap_id: DbUrl,
pub sensitive: bool,
}
use crate::{
schema::{comment, comment_alias_1, comment_like, comment_saved},
source::post::Post,
- Url,
+ DbUrl,
};
use serde::Serialize;
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool,
- pub ap_id: Url,
+ pub ap_id: DbUrl,
pub local: bool,
}
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool,
- pub ap_id: Url,
+ pub ap_id: DbUrl,
pub local: bool,
}
pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>,
- pub ap_id: Option<Url>,
+ pub ap_id: Option<DbUrl>,
pub local: bool,
}
use crate::{
schema::{community, community_follower, community_moderator, community_user_ban},
- Url,
+ DbUrl,
};
use serde::Serialize;
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool,
pub nsfw: bool,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime,
- pub icon: Option<String>,
- pub banner: Option<String>,
- pub followers_url: Url,
- pub inbox_url: Url,
- pub shared_inbox_url: Option<Url>,
+ pub icon: Option<DbUrl>,
+ pub banner: Option<DbUrl>,
+ pub followers_url: DbUrl,
+ pub inbox_url: DbUrl,
+ pub shared_inbox_url: Option<DbUrl>,
}
/// A safe representation of community, without the sensitive info
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: bool,
pub nsfw: bool,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub local: bool,
- pub icon: Option<String>,
- pub banner: Option<String>,
+ pub icon: Option<DbUrl>,
+ pub banner: Option<DbUrl>,
}
#[derive(Insertable, AsChangeset, Debug)]
pub updated: Option<chrono::NaiveDateTime>,
pub deleted: Option<bool>,
pub nsfw: bool,
- pub actor_id: Option<Url>,
+ pub actor_id: Option<DbUrl>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: Option<chrono::NaiveDateTime>,
- pub icon: Option<Option<String>>,
- pub banner: Option<Option<String>>,
- pub followers_url: Option<Url>,
- pub inbox_url: Option<Url>,
- pub shared_inbox_url: Option<Option<Url>>,
+ pub icon: Option<Option<DbUrl>>,
+ pub banner: Option<Option<DbUrl>>,
+ pub followers_url: Option<DbUrl>,
+ pub inbox_url: Option<DbUrl>,
+ pub shared_inbox_url: Option<Option<DbUrl>>,
}
#[derive(Identifiable, Queryable, Associations, PartialEq, Debug)]
use crate::{
schema::{post, post_like, post_read, post_saved},
- Url,
+ DbUrl,
};
use serde::Serialize;
pub struct Post {
pub id: i32,
pub name: String,
- pub url: Option<String>,
+ pub url: Option<DbUrl>,
pub body: Option<String>,
pub creator_id: i32,
pub community_id: i32,
pub embed_title: Option<String>,
pub embed_description: Option<String>,
pub embed_html: Option<String>,
- pub thumbnail_url: Option<String>,
- pub ap_id: Url,
+ pub thumbnail_url: Option<DbUrl>,
+ pub ap_id: DbUrl,
pub local: bool,
}
#[table_name = "post"]
pub struct PostForm {
pub name: String,
- pub url: Option<String>,
+ pub url: Option<DbUrl>,
pub body: Option<String>,
pub creator_id: i32,
pub community_id: i32,
pub embed_title: Option<String>,
pub embed_description: Option<String>,
pub embed_html: Option<String>,
- pub thumbnail_url: Option<String>,
- pub ap_id: Option<Url>,
+ pub thumbnail_url: Option<DbUrl>,
+ pub ap_id: Option<DbUrl>,
pub local: bool,
}
-use crate::{schema::post_report, source::post::Post};
+use crate::{schema::post_report, source::post::Post, DbUrl};
use serde::{Deserialize, Serialize};
#[derive(
pub creator_id: i32,
pub post_id: i32,
pub original_post_name: String,
- pub original_post_url: Option<String>,
+ pub original_post_url: Option<DbUrl>,
pub original_post_body: Option<String>,
pub reason: String,
pub resolved: bool,
pub creator_id: i32,
pub post_id: i32,
pub original_post_name: String,
- pub original_post_url: Option<String>,
+ pub original_post_url: Option<DbUrl>,
pub original_post_body: Option<String>,
pub reason: String,
}
-use crate::{schema::private_message, Url};
+use crate::{schema::private_message, DbUrl};
use serde::Serialize;
#[derive(Clone, Queryable, Associations, Identifiable, PartialEq, Debug, Serialize)]
pub read: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
- pub ap_id: Url,
+ pub ap_id: DbUrl,
pub local: bool,
}
pub read: Option<bool>,
pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
- pub ap_id: Option<Url>,
+ pub ap_id: Option<DbUrl>,
pub local: bool,
}
-use crate::schema::site;
+use crate::{schema::site, DbUrl};
use serde::Serialize;
#[derive(Queryable, Identifiable, PartialEq, Debug, Clone, Serialize)]
pub enable_downvotes: bool,
pub open_registration: bool,
pub enable_nsfw: bool,
- pub icon: Option<String>,
- pub banner: Option<String>,
+ pub icon: Option<DbUrl>,
+ pub banner: Option<DbUrl>,
}
#[derive(Insertable, AsChangeset)]
pub open_registration: 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.
- pub icon: Option<Option<String>>,
- pub banner: Option<Option<String>>,
+ pub icon: Option<Option<DbUrl>>,
+ pub banner: Option<Option<DbUrl>>,
}
use crate::{
schema::{user_, user_alias_1, user_alias_2},
- Url,
+ DbUrl,
};
use serde::Serialize;
pub preferred_username: Option<String>,
pub password_encrypted: String,
pub email: Option<String>,
- pub avatar: Option<String>,
+ pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime,
- pub banner: Option<String>,
+ pub banner: Option<DbUrl>,
pub deleted: bool,
- pub inbox_url: Url,
- pub shared_inbox_url: Option<Url>,
+ pub inbox_url: DbUrl,
+ pub shared_inbox_url: Option<DbUrl>,
}
/// A safe representation of user, without the sensitive info
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
- pub avatar: Option<String>,
+ pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
- pub banner: Option<String>,
+ pub banner: Option<DbUrl>,
pub deleted: bool,
- pub inbox_url: Url,
- pub shared_inbox_url: Option<Url>,
+ pub inbox_url: DbUrl,
+ pub shared_inbox_url: Option<DbUrl>,
}
/// A safe user view with only settings
pub name: String,
pub preferred_username: Option<String>,
pub email: Option<String>,
- pub avatar: Option<String>,
+ pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub last_refreshed_at: chrono::NaiveDateTime,
- pub banner: Option<String>,
+ pub banner: Option<DbUrl>,
pub deleted: bool,
}
pub preferred_username: Option<String>,
pub password_encrypted: String,
pub email: Option<String>,
- pub avatar: Option<String>,
+ pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime,
- pub banner: Option<String>,
+ pub banner: Option<DbUrl>,
pub deleted: bool,
}
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
- pub avatar: Option<String>,
+ pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
- pub banner: Option<String>,
+ pub banner: Option<DbUrl>,
pub deleted: bool,
}
pub preferred_username: Option<String>,
pub password_encrypted: String,
pub email: Option<String>,
- pub avatar: Option<String>,
+ pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<String>,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: chrono::NaiveDateTime,
- pub banner: Option<String>,
+ pub banner: Option<DbUrl>,
pub deleted: bool,
}
pub id: i32,
pub name: String,
pub preferred_username: Option<String>,
- pub avatar: Option<String>,
+ pub avatar: Option<DbUrl>,
pub admin: bool,
pub banned: bool,
pub published: chrono::NaiveDateTime,
pub updated: Option<chrono::NaiveDateTime>,
pub matrix_user_id: Option<String>,
- pub actor_id: Url,
+ pub actor_id: DbUrl,
pub bio: Option<String>,
pub local: bool,
- pub banner: Option<String>,
+ pub banner: Option<DbUrl>,
pub deleted: bool,
}
pub admin: bool,
pub banned: Option<bool>,
pub email: Option<Option<String>>,
- pub avatar: Option<Option<String>>,
+ pub avatar: Option<Option<DbUrl>>,
pub published: Option<chrono::NaiveDateTime>,
pub updated: Option<chrono::NaiveDateTime>,
pub show_nsfw: bool,
pub show_avatars: bool,
pub send_notifications_to_email: bool,
pub matrix_user_id: Option<Option<String>>,
- pub actor_id: Option<Url>,
+ pub actor_id: Option<DbUrl>,
pub bio: Option<Option<String>>,
pub local: bool,
pub private_key: Option<String>,
pub public_key: Option<String>,
pub last_refreshed_at: Option<chrono::NaiveDateTime>,
- pub banner: Option<Option<String>>,
- pub inbox_url: Option<Url>,
- pub shared_inbox_url: Option<Option<Url>>,
+ pub banner: Option<Option<DbUrl>>,
+ pub inbox_url: Option<DbUrl>,
+ pub shared_inbox_url: Option<Option<DbUrl>>,
}
diesel = { version = "1.4.5", features = ["postgres","chrono","r2d2","serde_json"] }
serde = { version = "1.0.123", features = ["derive"] }
log = "0.4.14"
-url = "2.2.0"
+url = "2.2.1"
[dev-dependencies]
serial_test = "0.5.1"
\ No newline at end of file
rss = "1.10.0"
serde = { version = "1.0.123", features = ["derive"] }
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"
lazy_static = "1.4.0"
comrak = { version = "0.9.0", default-features = false }
lazy_static = "1.4.0"
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-rt = { version = "1.1.1", default-features = false }
anyhow = "1.0.38"
use serde::Deserialize;
use std::future::Future;
use thiserror::Error;
+use url::Url;
#[derive(Clone, Debug, Error)]
#[error("Error sending request, {0}")]
pub(crate) struct IframelyResponse {
title: Option<String>,
description: Option<String>,
- thumbnail_url: Option<String>,
+ thumbnail_url: Option<Url>,
html: Option<String>,
}
pub(crate) async fn fetch_iframely(
client: &Client,
- url: &str,
+ url: &Url,
) -> Result<IframelyResponse, LemmyError> {
let fetch_url = format!("{}/oembed?url={}", Settings::get().iframely_url(), url);
pub(crate) async fn fetch_pictrs(
client: &Client,
- image_url: &str,
+ image_url: &Url,
) -> Result<PictrsResponse, LemmyError> {
is_image_content_type(client, image_url).await?;
let fetch_url = format!(
"{}/image/download?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?;
pub async fn fetch_iframely_and_pictrs_data(
client: &Client,
- url: Option<String>,
-) -> (
- Option<String>,
- Option<String>,
- Option<String>,
- Option<String>,
-) {
+ url: Option<&Url>,
+) -> (Option<String>, Option<String>, Option<String>, Option<Url>) {
match &url {
Some(url) => {
// Fetch iframely data
// The full urls are necessary for federation
let pictrs_thumbnail = if let Some(pictrs_hash) = pictrs_hash {
- Some(format!(
+ let url = Url::parse(&format!(
"{}/pictrs/image/{}",
Settings::get().get_protocol_and_hostname(),
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 {
None
};
}
}
-async fn is_image_content_type(client: &Client, test: &str) -> Result<(), LemmyError> {
- let response = retry(|| client.get(test).send()).await?;
-
+async fn is_image_content_type(client: &Client, test: &Url) -> Result<(), LemmyError> {
+ let response = retry(|| client.get(test.to_owned()).send()).await?;
if response
.headers()
.get("Content-Type")
--- /dev/null
+-- This is a clean-up migration that cannot be undone,
+-- but Diesel requires a non-empty script so run a no-op.
+SELECT 1;
+
--- /dev/null
+UPDATE post SET url = NULL where url = '';