mirror of https://github.com/kitsune-soc/kitsune
Rust beta fixes (#514)
* rust beta that breaks stuff * flake up * replace with macros, replace miette * progress * progress * finish * up * Update typos config * format toml * revert back to stable
This commit is contained in:
parent
5b814bf674
commit
0a92aebfd1
File diff suppressed because it is too large
Load Diff
|
@ -117,3 +117,4 @@ install-updater = true
|
|||
|
||||
[patch.crates-io]
|
||||
diesel-async = { git = "https://github.com/weiznich/diesel_async.git", rev = "017ebe2fb7a2709ab5db92148dea5ce812a35e09" }
|
||||
tokio-postgres-rustls = { git = "https://github.com/jbg/tokio-postgres-rustls.git", rev = "b3b59ac2fa1b5823f2426fef78a0fb74c004ec38" }
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
[default]
|
||||
extend-ignore-words-re = ["guid"]
|
||||
|
||||
[files]
|
||||
extend-exclude = [
|
||||
"crates/kitsune-language/examples/basic.rs",
|
||||
|
@ -10,4 +13,7 @@ extend-exclude = [
|
|||
|
||||
"lib/http-signatures/tests/data.rs",
|
||||
"lib/post-process/tests/input/*",
|
||||
|
||||
# Exclude all snapshot files
|
||||
"*.snap",
|
||||
]
|
||||
|
|
|
@ -11,6 +11,7 @@ autometrics = { version = "1.0.1", default-features = false }
|
|||
base64-simd = "0.8.0"
|
||||
diesel = "2.1.5"
|
||||
diesel-async = "0.4.1"
|
||||
eyre = "0.6.12"
|
||||
futures-util = "0.3.30"
|
||||
headers = "0.4.0"
|
||||
http = "1.1.0"
|
||||
|
@ -32,7 +33,6 @@ kitsune-wasm-mrf = { path = "../kitsune-wasm-mrf" }
|
|||
mime = "0.3.17"
|
||||
mime_guess = { version = "2.0.4", default-features = false }
|
||||
rsa = "0.9.6"
|
||||
scoped-futures = "0.1.3"
|
||||
serde = "1.0.197"
|
||||
sha2 = "0.10.8"
|
||||
simd-json = "0.13.9"
|
||||
|
@ -52,7 +52,7 @@ kitsune-config = { path = "../kitsune-config" }
|
|||
kitsune-test = { path = "../kitsune-test" }
|
||||
kitsune-webfinger = { path = "../kitsune-webfinger" }
|
||||
pretty_assertions = "1.4.0"
|
||||
tokio = { version = "1.36.0", features = ["macros"] }
|
||||
tokio = { version = "1.37.0", features = ["macros"] }
|
||||
tower = { version = "0.4.13", default-features = false, features = ["util"] }
|
||||
|
||||
[lints]
|
||||
|
|
|
@ -11,20 +11,16 @@ use diesel::{
|
|||
use diesel_async::RunQueryDsl;
|
||||
use futures_util::TryStreamExt;
|
||||
use iso8601_timestamp::Timestamp;
|
||||
use kitsune_core::{
|
||||
error::BoxError,
|
||||
traits::{deliverer::Action, Deliverer as DelivererTrait},
|
||||
};
|
||||
use kitsune_core::traits::{deliverer::Action, Deliverer as DelivererTrait};
|
||||
use kitsune_db::{
|
||||
model::{account::Account, favourite::Favourite, follower::Follow, post::Post, user::User},
|
||||
schema::{accounts, posts, users},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_service::attachment::AttachmentService;
|
||||
use kitsune_type::ap::{ap_context, Activity, ActivityType, ObjectField};
|
||||
use kitsune_url::UrlService;
|
||||
use kitsune_util::try_join;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use std::sync::Arc;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -61,26 +57,21 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn accept_follow(&self, follow: Follow) -> Result<()> {
|
||||
let (follower_inbox_url, (followed_account, followed_user)): (String, _) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let follower_inbox_url_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.select(accounts::inbox_url.assume_not_null())
|
||||
.get_result::<String>(db_conn);
|
||||
let (follower_inbox_url, (followed_account, followed_user)): (String, _) =
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
let follower_inbox_url_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.select(accounts::inbox_url.assume_not_null())
|
||||
.get_result::<String>(db_conn);
|
||||
|
||||
let followed_info_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.inner_join(users::table.on(accounts::id.eq(users::account_id)))
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
let followed_info_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.inner_join(users::table.on(accounts::id.eq(users::account_id)))
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
|
||||
try_join!(follower_inbox_url_fut, followed_info_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(follower_inbox_url_fut, followed_info_fut)
|
||||
})?;
|
||||
|
||||
let followed_account_url = self.service.url.user_url(followed_account.id);
|
||||
|
||||
|
@ -111,17 +102,14 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn create_or_repost(&self, post: Post) -> Result<()> {
|
||||
let (account, user) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.find(post.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let (account, user) = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(post.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
let inbox_stream = self
|
||||
.inbox_resolver
|
||||
|
@ -141,21 +129,15 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn delete_or_unrepost(&self, post: Post) -> Result<()> {
|
||||
let account_user_data = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts::table
|
||||
.find(post.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let account_user_data = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(post.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some((account, user)) = account_user_data else {
|
||||
return Ok(());
|
||||
|
@ -179,27 +161,21 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn favourite(&self, favourite: Favourite) -> Result<()> {
|
||||
let ((account, user), inbox_url) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account_user_fut = accounts::table
|
||||
.find(favourite.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result(db_conn);
|
||||
let ((account, user), inbox_url) = with_connection!(self.db_pool, |db_conn| {
|
||||
let account_user_fut = accounts::table
|
||||
.find(favourite.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
let inbox_url_fut = posts::table
|
||||
.find(favourite.post_id)
|
||||
.inner_join(accounts::table)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
let inbox_url_fut = posts::table
|
||||
.find(favourite.post_id)
|
||||
.inner_join(accounts::table)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
|
||||
try_join!(account_user_fut, inbox_url_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(account_user_fut, inbox_url_fut)
|
||||
})?;
|
||||
|
||||
if let Some(ref inbox_url) = inbox_url {
|
||||
let activity = favourite.into_activity(self.mapping_state()).await?;
|
||||
|
@ -213,26 +189,21 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn follow(&self, follow: Follow) -> Result<()> {
|
||||
let ((follower, follower_user), followed_inbox) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let follower_info_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
let ((follower, follower_user), followed_inbox) =
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
let follower_info_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
|
||||
let followed_inbox_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
let followed_inbox_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
|
||||
try_join!(follower_info_fut, followed_inbox_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(follower_info_fut, followed_inbox_fut)
|
||||
})?;
|
||||
|
||||
if let Some(followed_inbox) = followed_inbox {
|
||||
let follow_activity = follow.into_activity(self.mapping_state()).await?;
|
||||
|
@ -246,28 +217,23 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn reject_follow(&self, follow: Follow) -> Result<()> {
|
||||
let (follower_inbox_url, (followed_account, followed_user), _delete_result) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
let follower_inbox_url_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.select(accounts::inbox_url.assume_not_null())
|
||||
.get_result::<String>(db_conn);
|
||||
let (follower_inbox_url, (followed_account, followed_user), _delete_result) =
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
let follower_inbox_url_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.select(accounts::inbox_url.assume_not_null())
|
||||
.get_result::<String>(db_conn);
|
||||
|
||||
let followed_info_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.inner_join(users::table.on(accounts::id.eq(users::account_id)))
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
let followed_info_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.inner_join(users::table.on(accounts::id.eq(users::account_id)))
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
|
||||
let delete_fut = diesel::delete(&follow).execute(db_conn);
|
||||
let delete_fut = diesel::delete(&follow).execute(db_conn);
|
||||
|
||||
try_join!(follower_inbox_url_fut, followed_info_fut, delete_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(follower_inbox_url_fut, followed_info_fut, delete_fut)
|
||||
})?;
|
||||
|
||||
let followed_account_url = self.service.url.user_url(followed_account.id);
|
||||
|
||||
|
@ -298,27 +264,21 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn unfavourite(&self, favourite: Favourite) -> Result<()> {
|
||||
let ((account, user), inbox_url) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account_user_fut = accounts::table
|
||||
.find(favourite.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result(db_conn);
|
||||
let ((account, user), inbox_url) = with_connection!(self.db_pool, |db_conn| {
|
||||
let account_user_fut = accounts::table
|
||||
.find(favourite.account_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
let inbox_url_fut = posts::table
|
||||
.find(favourite.post_id)
|
||||
.inner_join(accounts::table)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
let inbox_url_fut = posts::table
|
||||
.find(favourite.post_id)
|
||||
.inner_join(accounts::table)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
|
||||
try_join!(account_user_fut, inbox_url_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(account_user_fut, inbox_url_fut)
|
||||
})?;
|
||||
|
||||
if let Some(ref inbox_url) = inbox_url {
|
||||
let activity = favourite.into_negate_activity(self.mapping_state()).await?;
|
||||
|
@ -331,26 +291,21 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn unfollow(&self, follow: Follow) -> Result<()> {
|
||||
let ((follower, follower_user), followed_account_inbox_url) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
let follower_info_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
let ((follower, follower_user), followed_account_inbox_url) =
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
let follower_info_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.inner_join(users::table)
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result::<(Account, User)>(db_conn);
|
||||
|
||||
let followed_account_inbox_url_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
let followed_account_inbox_url_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.select(accounts::inbox_url)
|
||||
.get_result::<Option<String>>(db_conn);
|
||||
|
||||
try_join!(follower_info_fut, followed_account_inbox_url_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(follower_info_fut, followed_account_inbox_url_fut)
|
||||
})?;
|
||||
|
||||
if let Some(ref followed_account_inbox_url) = followed_account_inbox_url {
|
||||
let follow_activity = follow.into_negate_activity(self.mapping_state()).await?;
|
||||
|
@ -369,20 +324,14 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn update_account(&self, account: Account) -> Result<()> {
|
||||
let user = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
users::table
|
||||
.filter(users::account_id.eq(account.id))
|
||||
.select(User::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let user = with_connection!(self.db_pool, |db_conn| {
|
||||
users::table
|
||||
.filter(users::account_id.eq(account.id))
|
||||
.select(User::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(user) = user else {
|
||||
return Ok(());
|
||||
|
@ -404,22 +353,16 @@ impl Deliverer {
|
|||
}
|
||||
|
||||
async fn update_post(&self, post: Post) -> Result<()> {
|
||||
let post_account_user_data = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts::table
|
||||
.find(post.id)
|
||||
.inner_join(accounts::table)
|
||||
.inner_join(users::table.on(accounts::id.eq(users::account_id)))
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post_account_user_data = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(post.id)
|
||||
.inner_join(accounts::table)
|
||||
.inner_join(users::table.on(accounts::id.eq(users::account_id)))
|
||||
.select(<(Account, User)>::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some((account, user)) = post_account_user_data else {
|
||||
return Ok(());
|
||||
|
@ -447,7 +390,7 @@ impl Deliverer {
|
|||
|
||||
#[async_trait]
|
||||
impl DelivererTrait for Deliverer {
|
||||
async fn deliver(&self, action: Action) -> Result<(), BoxError> {
|
||||
async fn deliver(&self, action: Action) -> eyre::Result<()> {
|
||||
match action {
|
||||
Action::AcceptFollow(follow) => self.accept_follow(follow).await,
|
||||
Action::Create(post) | Action::Repost(post) => self.create_or_repost(post).await,
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
use diesel_async::pooled_connection::bb8;
|
||||
use kitsune_core::error::BoxError;
|
||||
use rsa::pkcs8::der;
|
||||
use std::{
|
||||
convert::Infallible,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
use std::{convert::Infallible, fmt::Debug};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
@ -33,13 +29,13 @@ pub enum Error {
|
|||
FederationFilter(#[from] kitsune_federation_filter::error::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
FetchAccount(BoxError),
|
||||
FetchAccount(eyre::Report),
|
||||
|
||||
#[error(transparent)]
|
||||
FetchEmoji(BoxError),
|
||||
FetchEmoji(eyre::Report),
|
||||
|
||||
#[error(transparent)]
|
||||
FetchPost(BoxError),
|
||||
FetchPost(eyre::Report),
|
||||
|
||||
#[error(transparent)]
|
||||
Http(#[from] http::Error),
|
||||
|
@ -66,7 +62,7 @@ pub enum Error {
|
|||
NotFound,
|
||||
|
||||
#[error(transparent)]
|
||||
Resolver(BoxError),
|
||||
Resolver(eyre::Report),
|
||||
|
||||
#[error(transparent)]
|
||||
Search(#[from] kitsune_search::Error),
|
||||
|
@ -89,15 +85,3 @@ impl From<Infallible> for Error {
|
|||
match err {}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> From<kitsune_db::PoolError<E>> for Error
|
||||
where
|
||||
E: Into<Error> + Debug + Display,
|
||||
{
|
||||
fn from(value: kitsune_db::PoolError<E>) -> Self {
|
||||
match value {
|
||||
kitsune_db::PoolError::Pool(err) => err.into(),
|
||||
kitsune_db::PoolError::User(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,11 +11,11 @@ use kitsune_core::traits::fetcher::AccountFetchOptions;
|
|||
use kitsune_db::{
|
||||
model::account::{Account, AccountConflictChangeset, NewAccount, UpdateAccountMedia},
|
||||
schema::accounts,
|
||||
with_connection, with_transaction,
|
||||
};
|
||||
use kitsune_search::SearchBackend;
|
||||
use kitsune_type::ap::actor::Actor;
|
||||
use kitsune_util::{convert::timestamp_to_uuid, sanitize::CleanHtmlExt};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use url::Url;
|
||||
|
||||
impl Fetcher {
|
||||
|
@ -36,20 +36,14 @@ impl Fetcher {
|
|||
return Ok(Some(user));
|
||||
}
|
||||
|
||||
let user_data = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts::table
|
||||
.filter(accounts::url.eq(opts.url))
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let user_data = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.filter(accounts::url.eq(opts.url))
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(user) = user_data {
|
||||
return Ok(Some(user));
|
||||
|
@ -96,93 +90,87 @@ impl Fetcher {
|
|||
|
||||
actor.clean_html();
|
||||
|
||||
let account: Account = self
|
||||
.db_pool
|
||||
.with_transaction(|tx| {
|
||||
async move {
|
||||
let account = diesel::insert_into(accounts::table)
|
||||
.values(NewAccount {
|
||||
id: timestamp_to_uuid(actor.published),
|
||||
display_name: actor.name.as_deref(),
|
||||
note: actor.subject.as_deref(),
|
||||
username: actor.preferred_username.as_str(),
|
||||
locked: actor.manually_approves_followers,
|
||||
local: false,
|
||||
domain,
|
||||
actor_type: actor.r#type.into(),
|
||||
url: actor.id.as_str(),
|
||||
featured_collection_url: actor.featured.as_deref(),
|
||||
followers_url: actor.followers.as_deref(),
|
||||
following_url: actor.following.as_deref(),
|
||||
inbox_url: Some(actor.inbox.as_str()),
|
||||
outbox_url: actor.outbox.as_deref(),
|
||||
shared_inbox_url: actor
|
||||
.endpoints
|
||||
.and_then(|endpoints| endpoints.shared_inbox)
|
||||
.as_deref(),
|
||||
public_key_id: actor.public_key.id.as_str(),
|
||||
public_key: actor.public_key.public_key_pem.as_str(),
|
||||
created_at: Some(actor.published),
|
||||
})
|
||||
.on_conflict(accounts::url)
|
||||
.do_update()
|
||||
.set(AccountConflictChangeset {
|
||||
display_name: actor.name.as_deref(),
|
||||
note: actor.subject.as_deref(),
|
||||
locked: actor.manually_approves_followers,
|
||||
public_key_id: actor.public_key.id.as_str(),
|
||||
public_key: actor.public_key.public_key_pem.as_str(),
|
||||
})
|
||||
let account: Account = with_transaction!(self.db_pool, |tx| {
|
||||
let account = diesel::insert_into(accounts::table)
|
||||
.values(NewAccount {
|
||||
id: timestamp_to_uuid(actor.published),
|
||||
display_name: actor.name.as_deref(),
|
||||
note: actor.subject.as_deref(),
|
||||
username: actor.preferred_username.as_str(),
|
||||
locked: actor.manually_approves_followers,
|
||||
local: false,
|
||||
domain,
|
||||
actor_type: actor.r#type.into(),
|
||||
url: actor.id.as_str(),
|
||||
featured_collection_url: actor.featured.as_deref(),
|
||||
followers_url: actor.followers.as_deref(),
|
||||
following_url: actor.following.as_deref(),
|
||||
inbox_url: Some(actor.inbox.as_str()),
|
||||
outbox_url: actor.outbox.as_deref(),
|
||||
shared_inbox_url: actor
|
||||
.endpoints
|
||||
.and_then(|endpoints| endpoints.shared_inbox)
|
||||
.as_deref(),
|
||||
public_key_id: actor.public_key.id.as_str(),
|
||||
public_key: actor.public_key.public_key_pem.as_str(),
|
||||
created_at: Some(actor.published),
|
||||
})
|
||||
.on_conflict(accounts::url)
|
||||
.do_update()
|
||||
.set(AccountConflictChangeset {
|
||||
display_name: actor.name.as_deref(),
|
||||
note: actor.subject.as_deref(),
|
||||
locked: actor.manually_approves_followers,
|
||||
public_key_id: actor.public_key.id.as_str(),
|
||||
public_key: actor.public_key.public_key_pem.as_str(),
|
||||
})
|
||||
.returning(Account::as_returning())
|
||||
.get_result::<Account>(tx)
|
||||
.await?;
|
||||
|
||||
let avatar_id = if let Some(icon) = actor.icon {
|
||||
process_attachments(tx, &account, &[icon]).await?.pop()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let header_id = if let Some(image) = actor.image {
|
||||
process_attachments(tx, &account, &[image]).await?.pop()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut update_changeset = UpdateAccountMedia::default();
|
||||
if let Some(avatar_id) = avatar_id {
|
||||
update_changeset = UpdateAccountMedia {
|
||||
avatar_id: Some(avatar_id),
|
||||
..update_changeset
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(header_id) = header_id {
|
||||
update_changeset = UpdateAccountMedia {
|
||||
header_id: Some(header_id),
|
||||
..update_changeset
|
||||
};
|
||||
}
|
||||
|
||||
let account = match update_changeset {
|
||||
UpdateAccountMedia {
|
||||
avatar_id: None,
|
||||
header_id: None,
|
||||
} => account,
|
||||
_ => {
|
||||
diesel::update(&account)
|
||||
.set(update_changeset)
|
||||
.returning(Account::as_returning())
|
||||
.get_result::<Account>(tx)
|
||||
.await?;
|
||||
|
||||
let avatar_id = if let Some(icon) = actor.icon {
|
||||
process_attachments(tx, &account, &[icon]).await?.pop()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let header_id = if let Some(image) = actor.image {
|
||||
process_attachments(tx, &account, &[image]).await?.pop()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let mut update_changeset = UpdateAccountMedia::default();
|
||||
if let Some(avatar_id) = avatar_id {
|
||||
update_changeset = UpdateAccountMedia {
|
||||
avatar_id: Some(avatar_id),
|
||||
..update_changeset
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(header_id) = header_id {
|
||||
update_changeset = UpdateAccountMedia {
|
||||
header_id: Some(header_id),
|
||||
..update_changeset
|
||||
};
|
||||
}
|
||||
|
||||
let account = match update_changeset {
|
||||
UpdateAccountMedia {
|
||||
avatar_id: None,
|
||||
header_id: None,
|
||||
} => account,
|
||||
_ => {
|
||||
diesel::update(&account)
|
||||
.set(update_changeset)
|
||||
.returning(Account::as_returning())
|
||||
.get_result(tx)
|
||||
.await?
|
||||
}
|
||||
};
|
||||
|
||||
Ok::<_, Error>(account)
|
||||
.get_result(tx)
|
||||
.await?
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
};
|
||||
|
||||
Ok::<_, Error>(account)
|
||||
})?;
|
||||
|
||||
self.search_backend
|
||||
.add_to_index(account.clone().into())
|
||||
|
|
|
@ -9,28 +9,22 @@ use kitsune_db::{
|
|||
media_attachment::{MediaAttachment, NewMediaAttachment},
|
||||
},
|
||||
schema::{custom_emojis, media_attachments},
|
||||
with_connection, with_transaction,
|
||||
};
|
||||
use kitsune_type::ap::emoji::Emoji;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use url::Url;
|
||||
|
||||
impl Fetcher {
|
||||
pub(crate) async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>> {
|
||||
let existing_emoji = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
custom_emojis::table
|
||||
.filter(custom_emojis::remote_id.eq(url))
|
||||
.select(CustomEmoji::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let existing_emoji = with_connection!(self.db_pool, |db_conn| {
|
||||
custom_emojis::table
|
||||
.filter(custom_emojis::remote_id.eq(url))
|
||||
.select(CustomEmoji::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(emoji) = existing_emoji {
|
||||
return Ok(Some(emoji));
|
||||
|
@ -57,42 +51,36 @@ impl Fetcher {
|
|||
|
||||
let name_pure = emoji.name.replace(':', "");
|
||||
|
||||
let emoji: CustomEmoji = self
|
||||
.db_pool
|
||||
.with_transaction(|tx| {
|
||||
async move {
|
||||
let media_attachment = diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: Uuid::now_v7(),
|
||||
account_id: None,
|
||||
content_type,
|
||||
description: None,
|
||||
blurhash: None,
|
||||
file_path: None,
|
||||
remote_url: Some(&emoji.icon.url),
|
||||
})
|
||||
.returning(MediaAttachment::as_returning())
|
||||
.get_result::<MediaAttachment>(tx)
|
||||
.await?;
|
||||
let emoji = diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id: Uuid::now_v7(),
|
||||
remote_id: emoji.id,
|
||||
shortcode: name_pure.to_string(),
|
||||
domain: Some(domain.to_string()),
|
||||
media_attachment_id: media_attachment.id,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.returning(CustomEmoji::as_returning())
|
||||
.get_result::<CustomEmoji>(tx)
|
||||
.await?;
|
||||
Ok::<_, Error>(emoji)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let emoji: CustomEmoji = with_transaction!(self.db_pool, |tx| {
|
||||
let media_attachment = diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: Uuid::now_v7(),
|
||||
account_id: None,
|
||||
content_type,
|
||||
description: None,
|
||||
blurhash: None,
|
||||
file_path: None,
|
||||
remote_url: Some(&emoji.icon.url),
|
||||
})
|
||||
.returning(MediaAttachment::as_returning())
|
||||
.get_result::<MediaAttachment>(tx)
|
||||
.await?;
|
||||
let emoji = diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id: Uuid::now_v7(),
|
||||
remote_id: emoji.id,
|
||||
shortcode: name_pure.to_string(),
|
||||
domain: Some(domain.to_string()),
|
||||
media_attachment_id: media_attachment.id,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.returning(CustomEmoji::as_returning())
|
||||
.get_result::<CustomEmoji>(tx)
|
||||
.await?;
|
||||
Ok::<_, Error>(emoji)
|
||||
})?;
|
||||
|
||||
Ok(Some(emoji))
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ use kitsune_cache::ArcCache;
|
|||
use kitsune_config::language_detection::Configuration as LanguageDetectionConfig;
|
||||
use kitsune_core::{
|
||||
consts::USER_AGENT,
|
||||
error::BoxError,
|
||||
traits::{
|
||||
fetcher::{AccountFetchOptions, PostFetchOptions},
|
||||
Fetcher as FetcherTrait, Resolver,
|
||||
|
@ -124,18 +123,15 @@ impl FetcherTrait for Fetcher {
|
|||
Arc::new(self.resolver.clone())
|
||||
}
|
||||
|
||||
async fn fetch_account(
|
||||
&self,
|
||||
opts: AccountFetchOptions<'_>,
|
||||
) -> Result<Option<Account>, BoxError> {
|
||||
async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> eyre::Result<Option<Account>> {
|
||||
Ok(self.fetch_actor(opts).await?)
|
||||
}
|
||||
|
||||
async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>, BoxError> {
|
||||
async fn fetch_emoji(&self, url: &str) -> eyre::Result<Option<CustomEmoji>> {
|
||||
Ok(self.fetch_emoji(url).await?)
|
||||
}
|
||||
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result<Option<Post>, BoxError> {
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> eyre::Result<Option<Post>> {
|
||||
Ok(self.fetch_object(opts.url, opts.call_depth).await?)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,8 +4,7 @@ use autometrics::autometrics;
|
|||
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_cache::CacheBackend;
|
||||
use kitsune_db::{model::post::Post, schema::posts};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::post::Post, schema::posts, with_connection};
|
||||
|
||||
// Maximum call depth of fetching new posts. Prevents unbounded recursion.
|
||||
// Setting this to >=40 would cause the `fetch_infinitely_long_reply_chain` test to run into stack overflow
|
||||
|
@ -23,20 +22,14 @@ impl Fetcher {
|
|||
return Ok(Some(post));
|
||||
}
|
||||
|
||||
let post = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts::table
|
||||
.filter(posts::url.eq(url))
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.filter(posts::url.eq(url))
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(post) = post {
|
||||
self.post_cache.set(url, &post).await?;
|
||||
|
|
|
@ -13,9 +13,8 @@ use kitsune_db::{
|
|||
post::{Post, Visibility},
|
||||
},
|
||||
schema::{accounts, accounts_follows},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
|
||||
pub struct InboxResolver {
|
||||
db_pool: PgPool,
|
||||
|
@ -32,27 +31,25 @@ impl InboxResolver {
|
|||
&self,
|
||||
account: &Account,
|
||||
) -> Result<impl Stream<Item = Result<String, DieselError>> + Send + '_> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts_follows::table
|
||||
.filter(accounts_follows::account_id.eq(account.id))
|
||||
.inner_join(
|
||||
accounts::table.on(accounts::id.eq(accounts_follows::follower_id).and(
|
||||
accounts::inbox_url
|
||||
.is_not_null()
|
||||
.or(accounts::shared_inbox_url.is_not_null()),
|
||||
)),
|
||||
)
|
||||
.distinct()
|
||||
.select(coalesce_nullable(
|
||||
accounts::shared_inbox_url,
|
||||
accounts::inbox_url,
|
||||
))
|
||||
.load_stream(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.filter(accounts_follows::account_id.eq(account.id))
|
||||
.inner_join(
|
||||
accounts::table.on(accounts::id.eq(accounts_follows::follower_id).and(
|
||||
accounts::inbox_url
|
||||
.is_not_null()
|
||||
.or(accounts::shared_inbox_url.is_not_null()),
|
||||
)),
|
||||
)
|
||||
.distinct()
|
||||
.select(coalesce_nullable(
|
||||
accounts::shared_inbox_url,
|
||||
accounts::inbox_url,
|
||||
))
|
||||
.load_stream(db_conn)
|
||||
.await
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
#[instrument(skip_all, fields(post_id = %post.id))]
|
||||
|
@ -60,35 +57,29 @@ impl InboxResolver {
|
|||
&self,
|
||||
post: &Post,
|
||||
) -> Result<impl Stream<Item = Result<String, DieselError>> + Send + '_> {
|
||||
let (account, mentioned_inbox_stream) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account = accounts::table
|
||||
.find(post.account_id)
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.await?;
|
||||
let (account, mentioned_inbox_stream) = with_connection!(self.db_pool, |db_conn| {
|
||||
let account = accounts::table
|
||||
.find(post.account_id)
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.await?;
|
||||
|
||||
let mentioned_inbox_stream = Mention::belonging_to(post)
|
||||
.inner_join(accounts::table)
|
||||
.filter(
|
||||
accounts::shared_inbox_url
|
||||
.is_not_null()
|
||||
.or(accounts::inbox_url.is_not_null()),
|
||||
)
|
||||
.select(coalesce_nullable(
|
||||
accounts::shared_inbox_url,
|
||||
accounts::inbox_url,
|
||||
))
|
||||
.load_stream(db_conn)
|
||||
.await?;
|
||||
let mentioned_inbox_stream = Mention::belonging_to(post)
|
||||
.inner_join(accounts::table)
|
||||
.filter(
|
||||
accounts::shared_inbox_url
|
||||
.is_not_null()
|
||||
.or(accounts::inbox_url.is_not_null()),
|
||||
)
|
||||
.select(coalesce_nullable(
|
||||
accounts::shared_inbox_url,
|
||||
accounts::inbox_url,
|
||||
))
|
||||
.load_stream(db_conn)
|
||||
.await?;
|
||||
|
||||
Ok::<_, Error>((account, mentioned_inbox_stream))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
Ok::<_, Error>((account, mentioned_inbox_stream))
|
||||
})?;
|
||||
|
||||
let stream = if post.visibility == Visibility::MentionOnly {
|
||||
Either::Left(mentioned_inbox_stream)
|
||||
|
|
|
@ -20,14 +20,13 @@ use kitsune_db::{
|
|||
schema::{
|
||||
media_attachments, posts, posts_custom_emojis, posts_media_attachments, posts_mentions,
|
||||
},
|
||||
PgPool,
|
||||
with_transaction, PgPool,
|
||||
};
|
||||
use kitsune_embed::Client as EmbedClient;
|
||||
use kitsune_language::Language;
|
||||
use kitsune_search::{AnySearchBackend, SearchBackend};
|
||||
use kitsune_type::ap::{object::MediaAttachment, Object, Tag, TagType};
|
||||
use kitsune_util::{convert::timestamp_to_uuid, process, sanitize::CleanHtmlExt, CowBox};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -288,58 +287,53 @@ pub async fn process_new_object(process_data: ProcessNewObject<'_>) -> Result<Po
|
|||
search_backend,
|
||||
} = preprocess_object(process_data).await?;
|
||||
|
||||
let post = db_pool
|
||||
.with_transaction(|tx| {
|
||||
async move {
|
||||
let new_post = diesel::insert_into(posts::table)
|
||||
.values(NewPost {
|
||||
id: timestamp_to_uuid(object.published),
|
||||
account_id: user.id,
|
||||
in_reply_to_id,
|
||||
reposted_post_id: None,
|
||||
subject: object.summary.as_deref(),
|
||||
content: object.content.as_str(),
|
||||
content_source: "",
|
||||
content_lang: content_lang.into(),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: object.sensitive,
|
||||
visibility,
|
||||
is_local: false,
|
||||
url: object.id.as_str(),
|
||||
created_at: Some(object.published),
|
||||
let post = with_transaction!(db_pool, |tx| {
|
||||
let new_post = diesel::insert_into(posts::table)
|
||||
.values(NewPost {
|
||||
id: timestamp_to_uuid(object.published),
|
||||
account_id: user.id,
|
||||
in_reply_to_id,
|
||||
reposted_post_id: None,
|
||||
subject: object.summary.as_deref(),
|
||||
content: object.content.as_str(),
|
||||
content_source: "",
|
||||
content_lang: content_lang.into(),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: object.sensitive,
|
||||
visibility,
|
||||
is_local: false,
|
||||
url: object.id.as_str(),
|
||||
created_at: Some(object.published),
|
||||
})
|
||||
.on_conflict(posts::url)
|
||||
.do_update()
|
||||
.set(PostConflictChangeset {
|
||||
subject: object.summary.as_deref(),
|
||||
content: object.content.as_str(),
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result::<Post>(tx)
|
||||
.await?;
|
||||
|
||||
let attachment_ids = process_attachments(tx, &user, &object.attachment).await?;
|
||||
diesel::insert_into(posts_media_attachments::table)
|
||||
.values(
|
||||
attachment_ids
|
||||
.into_iter()
|
||||
.map(|attachment_id| NewPostMediaAttachment {
|
||||
post_id: new_post.id,
|
||||
media_attachment_id: attachment_id,
|
||||
})
|
||||
.on_conflict(posts::url)
|
||||
.do_update()
|
||||
.set(PostConflictChangeset {
|
||||
subject: object.summary.as_deref(),
|
||||
content: object.content.as_str(),
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result::<Post>(tx)
|
||||
.await?;
|
||||
.collect::<Vec<NewPostMediaAttachment>>(),
|
||||
)
|
||||
.execute(tx)
|
||||
.await?;
|
||||
|
||||
let attachment_ids = process_attachments(tx, &user, &object.attachment).await?;
|
||||
diesel::insert_into(posts_media_attachments::table)
|
||||
.values(
|
||||
attachment_ids
|
||||
.into_iter()
|
||||
.map(|attachment_id| NewPostMediaAttachment {
|
||||
post_id: new_post.id,
|
||||
media_attachment_id: attachment_id,
|
||||
})
|
||||
.collect::<Vec<NewPostMediaAttachment>>(),
|
||||
)
|
||||
.execute(tx)
|
||||
.await?;
|
||||
handle_mentions(tx, &user, new_post.id, &object.tag).await?;
|
||||
handle_custom_emojis(tx, new_post.id, fetcher, &object.tag).await?;
|
||||
|
||||
handle_mentions(tx, &user, new_post.id, &object.tag).await?;
|
||||
handle_custom_emojis(tx, new_post.id, fetcher, &object.tag).await?;
|
||||
|
||||
Ok::<_, Error>(new_post)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
Ok::<_, Error>(new_post)
|
||||
})?;
|
||||
|
||||
if post.visibility == Visibility::Public || post.visibility == Visibility::Unlisted {
|
||||
search_backend.add_to_index(post.clone().into()).await?;
|
||||
|
@ -362,51 +356,46 @@ pub async fn update_object(process_data: ProcessNewObject<'_>) -> Result<Post> {
|
|||
search_backend,
|
||||
} = preprocess_object(process_data).await?;
|
||||
|
||||
let post = db_pool
|
||||
.with_transaction(|tx| {
|
||||
async move {
|
||||
let updated_post = diesel::update(posts::table)
|
||||
.filter(posts::url.eq(object.id.as_str()))
|
||||
.set(FullPostChangeset {
|
||||
account_id: user.id,
|
||||
in_reply_to_id,
|
||||
reposted_post_id: None,
|
||||
subject: object.summary.as_deref(),
|
||||
content: object.content.as_str(),
|
||||
content_source: "",
|
||||
content_lang: content_lang.into(),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: object.sensitive,
|
||||
visibility,
|
||||
is_local: false,
|
||||
updated_at: Timestamp::now_utc(),
|
||||
let post = with_transaction!(db_pool, |tx| {
|
||||
let updated_post = diesel::update(posts::table)
|
||||
.filter(posts::url.eq(object.id.as_str()))
|
||||
.set(FullPostChangeset {
|
||||
account_id: user.id,
|
||||
in_reply_to_id,
|
||||
reposted_post_id: None,
|
||||
subject: object.summary.as_deref(),
|
||||
content: object.content.as_str(),
|
||||
content_source: "",
|
||||
content_lang: content_lang.into(),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: object.sensitive,
|
||||
visibility,
|
||||
is_local: false,
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result::<Post>(tx)
|
||||
.await?;
|
||||
|
||||
let attachment_ids = process_attachments(tx, &user, &object.attachment).await?;
|
||||
diesel::insert_into(posts_media_attachments::table)
|
||||
.values(
|
||||
attachment_ids
|
||||
.into_iter()
|
||||
.map(|attachment_id| NewPostMediaAttachment {
|
||||
post_id: updated_post.id,
|
||||
media_attachment_id: attachment_id,
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result::<Post>(tx)
|
||||
.await?;
|
||||
.collect::<Vec<NewPostMediaAttachment>>(),
|
||||
)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(tx)
|
||||
.await?;
|
||||
|
||||
let attachment_ids = process_attachments(tx, &user, &object.attachment).await?;
|
||||
diesel::insert_into(posts_media_attachments::table)
|
||||
.values(
|
||||
attachment_ids
|
||||
.into_iter()
|
||||
.map(|attachment_id| NewPostMediaAttachment {
|
||||
post_id: updated_post.id,
|
||||
media_attachment_id: attachment_id,
|
||||
})
|
||||
.collect::<Vec<NewPostMediaAttachment>>(),
|
||||
)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(tx)
|
||||
.await?;
|
||||
handle_mentions(tx, &user, updated_post.id, &object.tag).await?;
|
||||
|
||||
handle_mentions(tx, &user, updated_post.id, &object.tag).await?;
|
||||
|
||||
Ok::<_, Error>(updated_post)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
Ok::<_, Error>(updated_post)
|
||||
})?;
|
||||
|
||||
if post.visibility == Visibility::Public || post.visibility == Visibility::Unlisted {
|
||||
search_backend.update_in_index(post.clone().into()).await?;
|
||||
|
|
|
@ -6,10 +6,10 @@ use iso8601_timestamp::Timestamp;
|
|||
use kitsune_db::{
|
||||
model::{account::Account, favourite::Favourite, follower::Follow, post::Post},
|
||||
schema::{accounts, posts},
|
||||
with_connection,
|
||||
};
|
||||
use kitsune_type::ap::{ap_context, Activity, ActivityType, ObjectField};
|
||||
use kitsune_util::try_join;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use std::future::Future;
|
||||
|
||||
pub trait IntoActivity {
|
||||
|
@ -50,25 +50,19 @@ impl IntoActivity for Favourite {
|
|||
type NegateOutput = Activity;
|
||||
|
||||
async fn into_activity(self, state: State<'_>) -> Result<Self::Output> {
|
||||
let (account_url, post_url) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account_url_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn);
|
||||
let (account_url, post_url) = with_connection!(state.db_pool, |db_conn| {
|
||||
let account_url_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn);
|
||||
|
||||
let post_url_fut = posts::table
|
||||
.find(self.post_id)
|
||||
.select(posts::url)
|
||||
.get_result(db_conn);
|
||||
let post_url_fut = posts::table
|
||||
.find(self.post_id)
|
||||
.select(posts::url)
|
||||
.get_result(db_conn);
|
||||
|
||||
try_join!(account_url_fut, post_url_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(account_url_fut, post_url_fut)
|
||||
})?;
|
||||
|
||||
Ok(Activity {
|
||||
context: ap_context(),
|
||||
|
@ -81,16 +75,13 @@ impl IntoActivity for Favourite {
|
|||
}
|
||||
|
||||
async fn into_negate_activity(self, state: State<'_>) -> Result<Self::NegateOutput> {
|
||||
let account_url = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.find(self.account_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let account_url = with_connection!(state.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(self.account_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(Activity {
|
||||
context: ap_context(),
|
||||
|
@ -108,25 +99,19 @@ impl IntoActivity for Follow {
|
|||
type NegateOutput = Activity;
|
||||
|
||||
async fn into_activity(self, state: State<'_>) -> Result<Self::Output> {
|
||||
let (attributed_to, object) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let attributed_to_fut = accounts::table
|
||||
.find(self.follower_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn);
|
||||
let (attributed_to, object) = with_connection!(state.db_pool, |db_conn| {
|
||||
let attributed_to_fut = accounts::table
|
||||
.find(self.follower_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn);
|
||||
|
||||
let object_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn);
|
||||
let object_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn);
|
||||
|
||||
try_join!(attributed_to_fut, object_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(attributed_to_fut, object_fut)
|
||||
})?;
|
||||
|
||||
Ok(Activity {
|
||||
context: ap_context(),
|
||||
|
@ -139,16 +124,13 @@ impl IntoActivity for Follow {
|
|||
}
|
||||
|
||||
async fn into_negate_activity(self, state: State<'_>) -> Result<Self::NegateOutput> {
|
||||
let attributed_to = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.find(self.follower_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let attributed_to = with_connection!(state.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(self.follower_id)
|
||||
.select(accounts::url)
|
||||
.get_result::<String>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(Activity {
|
||||
context: ap_context(),
|
||||
|
@ -169,16 +151,13 @@ impl IntoActivity for Post {
|
|||
let account_url = state.service.url.user_url(self.account_id);
|
||||
|
||||
if let Some(reposted_post_id) = self.reposted_post_id {
|
||||
let reposted_post_url = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.find(reposted_post_id)
|
||||
.select(posts::url)
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let reposted_post_url = with_connection!(state.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(reposted_post_id)
|
||||
.select(posts::url)
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(Activity {
|
||||
context: ap_context(),
|
||||
|
|
|
@ -12,6 +12,7 @@ use kitsune_db::{
|
|||
post::Post,
|
||||
},
|
||||
schema::{accounts, custom_emojis, media_attachments, posts, posts_custom_emojis},
|
||||
with_connection,
|
||||
};
|
||||
use kitsune_type::ap::{
|
||||
actor::{Actor, PublicKey},
|
||||
|
@ -22,7 +23,6 @@ use kitsune_type::ap::{
|
|||
};
|
||||
use kitsune_util::try_join;
|
||||
use mime::Mime;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use std::{future::Future, str::FromStr};
|
||||
|
||||
pub trait IntoObject {
|
||||
|
@ -101,56 +101,51 @@ impl IntoObject for Post {
|
|||
return Err(Error::NotFound);
|
||||
}
|
||||
|
||||
let (account, in_reply_to, mentions, emojis, attachment_stream) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
let account_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let (account, in_reply_to, mentions, emojis, attachment_stream) =
|
||||
with_connection!(state.db_pool, |db_conn| {
|
||||
let account_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
let in_reply_to_fut =
|
||||
OptionFuture::from(self.in_reply_to_id.map(|in_reply_to_id| {
|
||||
posts::table
|
||||
.find(in_reply_to_id)
|
||||
.select(posts::url)
|
||||
.get_result(db_conn)
|
||||
}))
|
||||
.map(Option::transpose);
|
||||
let in_reply_to_fut =
|
||||
OptionFuture::from(self.in_reply_to_id.map(|in_reply_to_id| {
|
||||
posts::table
|
||||
.find(in_reply_to_id)
|
||||
.select(posts::url)
|
||||
.get_result(db_conn)
|
||||
}))
|
||||
.map(Option::transpose);
|
||||
|
||||
let mentions_fut = Mention::belonging_to(&self)
|
||||
.inner_join(accounts::table)
|
||||
.select((Mention::as_select(), Account::as_select()))
|
||||
.load::<(Mention, Account)>(db_conn);
|
||||
let mentions_fut = Mention::belonging_to(&self)
|
||||
.inner_join(accounts::table)
|
||||
.select((Mention::as_select(), Account::as_select()))
|
||||
.load::<(Mention, Account)>(db_conn);
|
||||
|
||||
let custom_emojis_fut = custom_emojis::table
|
||||
.inner_join(posts_custom_emojis::table)
|
||||
.inner_join(media_attachments::table)
|
||||
.filter(posts_custom_emojis::post_id.eq(self.id))
|
||||
.select((
|
||||
CustomEmoji::as_select(),
|
||||
PostCustomEmoji::as_select(),
|
||||
DbMediaAttachment::as_select(),
|
||||
))
|
||||
.load::<(CustomEmoji, PostCustomEmoji, DbMediaAttachment)>(db_conn);
|
||||
let custom_emojis_fut = custom_emojis::table
|
||||
.inner_join(posts_custom_emojis::table)
|
||||
.inner_join(media_attachments::table)
|
||||
.filter(posts_custom_emojis::post_id.eq(self.id))
|
||||
.select((
|
||||
CustomEmoji::as_select(),
|
||||
PostCustomEmoji::as_select(),
|
||||
DbMediaAttachment::as_select(),
|
||||
))
|
||||
.load::<(CustomEmoji, PostCustomEmoji, DbMediaAttachment)>(db_conn);
|
||||
|
||||
let attachment_stream_fut = PostMediaAttachment::belonging_to(&self)
|
||||
.inner_join(media_attachments::table)
|
||||
.select(DbMediaAttachment::as_select())
|
||||
.load_stream::<DbMediaAttachment>(db_conn);
|
||||
let attachment_stream_fut = PostMediaAttachment::belonging_to(&self)
|
||||
.inner_join(media_attachments::table)
|
||||
.select(DbMediaAttachment::as_select())
|
||||
.load_stream::<DbMediaAttachment>(db_conn);
|
||||
|
||||
try_join!(
|
||||
account_fut,
|
||||
in_reply_to_fut,
|
||||
mentions_fut,
|
||||
custom_emojis_fut,
|
||||
attachment_stream_fut
|
||||
)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(
|
||||
account_fut,
|
||||
in_reply_to_fut,
|
||||
mentions_fut,
|
||||
custom_emojis_fut,
|
||||
attachment_stream_fut
|
||||
)
|
||||
})?;
|
||||
|
||||
let attachment = attachment_stream
|
||||
.map_err(Error::from)
|
||||
|
@ -197,34 +192,28 @@ impl IntoObject for Account {
|
|||
type Output = Actor;
|
||||
|
||||
async fn into_object(self, state: State<'_>) -> Result<Self::Output> {
|
||||
let (icon, image) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
// These calls also probably allocate two cocnnections. ugh.
|
||||
let icon_fut = OptionFuture::from(self.avatar_id.map(|avatar_id| {
|
||||
media_attachments::table
|
||||
.find(avatar_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|media_attachment| media_attachment.into_object(state))
|
||||
}))
|
||||
.map(Option::transpose);
|
||||
let (icon, image) = with_connection!(state.db_pool, |db_conn| {
|
||||
// These calls also probably allocate two cocnnections. ugh.
|
||||
let icon_fut = OptionFuture::from(self.avatar_id.map(|avatar_id| {
|
||||
media_attachments::table
|
||||
.find(avatar_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|media_attachment| media_attachment.into_object(state))
|
||||
}))
|
||||
.map(Option::transpose);
|
||||
|
||||
let image_fut = OptionFuture::from(self.header_id.map(|header_id| {
|
||||
media_attachments::table
|
||||
.find(header_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|media_attachment| media_attachment.into_object(state))
|
||||
}))
|
||||
.map(Option::transpose);
|
||||
let image_fut = OptionFuture::from(self.header_id.map(|header_id| {
|
||||
media_attachments::table
|
||||
.find(header_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|media_attachment| media_attachment.into_object(state))
|
||||
}))
|
||||
.map(Option::transpose);
|
||||
|
||||
try_join!(icon_fut, image_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(icon_fut, image_fut)
|
||||
})?;
|
||||
|
||||
let user_url = state.service.url.user_url(self.id);
|
||||
let inbox = state.service.url.inbox_url(self.id);
|
||||
|
@ -269,17 +258,14 @@ impl IntoObject for CustomEmoji {
|
|||
Some(_) => Err(Error::NotFound),
|
||||
}?;
|
||||
|
||||
let icon = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
media_attachments::table
|
||||
.find(self.media_attachment_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|media_attachment| media_attachment.into_object(state))
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let icon = with_connection!(state.db_pool, |db_conn| {
|
||||
media_attachments::table
|
||||
.find(self.media_attachment_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|media_attachment| media_attachment.into_object(state))
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(Emoji {
|
||||
context: ap_context(),
|
||||
|
|
|
@ -8,6 +8,7 @@ use kitsune_core::traits::Fetcher as _;
|
|||
use kitsune_db::{
|
||||
model::{account::Account, media_attachment::MediaAttachment},
|
||||
schema::{accounts, media_attachments},
|
||||
with_connection_panicky,
|
||||
};
|
||||
use kitsune_federation_filter::FederationFilter;
|
||||
use kitsune_http_client::Client;
|
||||
|
@ -15,7 +16,6 @@ use kitsune_search::NoopSearchService;
|
|||
use kitsune_test::{database_test, language_detection_config};
|
||||
use kitsune_webfinger::Webfinger;
|
||||
use pretty_assertions::assert_eq;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use std::sync::Arc;
|
||||
use tower::service_fn;
|
||||
|
||||
|
@ -92,15 +92,14 @@ async fn fetch_emoji() {
|
|||
assert_eq!(emoji.shortcode, "Blobhaj");
|
||||
assert_eq!(emoji.domain, Some(String::from("corteximplant.com")));
|
||||
|
||||
let media_attachment = db_pool
|
||||
.with_connection(|db_conn| {
|
||||
let media_attachment =
|
||||
with_connection_panicky!(db_pool, |db_conn| {
|
||||
media_attachments::table
|
||||
.find(emoji.media_attachment_id)
|
||||
.select(MediaAttachment::as_select())
|
||||
.get_result::<MediaAttachment>(db_conn)
|
||||
.scoped()
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.expect("Get media attachment");
|
||||
|
||||
assert_eq!(
|
||||
|
@ -149,16 +148,14 @@ async fn fetch_note() {
|
|||
"https://corteximplant.com/users/0x0/statuses/109501674056556919"
|
||||
);
|
||||
|
||||
let author = db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.find(note.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result::<Account>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.expect("Get author");
|
||||
let author = with_connection_panicky!(db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(note.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result::<Account>(db_conn)
|
||||
.await
|
||||
})
|
||||
.expect("Get author");
|
||||
|
||||
assert_eq!(author.username, "0x0");
|
||||
assert_eq!(author.url, "https://corteximplant.com/users/0x0");
|
||||
|
|
|
@ -6,7 +6,7 @@ version.workspace = true
|
|||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
enum_dispatch = "0.3.12"
|
||||
enum_dispatch = "0.3.13"
|
||||
moka = { version = "0.12.5", features = ["future"] }
|
||||
multiplex-pool = { path = "../../lib/multiplex-pool" }
|
||||
redis = { version = "0.25.2", default-features = false, features = [
|
||||
|
@ -20,7 +20,7 @@ tracing = "0.1.40"
|
|||
typed-builder = "0.18.1"
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -6,7 +6,7 @@ edition.workspace = true
|
|||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
enum_dispatch = "0.3.12"
|
||||
enum_dispatch = "0.3.13"
|
||||
http = "1.1.0"
|
||||
kitsune-http-client = { path = "../kitsune-http-client" }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
|
|
|
@ -6,11 +6,11 @@ version.workspace = true
|
|||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
eyre = "0.6.12"
|
||||
isolang = { version = "2.4.0", features = ["serde"] }
|
||||
miette = "7.2.0"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
smol_str = { version = "0.2.1", features = ["serde"] }
|
||||
tokio = { version = "1.36.0", features = ["fs"] }
|
||||
tokio = { version = "1.37.0", features = ["fs"] }
|
||||
toml = { version = "0.8.12", default-features = false, features = ["parse"] }
|
||||
|
||||
[lints]
|
||||
|
|
|
@ -15,7 +15,7 @@ pub mod server;
|
|||
pub mod storage;
|
||||
pub mod url;
|
||||
|
||||
use miette::{Context, IntoDiagnostic};
|
||||
use eyre::{Result, WrapErr};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
use tokio::fs;
|
||||
|
@ -41,17 +41,14 @@ pub struct Configuration {
|
|||
}
|
||||
|
||||
impl Configuration {
|
||||
pub async fn load<P>(path: P) -> miette::Result<Self>
|
||||
pub async fn load<P>(path: P) -> Result<Self>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
let content = fs::read_to_string(path)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("Couldn't read configuration file")?;
|
||||
|
||||
toml::from_str(&content)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to parse configuration file")
|
||||
toml::from_str(&content).wrap_err("Failed to parse configuration file")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ build = "build.rs"
|
|||
[dependencies]
|
||||
async-trait = "0.1.79"
|
||||
const_format = "0.2.32"
|
||||
eyre = "0.6.12"
|
||||
http = "1.1.0"
|
||||
kitsune-db = { path = "../kitsune-db" }
|
||||
kitsune-messaging = { path = "../kitsune-messaging" }
|
||||
|
|
|
@ -1,10 +1,7 @@
|
|||
use http::StatusCode;
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error as StdError;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type BoxError = Box<dyn StdError + Send + Sync>;
|
||||
|
||||
macro_rules! http_error {
|
||||
($($variant_name:ident => $status_code:path),*$(,)?) => {
|
||||
#[derive(Debug, Error)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::error::BoxError;
|
||||
use async_trait::async_trait;
|
||||
use eyre::Result;
|
||||
use kitsune_db::model::{account::Account, favourite::Favourite, follower::Follow, post::Post};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
@ -22,12 +22,12 @@ pub enum Action {
|
|||
|
||||
#[async_trait]
|
||||
pub trait Deliverer: Send + Sync + 'static {
|
||||
async fn deliver(&self, action: Action) -> Result<(), BoxError>;
|
||||
async fn deliver(&self, action: Action) -> Result<()>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl Deliverer for Arc<dyn Deliverer> {
|
||||
async fn deliver(&self, action: Action) -> Result<(), BoxError> {
|
||||
async fn deliver(&self, action: Action) -> Result<()> {
|
||||
(**self).deliver(action).await
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ impl<T> Deliverer for Vec<T>
|
|||
where
|
||||
T: Deliverer,
|
||||
{
|
||||
async fn deliver(&self, action: Action) -> Result<(), BoxError> {
|
||||
async fn deliver(&self, action: Action) -> Result<()> {
|
||||
for deliverer in self {
|
||||
deliverer.deliver(action.clone()).await?;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::Resolver;
|
||||
use crate::error::BoxError;
|
||||
use async_trait::async_trait;
|
||||
use eyre::Result;
|
||||
use kitsune_db::model::{account::Account, custom_emoji::CustomEmoji, post::Post};
|
||||
use std::sync::Arc;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
@ -8,7 +8,7 @@ use typed_builder::TypedBuilder;
|
|||
#[derive(Clone, Copy, Debug, TypedBuilder)]
|
||||
/// Options passed to the fetcher
|
||||
pub struct AccountFetchOptions<'a> {
|
||||
/// Prefetched WebFinger `acct` URI
|
||||
/// Prefetched Webfinger `acct` URI
|
||||
#[builder(default, setter(strip_option))]
|
||||
pub acct: Option<(&'a str, &'a str)>,
|
||||
|
||||
|
@ -50,14 +50,11 @@ impl<'a> From<&'a str> for PostFetchOptions<'a> {
|
|||
pub trait Fetcher: Send + Sync + 'static {
|
||||
fn resolver(&self) -> Arc<dyn Resolver>;
|
||||
|
||||
async fn fetch_account(
|
||||
&self,
|
||||
opts: AccountFetchOptions<'_>,
|
||||
) -> Result<Option<Account>, BoxError>;
|
||||
async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result<Option<Account>>;
|
||||
|
||||
async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>, BoxError>;
|
||||
async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>>;
|
||||
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result<Option<Post>, BoxError>;
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result<Option<Post>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -66,18 +63,15 @@ impl Fetcher for Arc<dyn Fetcher> {
|
|||
(**self).resolver()
|
||||
}
|
||||
|
||||
async fn fetch_account(
|
||||
&self,
|
||||
opts: AccountFetchOptions<'_>,
|
||||
) -> Result<Option<Account>, BoxError> {
|
||||
async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result<Option<Account>> {
|
||||
(**self).fetch_account(opts).await
|
||||
}
|
||||
|
||||
async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>, BoxError> {
|
||||
async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>> {
|
||||
(**self).fetch_emoji(url).await
|
||||
}
|
||||
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result<Option<Post>, BoxError> {
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result<Option<Post>> {
|
||||
(**self).fetch_post(opts).await
|
||||
}
|
||||
}
|
||||
|
@ -91,10 +85,7 @@ where
|
|||
Arc::new(self.iter().map(Fetcher::resolver).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
async fn fetch_account(
|
||||
&self,
|
||||
opts: AccountFetchOptions<'_>,
|
||||
) -> Result<Option<Account>, BoxError> {
|
||||
async fn fetch_account(&self, opts: AccountFetchOptions<'_>) -> Result<Option<Account>> {
|
||||
for fetcher in self {
|
||||
if let Some(account) = fetcher.fetch_account(opts).await? {
|
||||
return Ok(Some(account));
|
||||
|
@ -104,7 +95,7 @@ where
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>, BoxError> {
|
||||
async fn fetch_emoji(&self, url: &str) -> Result<Option<CustomEmoji>> {
|
||||
for fetcher in self {
|
||||
if let Some(emoji) = fetcher.fetch_emoji(url).await? {
|
||||
return Ok(Some(emoji));
|
||||
|
@ -114,7 +105,7 @@ where
|
|||
Ok(None)
|
||||
}
|
||||
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result<Option<Post>, BoxError> {
|
||||
async fn fetch_post(&self, opts: PostFetchOptions<'_>) -> Result<Option<Post>> {
|
||||
for fetcher in self {
|
||||
if let Some(post) = fetcher.fetch_post(opts).await? {
|
||||
return Ok(Some(post));
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::error::BoxError;
|
||||
use async_trait::async_trait;
|
||||
use eyre::Result;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -20,7 +20,7 @@ pub trait Resolver: Send + Sync + 'static {
|
|||
&self,
|
||||
username: &str,
|
||||
domain: &str,
|
||||
) -> Result<Option<AccountResource>, BoxError>;
|
||||
) -> Result<Option<AccountResource>>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
@ -29,7 +29,7 @@ impl Resolver for Arc<dyn Resolver> {
|
|||
&self,
|
||||
username: &str,
|
||||
domain: &str,
|
||||
) -> Result<Option<AccountResource>, BoxError> {
|
||||
) -> Result<Option<AccountResource>> {
|
||||
(**self).resolve_account(username, domain).await
|
||||
}
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ where
|
|||
&self,
|
||||
username: &str,
|
||||
domain: &str,
|
||||
) -> Result<Option<AccountResource>, BoxError> {
|
||||
) -> Result<Option<AccountResource>> {
|
||||
for resolver in self {
|
||||
if let Some(resource) = resolver.resolve_account(username, domain).await? {
|
||||
return Ok(Some(resource));
|
||||
|
|
|
@ -24,16 +24,20 @@ iso8601-timestamp = { version = "0.2.17", features = ["diesel-pg"] }
|
|||
kitsune-config = { path = "../kitsune-config" }
|
||||
kitsune-language = { path = "../kitsune-language" }
|
||||
kitsune-type = { path = "../kitsune-type" }
|
||||
miette = "7.2.0"
|
||||
num-derive = "0.4.2"
|
||||
num-traits = "0.2.18"
|
||||
rustls = "=0.22.2"
|
||||
rustls = { version = "0.23.4", default-features = false, features = [
|
||||
"logging",
|
||||
"ring",
|
||||
"std",
|
||||
"tls12",
|
||||
] }
|
||||
rustls-native-certs = "0.7.0"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
simd-json = "0.13.9"
|
||||
speedy-uuid = { path = "../../lib/speedy-uuid", features = ["diesel"] }
|
||||
thiserror = "1.0.58"
|
||||
tokio = { version = "1.36.0", features = ["rt"] }
|
||||
tokio = { version = "1.37.0", features = ["rt"] }
|
||||
tokio-postgres = "0.7.10"
|
||||
tokio-postgres-rustls = "0.11.1"
|
||||
tracing = "0.1.40"
|
||||
|
@ -42,7 +46,7 @@ typed-builder = "0.18.1"
|
|||
|
||||
[dev-dependencies]
|
||||
kitsune-test = { path = "../kitsune-test" }
|
||||
tokio = { version = "1.36.0", features = ["macros"] }
|
||||
tokio = { version = "1.37.0", features = ["macros"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use core::fmt;
|
||||
use diesel_async::pooled_connection::bb8;
|
||||
use miette::Diagnostic;
|
||||
use std::error::Error as StdError;
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -37,7 +36,7 @@ impl fmt::Display for IsoCodeConversionError {
|
|||
|
||||
impl StdError for IsoCodeConversionError {}
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Blocking(#[from] blowocking::Error),
|
||||
|
|
|
@ -11,10 +11,11 @@ use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness};
|
|||
use kitsune_config::database::Configuration as DatabaseConfig;
|
||||
use tracing_log::LogTracer;
|
||||
|
||||
pub use crate::{
|
||||
error::{Error, Result},
|
||||
pool::{PgPool, PoolError},
|
||||
};
|
||||
pub type PgPool = Pool<AsyncPgConnection>;
|
||||
|
||||
pub use crate::error::{Error, Result};
|
||||
#[doc(hidden)]
|
||||
pub use diesel_async;
|
||||
|
||||
mod error;
|
||||
mod pool;
|
||||
|
@ -75,5 +76,5 @@ pub async fn connect(config: &DatabaseConfig) -> Result<PgPool> {
|
|||
.await?;
|
||||
}
|
||||
|
||||
Ok(pool.into())
|
||||
Ok(pool)
|
||||
}
|
||||
|
|
|
@ -1,70 +1,41 @@
|
|||
use diesel_async::{
|
||||
pooled_connection::bb8::{self, Pool},
|
||||
scoped_futures::{ScopedFutureExt, ScopedFutureWrapper},
|
||||
AsyncConnection, AsyncPgConnection,
|
||||
};
|
||||
use miette::Diagnostic;
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
future::Future,
|
||||
};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
pub enum PoolError<E>
|
||||
where
|
||||
E: Display + Debug,
|
||||
{
|
||||
#[error(transparent)]
|
||||
Pool(#[from] bb8::RunError),
|
||||
|
||||
#[error("{0}")]
|
||||
User(E),
|
||||
#[macro_export]
|
||||
macro_rules! with_connection {
|
||||
($pool:expr, |$conn_name:ident| $code:block) => {{
|
||||
let mut conn = $pool.get().await?;
|
||||
let $conn_name = &mut *conn;
|
||||
async { $code }.await
|
||||
}};
|
||||
}
|
||||
|
||||
/// Small wrapper around [`Pool<AsyncPgConnection>`]
|
||||
///
|
||||
/// The intent of this API is to encourage and make short-lived ownership of connections easier.
|
||||
/// With the traditional RAII guard based approach, it is rather hard (and/or ugly) to define clear scopes for connections
|
||||
/// (especially when they are used *a lot* throughout the code).
|
||||
///
|
||||
/// The API of this wrapper is based on closures, meaning you have no choice but to be aware of the scope.
|
||||
/// And the extra level of indentation this forces is supposed to coerce users to keep the scope as small as possible.
|
||||
#[derive(Clone)]
|
||||
pub struct PgPool {
|
||||
inner: Pool<AsyncPgConnection>,
|
||||
#[macro_export]
|
||||
macro_rules! catch_error {
|
||||
($($tt:tt)*) => {{
|
||||
let result: ::std::result::Result<_, ::diesel_async::pooled_connection::bb8::RunError> = async {
|
||||
Ok({ $($tt)* })
|
||||
}.await;
|
||||
result
|
||||
}};
|
||||
}
|
||||
|
||||
impl PgPool {
|
||||
/// Run the code inside a context with a database connection
|
||||
pub async fn with_connection<'a, F, Fut, T, E>(&self, func: F) -> Result<T, PoolError<E>>
|
||||
where
|
||||
for<'conn> F: FnOnce(&'conn mut AsyncPgConnection) -> ScopedFutureWrapper<'conn, 'a, Fut>,
|
||||
Fut: Future<Output = Result<T, E>>,
|
||||
E: Display + Debug,
|
||||
{
|
||||
let mut conn = self.inner.get().await?;
|
||||
func(&mut conn).await.map_err(PoolError::User)
|
||||
}
|
||||
|
||||
/// Run the code inside a context with a database transaction
|
||||
pub async fn with_transaction<'a, F, Fut, T, E>(&self, func: F) -> Result<T, PoolError<E>>
|
||||
where
|
||||
for<'conn> F:
|
||||
FnOnce(&'conn mut AsyncPgConnection) -> ScopedFutureWrapper<'conn, 'a, Fut> + Send,
|
||||
Fut: Future<Output = Result<T, E>> + Send,
|
||||
T: Send,
|
||||
E: From<diesel::result::Error> + Debug + Display + Send,
|
||||
{
|
||||
let mut conn = self.inner.get().await?;
|
||||
conn.transaction(|conn| func(conn).scope_boxed())
|
||||
.await
|
||||
.map_err(PoolError::User)
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! with_connection_panicky {
|
||||
($pool:expr, $($other:tt)*) => {{
|
||||
$crate::catch_error!($crate::with_connection!($pool, $($other)*)).unwrap()
|
||||
}};
|
||||
}
|
||||
|
||||
impl From<Pool<AsyncPgConnection>> for PgPool {
|
||||
fn from(value: Pool<AsyncPgConnection>) -> Self {
|
||||
Self { inner: value }
|
||||
}
|
||||
#[macro_export]
|
||||
macro_rules! with_transaction {
|
||||
($pool:expr, |$conn_name:ident| $code:block) => {{
|
||||
use $crate::diesel_async::AsyncConnection;
|
||||
|
||||
let mut conn = $pool.get().await?;
|
||||
conn.transaction(|conn| {
|
||||
Box::pin(async move {
|
||||
let $conn_name = conn;
|
||||
$code
|
||||
})
|
||||
})
|
||||
.await
|
||||
}};
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use diesel::SelectableHelper;
|
||||
use diesel_async::{scoped_futures::ScopedFutureExt, AsyncPgConnection, RunQueryDsl};
|
||||
use diesel_async::{AsyncPgConnection, RunQueryDsl};
|
||||
use kitsune_db::{
|
||||
model::{
|
||||
account::{Account, ActorType, NewAccount},
|
||||
user::{NewUser, User},
|
||||
},
|
||||
schema::{accounts, users},
|
||||
with_connection_panicky,
|
||||
};
|
||||
use kitsune_test::database_test;
|
||||
use speedy_uuid::Uuid;
|
||||
|
@ -65,30 +66,22 @@ async fn create_user(conn: &mut AsyncPgConnection, username: &str) -> Result<Use
|
|||
#[tokio::test]
|
||||
async fn accounts_username() {
|
||||
database_test(|db_pool| async move {
|
||||
db_pool
|
||||
.with_connection(|conn| {
|
||||
async move {
|
||||
let initial_insert = create_account(conn, "aumetra").await;
|
||||
assert!(initial_insert.is_ok());
|
||||
with_connection_panicky!(db_pool, |conn| {
|
||||
let initial_insert = create_account(conn, "aumetra").await;
|
||||
assert!(initial_insert.is_ok());
|
||||
|
||||
let case_mutation = create_account(conn, "AuMeTrA").await;
|
||||
assert!(case_mutation.is_err());
|
||||
let case_mutation = create_account(conn, "AuMeTrA").await;
|
||||
assert!(case_mutation.is_err());
|
||||
|
||||
let unicode_mutation_1 = create_account(conn, "äumeträ").await;
|
||||
assert!(unicode_mutation_1.is_err());
|
||||
let unicode_mutation_1 = create_account(conn, "äumeträ").await;
|
||||
assert!(unicode_mutation_1.is_err());
|
||||
|
||||
let unicode_mutation_2 = create_account(conn, "🅰umetr🅰").await;
|
||||
assert!(unicode_mutation_2.is_err());
|
||||
let unicode_mutation_2 = create_account(conn, "🅰umetr🅰").await;
|
||||
assert!(unicode_mutation_2.is_err());
|
||||
|
||||
let unicode_case_mutation = create_account(conn, "🅰UMETR🅰").await;
|
||||
assert!(unicode_case_mutation.is_err());
|
||||
|
||||
Result::Ok(())
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let unicode_case_mutation = create_account(conn, "🅰UMETR🅰").await;
|
||||
assert!(unicode_case_mutation.is_err());
|
||||
});
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
@ -96,30 +89,22 @@ async fn accounts_username() {
|
|||
#[tokio::test]
|
||||
async fn users_username() {
|
||||
database_test(|db_pool| async move {
|
||||
db_pool
|
||||
.with_connection(|conn| {
|
||||
async move {
|
||||
let initial_insert = create_user(conn, "aumetra").await;
|
||||
assert!(initial_insert.is_ok());
|
||||
with_connection_panicky!(db_pool, |conn| {
|
||||
let initial_insert = create_user(conn, "aumetra").await;
|
||||
assert!(initial_insert.is_ok());
|
||||
|
||||
let case_mutation = create_user(conn, "AuMeTrA").await;
|
||||
assert!(case_mutation.is_err());
|
||||
let case_mutation = create_user(conn, "AuMeTrA").await;
|
||||
assert!(case_mutation.is_err());
|
||||
|
||||
let unicode_mutation_1 = create_user(conn, "äumeträ").await;
|
||||
assert!(unicode_mutation_1.is_err());
|
||||
let unicode_mutation_1 = create_user(conn, "äumeträ").await;
|
||||
assert!(unicode_mutation_1.is_err());
|
||||
|
||||
let unicode_mutation_2 = create_user(conn, "🅰umetr🅰").await;
|
||||
assert!(unicode_mutation_2.is_err());
|
||||
let unicode_mutation_2 = create_user(conn, "🅰umetr🅰").await;
|
||||
assert!(unicode_mutation_2.is_err());
|
||||
|
||||
let unicode_case_mutation = create_user(conn, "🅰UMETR🅰").await;
|
||||
assert!(unicode_case_mutation.is_err());
|
||||
|
||||
Result::Ok(())
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let unicode_case_mutation = create_user(conn, "🅰UMETR🅰").await;
|
||||
assert!(unicode_case_mutation.is_err());
|
||||
});
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ diesel = "2.1.5"
|
|||
diesel-async = "0.4.1"
|
||||
kitsune-db = { path = "../kitsune-db" }
|
||||
kitsune-url = { path = "../kitsune-url" }
|
||||
lettre = { version = "0.11.5", default-features = false, features = [
|
||||
lettre = { version = "0.11.6", default-features = false, features = [
|
||||
"builder",
|
||||
"hostname",
|
||||
"pool",
|
||||
|
@ -24,13 +24,11 @@ lettre = { version = "0.11.5", default-features = false, features = [
|
|||
"tokio1-rustls-tls",
|
||||
"tracing",
|
||||
] }
|
||||
miette = "7.2.0"
|
||||
mrml = { version = "3.1.3", default-features = false, features = [
|
||||
"orderedmap",
|
||||
"parse",
|
||||
"render",
|
||||
] }
|
||||
scoped-futures = "0.1.3"
|
||||
speedy-uuid = { path = "../../lib/speedy-uuid" }
|
||||
thiserror = "1.0.58"
|
||||
typed-builder = "0.18.1"
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
use diesel_async::pooled_connection::bb8::RunError as DatabasePoolError;
|
||||
use miette::Diagnostic;
|
||||
use std::{
|
||||
error::Error as StdError,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
use std::{error::Error as StdError, fmt::Debug};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type BoxError = Box<dyn StdError + Send + Sync>;
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Address(#[from] lettre::address::AddressError),
|
||||
|
@ -35,15 +31,3 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Rendering(#[from] mrml::prelude::render::Error),
|
||||
}
|
||||
|
||||
impl<E> From<kitsune_db::PoolError<E>> for Error
|
||||
where
|
||||
E: Into<Error> + Debug + Display,
|
||||
{
|
||||
fn from(value: kitsune_db::PoolError<E>) -> Self {
|
||||
match value {
|
||||
kitsune_db::PoolError::Pool(err) => err.into(),
|
||||
kitsune_db::PoolError::User(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
use crate::{error::Result, mails::confirm_account::ConfirmAccount, MailSender};
|
||||
use diesel::{ExpressionMethods, NullableExpressionMethods, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_db::{function::now, model::user::User, schema::users, PgPool};
|
||||
use kitsune_db::{function::now, model::user::User, schema::users, with_connection, PgPool};
|
||||
use kitsune_url::UrlService;
|
||||
use lettre::{AsyncSmtpTransport, Tokio1Executor};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -27,31 +26,27 @@ impl Mailing {
|
|||
}
|
||||
|
||||
pub async fn mark_as_confirmed(&self, user_id: Uuid) -> Result<()> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::update(users::table.find(user_id))
|
||||
.set(users::confirmed_at.eq(now().nullable()))
|
||||
.execute(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::update(users::table.find(user_id))
|
||||
.set(users::confirmed_at.eq(now().nullable()))
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn mark_as_confirmed_by_token(&self, confirmation_token: &str) -> Result<()> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::update(
|
||||
users::table
|
||||
.filter(users::confirmation_token.eq(confirmation_token))
|
||||
.filter(users::confirmed_at.is_null()),
|
||||
)
|
||||
.set(users::confirmed_at.eq(now().nullable()))
|
||||
.execute(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::update(
|
||||
users::table
|
||||
.filter(users::confirmation_token.eq(confirmation_token))
|
||||
.filter(users::confirmed_at.is_null()),
|
||||
)
|
||||
.set(users::confirmed_at.eq(now().nullable()))
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use diesel::{OptionalExtension, QueryDsl};
|
||||
use diesel_async::{pooled_connection::bb8, scoped_futures::ScopedFutureExt, RunQueryDsl};
|
||||
use diesel_async::{pooled_connection::bb8, RunQueryDsl};
|
||||
use embed_sdk::EmbedWithExpire;
|
||||
use http::{Method, Request};
|
||||
use iso8601_timestamp::Timestamp;
|
||||
|
@ -7,13 +7,13 @@ use kitsune_db::{
|
|||
json::Json,
|
||||
model::link_preview::{ConflictLinkPreviewChangeset, LinkPreview, NewLinkPreview},
|
||||
schema::link_previews,
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_http_client::Client as HttpClient;
|
||||
use once_cell::sync::Lazy;
|
||||
use scraper::{Html, Selector};
|
||||
use smol_str::SmolStr;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::fmt::Debug;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
pub use embed_sdk;
|
||||
|
@ -46,18 +46,6 @@ pub enum Error {
|
|||
Pool(#[from] bb8::RunError),
|
||||
}
|
||||
|
||||
impl<E> From<kitsune_db::PoolError<E>> for Error
|
||||
where
|
||||
E: Into<Error> + Debug + Display,
|
||||
{
|
||||
fn from(value: kitsune_db::PoolError<E>) -> Self {
|
||||
match value {
|
||||
kitsune_db::PoolError::Pool(err) => err.into(),
|
||||
kitsune_db::PoolError::User(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, TypedBuilder)]
|
||||
pub struct Client {
|
||||
db_pool: PgPool,
|
||||
|
@ -83,19 +71,13 @@ impl Client {
|
|||
}
|
||||
|
||||
pub async fn fetch_embed(&self, url: &str) -> Result<LinkPreview<Embed>> {
|
||||
let embed_data = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
link_previews::table
|
||||
.find(url)
|
||||
.get_result::<LinkPreview<Embed>>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let embed_data = with_connection!(self.db_pool, |db_conn| {
|
||||
link_previews::table
|
||||
.find(url)
|
||||
.get_result::<LinkPreview<Embed>>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(data) = embed_data {
|
||||
if data.expires_at > Timestamp::now_utc() {
|
||||
|
@ -112,25 +94,22 @@ impl Client {
|
|||
let response = HttpClient::execute(&self.http_client, request).await?;
|
||||
let (expires_at, embed_data): EmbedWithExpire = response.json().await?;
|
||||
|
||||
let embed_data = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::insert_into(link_previews::table)
|
||||
.values(NewLinkPreview {
|
||||
url,
|
||||
embed_data: Json(&embed_data),
|
||||
expires_at,
|
||||
})
|
||||
.on_conflict(link_previews::url)
|
||||
.do_update()
|
||||
.set(ConflictLinkPreviewChangeset {
|
||||
embed_data: Json(&embed_data),
|
||||
expires_at,
|
||||
})
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let embed_data = with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::insert_into(link_previews::table)
|
||||
.values(NewLinkPreview {
|
||||
url,
|
||||
embed_data: Json(&embed_data),
|
||||
expires_at,
|
||||
})
|
||||
.on_conflict(link_previews::url)
|
||||
.do_update()
|
||||
.set(ConflictLinkPreviewChangeset {
|
||||
embed_data: Json(&embed_data),
|
||||
expires_at,
|
||||
})
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(embed_data)
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ license.workspace = true
|
|||
globset = "0.4.14"
|
||||
kitsune-config = { path = "../kitsune-config" }
|
||||
kitsune-type = { path = "../kitsune-type" }
|
||||
miette = "7.2.0"
|
||||
thiserror = "1.0.58"
|
||||
url = "2.5.0"
|
||||
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Glob(#[from] globset::Error),
|
||||
|
|
|
@ -48,7 +48,7 @@ tower-http = { version = "0.5.2", features = [
|
|||
] }
|
||||
|
||||
[dev-dependencies]
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -10,12 +10,11 @@ athena = { path = "../../lib/athena" }
|
|||
derive_more = { version = "1.0.0-beta.6", features = ["from"] }
|
||||
diesel = "2.1.5"
|
||||
diesel-async = "0.4.1"
|
||||
eyre = "0.6.12"
|
||||
futures-util = "0.3.30"
|
||||
kitsune-core = { path = "../kitsune-core" }
|
||||
kitsune-db = { path = "../kitsune-db" }
|
||||
kitsune-email = { path = "../kitsune-email" }
|
||||
miette = "7.2.0"
|
||||
scoped-futures = "0.1.3"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
speedy-uuid = { path = "../../lib/speedy-uuid" }
|
||||
tracing = "0.1.40"
|
||||
|
|
|
@ -2,8 +2,7 @@ use crate::{JobRunnerContext, Runnable};
|
|||
use diesel::{OptionalExtension, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -14,32 +13,23 @@ pub struct DeliverAccept {
|
|||
|
||||
impl Runnable for DeliverAccept {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
#[instrument(skip_all, fields(follow_id = %self.follow_id))]
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let follow = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow = with_connection!(ctx.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(follow) = follow else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(Action::AcceptFollow(follow))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(Action::AcceptFollow(follow)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ use athena::Runnable;
|
|||
use diesel::{OptionalExtension, QueryDsl, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::post::Post, schema::posts};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::post::Post, schema::posts, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -15,33 +14,24 @@ pub struct DeliverCreate {
|
|||
|
||||
impl Runnable for DeliverCreate {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
#[instrument(skip_all, fields(post_id = %self.post_id))]
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let post = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts::table
|
||||
.find(self.post_id)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post = with_connection!(ctx.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(self.post_id)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(post) = post else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(Action::Create(post))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(Action::Create(post)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ use athena::Runnable;
|
|||
use diesel::{OptionalExtension, QueryDsl, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::post::Post, schema::posts};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::post::Post, schema::posts, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -15,41 +14,30 @@ pub struct DeliverDelete {
|
|||
|
||||
impl Runnable for DeliverDelete {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
#[instrument(skip_all, fields(post_id = %self.post_id))]
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let post = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts::table
|
||||
.find(self.post_id)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post = with_connection!(ctx.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(self.post_id)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(post) = post else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(Action::Delete(post))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(Action::Delete(post)).await?;
|
||||
|
||||
ctx.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::delete(posts::table.find(self.post_id))
|
||||
.execute(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(ctx.db_pool, |db_conn| {
|
||||
diesel::delete(posts::table.find(self.post_id))
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ use athena::Runnable;
|
|||
use diesel::QueryDsl;
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::favourite::Favourite, schema::posts_favourites};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::favourite::Favourite, schema::posts_favourites, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -15,24 +14,18 @@ pub struct DeliverFavourite {
|
|||
|
||||
impl Runnable for DeliverFavourite {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
#[instrument(skip_all, fields(favourite_id = %self.favourite_id))]
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let favourite = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts_favourites::table
|
||||
.find(self.favourite_id)
|
||||
.get_result::<Favourite>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let favourite = with_connection!(ctx.db_pool, |db_conn| {
|
||||
posts_favourites::table
|
||||
.find(self.favourite_id)
|
||||
.get_result::<Favourite>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(Action::Favourite(favourite))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(Action::Favourite(favourite)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ use athena::Runnable;
|
|||
use diesel::{OptionalExtension, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -15,32 +14,23 @@ pub struct DeliverFollow {
|
|||
|
||||
impl Runnable for DeliverFollow {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
#[instrument(skip_all, fields(follow_id = %self.follow_id))]
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let follow = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow = with_connection!(ctx.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(follow) = follow else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(Action::Follow(follow))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(Action::Follow(follow)).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@ use crate::{JobRunnerContext, Runnable};
|
|||
use diesel::{OptionalExtension, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -14,40 +13,29 @@ pub struct DeliverReject {
|
|||
|
||||
impl Runnable for DeliverReject {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
#[instrument(skip_all, fields(follow_id = %self.follow_id))]
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let follow = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow = with_connection!(ctx.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(follow) = follow else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(Action::RejectFollow(follow))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(Action::RejectFollow(follow)).await?;
|
||||
|
||||
ctx.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::delete(accounts_follows::table.find(self.follow_id))
|
||||
.execute(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(ctx.db_pool, |db_conn| {
|
||||
diesel::delete(accounts_follows::table.find(self.follow_id))
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ use athena::Runnable;
|
|||
use diesel::{OptionalExtension, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::favourite::Favourite, schema::posts_favourites};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::favourite::Favourite, schema::posts_favourites, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -15,23 +14,17 @@ pub struct DeliverUnfavourite {
|
|||
|
||||
impl Runnable for DeliverUnfavourite {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
#[instrument(skip_all, fields(favourite_id = %self.favourite_id))]
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let favourite = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts_favourites::table
|
||||
.find(self.favourite_id)
|
||||
.get_result::<Favourite>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let favourite = with_connection!(ctx.db_pool, |db_conn| {
|
||||
posts_favourites::table
|
||||
.find(self.favourite_id)
|
||||
.get_result::<Favourite>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(favourite) = favourite else {
|
||||
return Ok(());
|
||||
|
@ -39,17 +32,14 @@ impl Runnable for DeliverUnfavourite {
|
|||
|
||||
ctx.deliverer
|
||||
.deliver(Action::Unfavourite(favourite))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
|
||||
ctx.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::delete(posts_favourites::table.find(self.favourite_id))
|
||||
.execute(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
|
||||
with_connection!(ctx.db_pool, |db_conn| {
|
||||
diesel::delete(posts_favourites::table.find(self.favourite_id))
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,7 @@ use athena::Runnable;
|
|||
use diesel::{OptionalExtension, QueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_core::traits::deliverer::Action;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::follower::Follow, schema::accounts_follows, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -15,39 +14,28 @@ pub struct DeliverUnfollow {
|
|||
|
||||
impl Runnable for DeliverUnfollow {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let follow = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow = with_connection!(ctx.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.find(self.follow_id)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(follow) = follow else {
|
||||
return Ok(());
|
||||
};
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(Action::Unfollow(follow))
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(Action::Unfollow(follow)).await?;
|
||||
|
||||
ctx.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::delete(accounts_follows::table.find(self.follow_id))
|
||||
.execute(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(ctx.db_pool, |db_conn| {
|
||||
diesel::delete(accounts_follows::table.find(self.follow_id))
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use kitsune_core::traits::deliverer::Action;
|
|||
use kitsune_db::{
|
||||
model::{account::Account, post::Post},
|
||||
schema::{accounts, posts},
|
||||
with_connection,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -25,25 +25,19 @@ pub struct DeliverUpdate {
|
|||
|
||||
impl Runnable for DeliverUpdate {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let action = match self.entity {
|
||||
UpdateEntity::Account => {
|
||||
let account = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts::table
|
||||
.find(self.id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let account = with_connection!(ctx.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(self.id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(account) = account else {
|
||||
return Ok(());
|
||||
|
@ -52,20 +46,14 @@ impl Runnable for DeliverUpdate {
|
|||
Action::UpdateAccount(account)
|
||||
}
|
||||
UpdateEntity::Status => {
|
||||
let post = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts::table
|
||||
.find(self.id)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post = with_connection!(ctx.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(self.id)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
let Some(post) = post else {
|
||||
return Ok(());
|
||||
|
@ -75,10 +63,7 @@ impl Runnable for DeliverUpdate {
|
|||
}
|
||||
};
|
||||
|
||||
ctx.deliverer
|
||||
.deliver(action)
|
||||
.await
|
||||
.map_err(|err| miette::Report::new_boxed(err.into()))?;
|
||||
ctx.deliverer.deliver(action).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -19,11 +19,9 @@ use kitsune_db::{
|
|||
json::Json,
|
||||
model::job_context::{JobContext, NewJobContext},
|
||||
schema::job_context,
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_email::MailingService;
|
||||
use miette::IntoDiagnostic;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
@ -57,7 +55,7 @@ pub enum Job {
|
|||
|
||||
impl Runnable for Job {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
match self {
|
||||
|
@ -89,37 +87,32 @@ impl KitsuneContextRepo {
|
|||
|
||||
impl JobContextRepository for KitsuneContextRepo {
|
||||
type JobContext = Job;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
type Stream = BoxStream<'static, Result<(Uuid, Self::JobContext), Self::Error>>;
|
||||
|
||||
async fn fetch_context<I>(&self, job_ids: I) -> Result<Self::Stream, Self::Error>
|
||||
where
|
||||
I: Iterator<Item = Uuid> + Send + 'static,
|
||||
{
|
||||
let stream = self
|
||||
.db_pool
|
||||
.with_connection(|conn| {
|
||||
job_context::table
|
||||
.filter(job_context::id.eq_any(job_ids))
|
||||
.load_stream::<JobContext<Job>>(conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let stream = with_connection!(self.db_pool, |conn| {
|
||||
job_context::table
|
||||
.filter(job_context::id.eq_any(job_ids))
|
||||
.load_stream::<JobContext<Job>>(conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(stream
|
||||
.map_ok(|ctx| (ctx.id, ctx.context.0))
|
||||
.map(IntoDiagnostic::into_diagnostic)
|
||||
.map_err(eyre::Report::from)
|
||||
.boxed())
|
||||
}
|
||||
|
||||
async fn remove_context(&self, job_id: Uuid) -> Result<(), Self::Error> {
|
||||
self.db_pool
|
||||
.with_connection(|conn| {
|
||||
diesel::delete(job_context::table.find(job_id))
|
||||
.execute(conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |conn| {
|
||||
diesel::delete(job_context::table.find(job_id))
|
||||
.execute(conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -129,17 +122,15 @@ impl JobContextRepository for KitsuneContextRepo {
|
|||
job_id: Uuid,
|
||||
context: Self::JobContext,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.db_pool
|
||||
.with_connection(|conn| {
|
||||
diesel::insert_into(job_context::table)
|
||||
.values(NewJobContext {
|
||||
id: job_id,
|
||||
context: Json(context),
|
||||
})
|
||||
.execute(conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |conn| {
|
||||
diesel::insert_into(job_context::table)
|
||||
.values(NewJobContext {
|
||||
id: job_id,
|
||||
context: Json(context),
|
||||
})
|
||||
.execute(conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,8 +2,7 @@ use crate::JobRunnerContext;
|
|||
use athena::Runnable;
|
||||
use diesel::{QueryDsl, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_db::{model::user::User, schema::users};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use kitsune_db::{model::user::User, schema::users, with_connection};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
|
@ -14,7 +13,7 @@ pub struct SendConfirmationMail {
|
|||
|
||||
impl Runnable for SendConfirmationMail {
|
||||
type Context = JobRunnerContext;
|
||||
type Error = miette::Report;
|
||||
type Error = eyre::Report;
|
||||
|
||||
async fn run(&self, ctx: &Self::Context) -> Result<(), Self::Error> {
|
||||
let mailing_service = &ctx.service.mailing;
|
||||
|
@ -24,16 +23,13 @@ impl Runnable for SendConfirmationMail {
|
|||
mailing_service.mark_as_confirmed(self.user_id).await?;
|
||||
}
|
||||
|
||||
let user = ctx
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
users::table
|
||||
.find(self.user_id)
|
||||
.select(User::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let user = with_connection!(ctx.db_pool, |db_conn| {
|
||||
users::table
|
||||
.find(self.user_id)
|
||||
.select(User::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
mailing_service.send_confirmation_email(&user).await?;
|
||||
|
||||
|
|
|
@ -20,13 +20,12 @@ kitsune-type = { path = "../kitsune-type" }
|
|||
kitsune-url = { path = "../kitsune-url" }
|
||||
kitsune-util = { path = "../kitsune-util" }
|
||||
mime = "0.3.17"
|
||||
scoped-futures = "0.1.3"
|
||||
serde = "1.0.197"
|
||||
simd-json = "0.13.9"
|
||||
smol_str = "0.2.1"
|
||||
speedy-uuid = { path = "../../lib/speedy-uuid" }
|
||||
thiserror = "1.0.58"
|
||||
tokio = { version = "1.36.0", features = ["rt"] }
|
||||
tokio = { version = "1.37.0", features = ["rt"] }
|
||||
tracing = "0.1.40"
|
||||
typed-builder = "0.18.1"
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use std::fmt::{Debug, Display};
|
||||
|
||||
use diesel_async::pooled_connection::bb8;
|
||||
use std::fmt::Debug;
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
|
@ -21,15 +20,3 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Service(#[from] kitsune_service::error::Error),
|
||||
}
|
||||
|
||||
impl<E> From<kitsune_db::PoolError<E>> for Error
|
||||
where
|
||||
E: Into<Error> + Debug + Display,
|
||||
{
|
||||
fn from(value: kitsune_db::PoolError<E>) -> Self {
|
||||
match value {
|
||||
kitsune_db::PoolError::Pool(err) => err.into(),
|
||||
kitsune_db::PoolError::User(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ use kitsune_db::{
|
|||
accounts, accounts_follows, custom_emojis, media_attachments, notifications, posts,
|
||||
posts_favourites,
|
||||
},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_embed::Client as EmbedClient;
|
||||
use kitsune_embed::{embed_sdk::EmbedType, Embed};
|
||||
|
@ -40,7 +40,6 @@ use kitsune_type::mastodon::{
|
|||
use kitsune_url::UrlService;
|
||||
use kitsune_util::try_join;
|
||||
use mime::Mime;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use smol_str::SmolStr;
|
||||
use speedy_uuid::Uuid;
|
||||
|
@ -78,30 +77,25 @@ impl IntoMastodon for DbAccount {
|
|||
}
|
||||
|
||||
async fn into_mastodon(self, state: MapperState<'_>) -> Result<Self::Output> {
|
||||
let (statuses_count, followers_count, following_count) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
let statuses_count_fut = posts::table
|
||||
.filter(posts::account_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn);
|
||||
let (statuses_count, followers_count, following_count) =
|
||||
with_connection!(state.db_pool, |db_conn| {
|
||||
let statuses_count_fut = posts::table
|
||||
.filter(posts::account_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn);
|
||||
|
||||
let followers_count_fut = accounts_follows::table
|
||||
.filter(accounts_follows::account_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn);
|
||||
let followers_count_fut = accounts_follows::table
|
||||
.filter(accounts_follows::account_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn);
|
||||
|
||||
let following_count_fut = accounts_follows::table
|
||||
.filter(accounts_follows::follower_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn);
|
||||
let following_count_fut = accounts_follows::table
|
||||
.filter(accounts_follows::follower_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn);
|
||||
|
||||
try_join!(statuses_count_fut, followers_count_fut, following_count_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(statuses_count_fut, followers_count_fut, following_count_fut)
|
||||
})?;
|
||||
|
||||
let mut acct = self.username.clone();
|
||||
if !self.local {
|
||||
|
@ -163,39 +157,34 @@ impl IntoMastodon for (&DbAccount, &DbAccount) {
|
|||
async fn into_mastodon(self, state: MapperState<'_>) -> Result<Self::Output> {
|
||||
let (requester, target) = self;
|
||||
|
||||
let ((following, follow_requested), followed_by) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let following_requested_fut = accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(target.id)
|
||||
.and(accounts_follows::follower_id.eq(requester.id)),
|
||||
)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.map(OptionalExtension::optional)
|
||||
.map_ok(|optional_follow| {
|
||||
optional_follow.map_or((false, false), |follow| {
|
||||
(follow.approved_at.is_some(), follow.approved_at.is_none())
|
||||
})
|
||||
});
|
||||
let ((following, follow_requested), followed_by) =
|
||||
with_connection!(state.db_pool, |db_conn| {
|
||||
let following_requested_fut = accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(target.id)
|
||||
.and(accounts_follows::follower_id.eq(requester.id)),
|
||||
)
|
||||
.get_result::<Follow>(db_conn)
|
||||
.map(OptionalExtension::optional)
|
||||
.map_ok(|optional_follow| {
|
||||
optional_follow.map_or((false, false), |follow| {
|
||||
(follow.approved_at.is_some(), follow.approved_at.is_none())
|
||||
})
|
||||
});
|
||||
|
||||
let followed_by_fut = accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(requester.id)
|
||||
.and(accounts_follows::follower_id.eq(target.id)),
|
||||
)
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_ok(|count| count != 0);
|
||||
let followed_by_fut = accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(requester.id)
|
||||
.and(accounts_follows::follower_id.eq(target.id)),
|
||||
)
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_ok(|count| count != 0);
|
||||
|
||||
try_join!(following_requested_fut, followed_by_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(following_requested_fut, followed_by_fut)
|
||||
})?;
|
||||
|
||||
Ok(Relationship {
|
||||
id: target.id,
|
||||
|
@ -223,16 +212,13 @@ impl IntoMastodon for DbMention {
|
|||
}
|
||||
|
||||
async fn into_mastodon(self, state: MapperState<'_>) -> Result<Self::Output> {
|
||||
let account: DbAccount = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.find(self.account_id)
|
||||
.select(DbAccount::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let account: DbAccount = with_connection!(state.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(self.account_id)
|
||||
.select(DbAccount::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
let mut acct = account.username.clone();
|
||||
if !account.local {
|
||||
|
@ -289,29 +275,23 @@ impl IntoMastodon for (&DbAccount, DbPost) {
|
|||
async fn into_mastodon(self, state: MapperState<'_>) -> Result<Self::Output> {
|
||||
let (account, post) = self;
|
||||
|
||||
let (favourited, reblogged) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let favourited_fut = posts_favourites::table
|
||||
.filter(posts_favourites::account_id.eq(account.id))
|
||||
.filter(posts_favourites::post_id.eq(post.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_ok(|count| count != 0);
|
||||
let (favourited, reblogged) = with_connection!(state.db_pool, |db_conn| {
|
||||
let favourited_fut = posts_favourites::table
|
||||
.filter(posts_favourites::account_id.eq(account.id))
|
||||
.filter(posts_favourites::post_id.eq(post.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_ok(|count| count != 0);
|
||||
|
||||
let reblogged_fut = posts::table
|
||||
.filter(posts::account_id.eq(account.id))
|
||||
.filter(posts::reposted_post_id.eq(post.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_ok(|count| count != 0);
|
||||
let reblogged_fut = posts::table
|
||||
.filter(posts::account_id.eq(account.id))
|
||||
.filter(posts::reposted_post_id.eq(post.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_ok(|count| count != 0);
|
||||
|
||||
try_join!(favourited_fut, reblogged_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(favourited_fut, reblogged_fut)
|
||||
})?;
|
||||
|
||||
let mut status = post.into_mastodon(state).await?;
|
||||
status.favourited = favourited;
|
||||
|
@ -341,62 +321,56 @@ impl IntoMastodon for DbPost {
|
|||
media_attachments,
|
||||
mentions_stream,
|
||||
custom_emojis_stream,
|
||||
) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
let account_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(DbAccount::as_select())
|
||||
.get_result::<DbAccount>(db_conn)
|
||||
) = with_connection!(state.db_pool, |db_conn| {
|
||||
let account_fut = accounts::table
|
||||
.find(self.account_id)
|
||||
.select(DbAccount::as_select())
|
||||
.get_result::<DbAccount>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|db_account| db_account.into_mastodon(state));
|
||||
|
||||
let reblog_count_fut = posts::table
|
||||
.filter(posts::reposted_post_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_err(Error::from);
|
||||
|
||||
let favourites_count_fut = DbFavourite::belonging_to(&self)
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_err(Error::from);
|
||||
|
||||
let media_attachments_fut = DbPostMediaAttachment::belonging_to(&self)
|
||||
.inner_join(media_attachments::table)
|
||||
.select(DbMediaAttachment::as_select())
|
||||
.load_stream::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|attachment_stream| {
|
||||
attachment_stream
|
||||
.map_err(Error::from)
|
||||
.and_then(|db_account| db_account.into_mastodon(state));
|
||||
.and_then(|attachment| attachment.into_mastodon(state))
|
||||
.try_collect()
|
||||
});
|
||||
|
||||
let reblog_count_fut = posts::table
|
||||
.filter(posts::reposted_post_id.eq(self.id))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_err(Error::from);
|
||||
let mentions_stream_fut = DbMention::belonging_to(&self)
|
||||
.load_stream::<DbMention>(db_conn)
|
||||
.map_err(Error::from);
|
||||
|
||||
let favourites_count_fut = DbFavourite::belonging_to(&self)
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.map_err(Error::from);
|
||||
let custom_emojis_stream_fut = DbPostCustomEmoji::belonging_to(&self)
|
||||
.inner_join(custom_emojis::table.inner_join(media_attachments::table))
|
||||
.select((DbCustomEmoji::as_select(), DbMediaAttachment::as_select()))
|
||||
.load_stream::<(DbCustomEmoji, DbMediaAttachment)>(db_conn)
|
||||
.map_err(Error::from);
|
||||
|
||||
let media_attachments_fut = DbPostMediaAttachment::belonging_to(&self)
|
||||
.inner_join(media_attachments::table)
|
||||
.select(DbMediaAttachment::as_select())
|
||||
.load_stream::<DbMediaAttachment>(db_conn)
|
||||
.map_err(Error::from)
|
||||
.and_then(|attachment_stream| {
|
||||
attachment_stream
|
||||
.map_err(Error::from)
|
||||
.and_then(|attachment| attachment.into_mastodon(state))
|
||||
.try_collect()
|
||||
});
|
||||
|
||||
let mentions_stream_fut = DbMention::belonging_to(&self)
|
||||
.load_stream::<DbMention>(db_conn)
|
||||
.map_err(Error::from);
|
||||
|
||||
let custom_emojis_stream_fut = DbPostCustomEmoji::belonging_to(&self)
|
||||
.inner_join(custom_emojis::table.inner_join(media_attachments::table))
|
||||
.select((DbCustomEmoji::as_select(), DbMediaAttachment::as_select()))
|
||||
.load_stream::<(DbCustomEmoji, DbMediaAttachment)>(db_conn)
|
||||
.map_err(Error::from);
|
||||
|
||||
try_join!(
|
||||
account_fut,
|
||||
reblog_count_fut,
|
||||
favourites_count_fut,
|
||||
media_attachments_fut,
|
||||
mentions_stream_fut,
|
||||
custom_emojis_stream_fut
|
||||
)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(
|
||||
account_fut,
|
||||
reblog_count_fut,
|
||||
favourites_count_fut,
|
||||
media_attachments_fut,
|
||||
mentions_stream_fut,
|
||||
custom_emojis_stream_fut
|
||||
)
|
||||
})?;
|
||||
|
||||
let link_preview = OptionFuture::from(
|
||||
self.link_preview_url
|
||||
|
@ -423,30 +397,24 @@ impl IntoMastodon for DbPost {
|
|||
.try_collect()
|
||||
.await?;
|
||||
|
||||
let reblog = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
OptionFuture::from(
|
||||
OptionFuture::from(self.reposted_post_id.map(|id| {
|
||||
posts::table
|
||||
.find(id)
|
||||
.select(DbPost::as_select())
|
||||
.get_result::<DbPost>(db_conn)
|
||||
.map(OptionalExtension::optional)
|
||||
}))
|
||||
.await
|
||||
.transpose()?
|
||||
.flatten()
|
||||
.map(|post| post.into_mastodon(state)), // This will allocate two database connections. Fuck.
|
||||
)
|
||||
.await
|
||||
.transpose()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?
|
||||
.map(Box::new);
|
||||
let reblog = with_connection!(state.db_pool, |db_conn| {
|
||||
OptionFuture::from(
|
||||
OptionFuture::from(self.reposted_post_id.map(|id| {
|
||||
posts::table
|
||||
.find(id)
|
||||
.select(DbPost::as_select())
|
||||
.get_result::<DbPost>(db_conn)
|
||||
.map(OptionalExtension::optional)
|
||||
}))
|
||||
.await
|
||||
.transpose()?
|
||||
.flatten()
|
||||
.map(|post| post.into_mastodon(state)), // This will allocate two database connections. Fuck.
|
||||
)
|
||||
.await
|
||||
.transpose()
|
||||
})?
|
||||
.map(Box::new);
|
||||
|
||||
let language = self.content_lang.to_639_1().map(str::to_string);
|
||||
|
||||
|
@ -581,9 +549,8 @@ impl IntoMastodon for DbNotification {
|
|||
}
|
||||
|
||||
async fn into_mastodon(self, state: MapperState<'_>) -> Result<Self::Output> {
|
||||
let (notification, account, status): (DbNotification, DbAccount, Option<DbPost>) = state
|
||||
.db_pool
|
||||
.with_connection(|mut db_conn| {
|
||||
let (notification, account, status): (DbNotification, DbAccount, Option<DbPost>) =
|
||||
with_connection!(state.db_pool, |db_conn| {
|
||||
notifications::table
|
||||
.filter(notifications::receiving_account_id.eq(self.receiving_account_id))
|
||||
.inner_join(
|
||||
|
@ -592,10 +559,9 @@ impl IntoMastodon for DbNotification {
|
|||
)
|
||||
.left_outer_join(posts::table)
|
||||
.select(<(DbNotification, DbAccount, Option<DbPost>)>::as_select())
|
||||
.get_result(&mut db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
let status = OptionFuture::from(status.map(|status| status.into_mastodon(state)))
|
||||
.await
|
||||
|
|
|
@ -10,11 +10,11 @@ ahash = "0.8.11"
|
|||
derive_more = { version = "1.0.0-beta.6", features = ["from"] }
|
||||
futures-util = "0.3.30"
|
||||
just-retry = { path = "../../lib/just-retry" }
|
||||
pin-project-lite = "0.2.13"
|
||||
pin-project-lite = "0.2.14"
|
||||
redis = { version = "0.25.2", features = ["connection-manager", "tokio-comp"] }
|
||||
serde = "1.0.197"
|
||||
simd-json = "0.13.9"
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt", "sync"] }
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt", "sync"] }
|
||||
tokio-stream = { version = "0.1.15", features = ["sync"] }
|
||||
tracing = "0.1.40"
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ license.workspace = true
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.79"
|
||||
eyre = "0.6.12"
|
||||
http-body-util = "0.1.1"
|
||||
http-compat = { path = "../../lib/http-compat" }
|
||||
hyper = { version = "1.2.0", default-features = false }
|
||||
|
@ -16,11 +17,10 @@ metrics = "=0.22.0"
|
|||
metrics-opentelemetry = { git = "https://github.com/aumetra/metrics-opentelemetry.git", rev = "95537b16370e595981e195be52f98ea5983a7a8e" }
|
||||
metrics-tracing-context = "0.15.0"
|
||||
metrics-util = "0.16.3"
|
||||
miette = "7.2.0"
|
||||
opentelemetry = { version = "0.22.0", default-features = false, features = [
|
||||
"trace",
|
||||
] }
|
||||
opentelemetry-http = "0.11.0"
|
||||
opentelemetry-http = "0.11.1"
|
||||
opentelemetry-otlp = { version = "0.15.0", default-features = false, features = [
|
||||
"grpc-tonic",
|
||||
"http-proto",
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use async_trait::async_trait;
|
||||
use eyre::WrapErr;
|
||||
use http_body_util::BodyExt;
|
||||
use http_compat::Compat;
|
||||
use kitsune_config::{open_telemetry::Transport, Configuration};
|
||||
use metrics_opentelemetry::OpenTelemetryRecorder;
|
||||
use metrics_tracing_context::{MetricsLayer, TracingContextLayer};
|
||||
use metrics_util::layers::Layer as _;
|
||||
use miette::{Context, IntoDiagnostic};
|
||||
use opentelemetry::{
|
||||
metrics::{noop::NoopMeterProvider, Meter, MeterProvider},
|
||||
trace::{noop::NoopTracer, Tracer},
|
||||
|
@ -67,18 +67,13 @@ macro_rules! build_exporter {
|
|||
}};
|
||||
}
|
||||
|
||||
fn initialise_logging<T>(tracer: T) -> miette::Result<()>
|
||||
fn initialise_logging<T>(tracer: T) -> eyre::Result<()>
|
||||
where
|
||||
T: Tracer + PreSampledTracer + Send + Sync + 'static,
|
||||
{
|
||||
let env_filter = env::var("RUST_LOG")
|
||||
.into_diagnostic()
|
||||
.and_then(|targets| {
|
||||
targets
|
||||
.parse()
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to parse RUST_LOG value")
|
||||
})
|
||||
.map_err(eyre::Report::from)
|
||||
.and_then(|targets| targets.parse().wrap_err("Failed to parse RUST_LOG value"))
|
||||
.unwrap_or_else(|_| Targets::default().with_default(LevelFilter::INFO));
|
||||
|
||||
let subscriber = Registry::default()
|
||||
|
@ -89,22 +84,20 @@ where
|
|||
let subscriber = subscriber.with(MetricsLayer::new());
|
||||
|
||||
tracing::subscriber::set_global_default(subscriber)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Couldn't install the global tracing subscriber")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn initialise_metrics(meter: Meter) -> miette::Result<()> {
|
||||
fn initialise_metrics(meter: Meter) -> eyre::Result<()> {
|
||||
let recorder = TracingContextLayer::all().layer(OpenTelemetryRecorder::new(meter));
|
||||
metrics::set_global_recorder(recorder)
|
||||
.into_diagnostic()
|
||||
.wrap_err("Couldn't install the global metrics recorder")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn initialise(app_name: &'static str, config: &Configuration) -> miette::Result<()> {
|
||||
pub fn initialise(app_name: &'static str, config: &Configuration) -> eyre::Result<()> {
|
||||
if let Some(ref opentelemetry_config) = config.opentelemetry {
|
||||
let http_client = HttpClientAdapter {
|
||||
inner: kitsune_http_client::Client::default(),
|
||||
|
@ -120,8 +113,7 @@ pub fn initialise(app_name: &'static str, config: &Configuration) -> miette::Res
|
|||
let tracer = opentelemetry_otlp::new_pipeline()
|
||||
.tracing()
|
||||
.with_exporter(trace_exporter)
|
||||
.install_batch(Tokio)
|
||||
.into_diagnostic()?;
|
||||
.install_batch(Tokio)?;
|
||||
|
||||
initialise_logging(tracer)?;
|
||||
|
||||
|
@ -135,8 +127,7 @@ pub fn initialise(app_name: &'static str, config: &Configuration) -> miette::Res
|
|||
let meter_provider = opentelemetry_otlp::new_pipeline()
|
||||
.metrics(Tokio)
|
||||
.with_exporter(metrics_exporter)
|
||||
.build()
|
||||
.into_diagnostic()?;
|
||||
.build()?;
|
||||
|
||||
initialise_metrics(meter_provider.meter(app_name))?;
|
||||
} else {
|
||||
|
|
|
@ -6,12 +6,11 @@ version.workspace = true
|
|||
license.workspace = true
|
||||
|
||||
[dependencies]
|
||||
enum_dispatch = "0.3.12"
|
||||
enum_dispatch = "0.3.13"
|
||||
http = "1.1.0"
|
||||
http-compat = { path = "../../lib/http-compat" }
|
||||
kitsune-config = { path = "../kitsune-config" }
|
||||
kitsune-http-client = { path = "../kitsune-http-client" }
|
||||
miette = "7.2.0"
|
||||
moka = { version = "0.12.5", features = ["future"] }
|
||||
multiplex-pool = { path = "../../lib/multiplex-pool" }
|
||||
once_cell = "1.19.0"
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
use miette::Diagnostic;
|
||||
use openidconnect::{
|
||||
core::CoreErrorResponseType, ClaimsVerificationError, DiscoveryError, RequestTokenError,
|
||||
SigningError, StandardErrorResponse,
|
||||
|
@ -7,7 +6,7 @@ use thiserror::Error;
|
|||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
ClaimsVerification(#[from] ClaimsVerificationError),
|
||||
|
|
|
@ -17,7 +17,7 @@ typed-builder = "0.18.1"
|
|||
|
||||
[dev-dependencies]
|
||||
kitsune-test = { path = "../kitsune-test" }
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -33,9 +33,9 @@ const fn s3_method_to_http(method: rusty_s3::Method) -> http::Method {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
const fn http_method_by_value<'a, T: ?Sized>(_: &T) -> http::Method
|
||||
const fn http_method_by_value<'a, T>(_: &T) -> http::Method
|
||||
where
|
||||
T: S3Action<'a>,
|
||||
T: S3Action<'a> + ?Sized,
|
||||
{
|
||||
s3_method_to_http(T::METHOD)
|
||||
}
|
||||
|
|
|
@ -12,12 +12,11 @@ ignored = ["isahc"] # To make `meilisearch` builds static
|
|||
diesel = "2.1.5"
|
||||
diesel-async = "0.4.1"
|
||||
diesel_full_text_search = { version = "2.1.1", default-features = false }
|
||||
enum_dispatch = "0.3.12"
|
||||
enum_dispatch = "0.3.13"
|
||||
futures-util = "0.3.30"
|
||||
kitsune-config = { path = "../kitsune-config" }
|
||||
kitsune-db = { path = "../kitsune-db" }
|
||||
kitsune-language = { path = "../kitsune-language" }
|
||||
miette = "7.2.0"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
speedy-uuid = { path = "../../lib/speedy-uuid" }
|
||||
strum = { version = "0.26.2", features = ["derive"] }
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
use diesel_async::pooled_connection::bb8;
|
||||
use miette::Diagnostic;
|
||||
use std::fmt::{Debug, Display};
|
||||
use std::fmt::Debug;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Database(#[from] diesel::result::Error),
|
||||
|
@ -15,15 +14,3 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Meilisearch(#[from] meilisearch_sdk::errors::Error),
|
||||
}
|
||||
|
||||
impl<E> From<kitsune_db::PoolError<E>> for Error
|
||||
where
|
||||
E: Into<Error> + Debug + Display,
|
||||
{
|
||||
fn from(value: kitsune_db::PoolError<E>) -> Self {
|
||||
match value {
|
||||
kitsune_db::PoolError::Pool(err) => err.into(),
|
||||
kitsune_db::PoolError::User(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::{Result, SearchBackend, SearchIndex, SearchItem, SearchResultReference};
|
||||
use diesel::{ExpressionMethods, QueryDsl};
|
||||
use diesel_async::{scoped_futures::ScopedFutureExt, RunQueryDsl};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use diesel_full_text_search::{websearch_to_tsquery_with_search_config, TsVectorExtensions};
|
||||
use futures_util::TryStreamExt;
|
||||
use kitsune_config::language_detection::Configuration as LanguageDetectionConfig;
|
||||
|
@ -9,7 +9,7 @@ use kitsune_db::{
|
|||
lang::LanguageIsoCode,
|
||||
model::post::Visibility,
|
||||
schema::{accounts, posts},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
@ -62,23 +62,17 @@ impl SearchBackend for SearchService {
|
|||
query = query.filter(accounts::id.lt(max_id));
|
||||
}
|
||||
|
||||
let results = self
|
||||
.db_pool
|
||||
.with_connection(move |db_conn| {
|
||||
async move {
|
||||
query
|
||||
.limit(max_results as i64)
|
||||
.offset(offset as i64)
|
||||
.select(accounts::id)
|
||||
.load_stream(db_conn)
|
||||
.await?
|
||||
.map_ok(|id| SearchResultReference { index, id })
|
||||
.try_collect()
|
||||
.await
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let results = with_connection!(self.db_pool, |db_conn| {
|
||||
query
|
||||
.limit(max_results as i64)
|
||||
.offset(offset as i64)
|
||||
.select(accounts::id)
|
||||
.load_stream(db_conn)
|
||||
.await?
|
||||
.map_ok(|id| SearchResultReference { index, id })
|
||||
.try_collect()
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
@ -94,27 +88,20 @@ impl SearchBackend for SearchService {
|
|||
query = query.filter(posts::id.lt(max_id));
|
||||
}
|
||||
|
||||
let results = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
query
|
||||
.filter(
|
||||
posts::visibility
|
||||
.eq_any([Visibility::Public, Visibility::Unlisted]),
|
||||
)
|
||||
.limit(max_results as i64)
|
||||
.offset(offset as i64)
|
||||
.select(posts::id)
|
||||
.load_stream(db_conn)
|
||||
.await?
|
||||
.map_ok(|id| SearchResultReference { index, id })
|
||||
.try_collect()
|
||||
.await
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let results = with_connection!(self.db_pool, |db_conn| {
|
||||
query
|
||||
.filter(
|
||||
posts::visibility.eq_any([Visibility::Public, Visibility::Unlisted]),
|
||||
)
|
||||
.limit(max_results as i64)
|
||||
.offset(offset as i64)
|
||||
.select(posts::id)
|
||||
.load_stream(db_conn)
|
||||
.await?
|
||||
.map_ok(|id| SearchResultReference { index, id })
|
||||
.try_collect()
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ bytes = "1.6.0"
|
|||
derive_builder = "0.20.0"
|
||||
diesel = "2.1.5"
|
||||
diesel-async = "0.4.1"
|
||||
eyre = "0.6.12"
|
||||
futures-util = "0.3.30"
|
||||
garde = { version = "0.18.0", default-features = false, features = [
|
||||
"derive",
|
||||
|
@ -41,7 +42,6 @@ kitsune-search = { path = "../kitsune-search" }
|
|||
kitsune-storage = { path = "../kitsune-storage" }
|
||||
kitsune-url = { path = "../kitsune-url" }
|
||||
kitsune-util = { path = "../kitsune-util" }
|
||||
miette = "7.2.0"
|
||||
mime = "0.3.17"
|
||||
multiplex-pool = { path = "../../lib/multiplex-pool" }
|
||||
password-hash = { version = "0.5.0", features = ["std"] }
|
||||
|
@ -54,13 +54,12 @@ redis = { version = "0.25.2", default-features = false, features = [
|
|||
] }
|
||||
rsa = "0.9.6"
|
||||
rusty-s3 = { version = "0.5.0", default-features = false }
|
||||
scoped-futures = "0.1.3"
|
||||
serde = "1.0.197"
|
||||
simd-json = "0.13.9"
|
||||
smol_str = "0.2.1"
|
||||
speedy-uuid = { path = "../../lib/speedy-uuid" }
|
||||
thiserror = "1.0.58"
|
||||
tokio = { version = "1.36.0", features = ["macros", "sync"] }
|
||||
tokio = { version = "1.37.0", features = ["macros", "sync"] }
|
||||
tracing = "0.1.40"
|
||||
typed-builder = "0.18.1"
|
||||
url = "2.5.0"
|
||||
|
|
|
@ -7,8 +7,8 @@ use crate::error::{Error, Result};
|
|||
use bytes::Bytes;
|
||||
use derive_builder::Builder;
|
||||
use diesel::{
|
||||
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, OptionalExtension, QueryDsl,
|
||||
SelectableHelper,
|
||||
BoolExpressionMethods, ExpressionMethods, JoinOnDsl, NullableExpressionMethods,
|
||||
OptionalExtension, QueryDsl, SelectableHelper,
|
||||
};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use futures_util::{Stream, TryStreamExt};
|
||||
|
@ -16,17 +16,17 @@ use garde::Validate;
|
|||
use iso8601_timestamp::Timestamp;
|
||||
use kitsune_core::traits::{fetcher::AccountFetchOptions, Fetcher, Resolver};
|
||||
use kitsune_db::{
|
||||
function::now,
|
||||
model::{
|
||||
account::{Account, UpdateAccount},
|
||||
follower::Follow as DbFollow,
|
||||
follower::NewFollow,
|
||||
follower::{Follow as DbFollow, NewFollow},
|
||||
notification::NewNotification,
|
||||
post::Post,
|
||||
preference::Preferences,
|
||||
},
|
||||
post_permission_check::{PermissionCheck, PostPermissionCheckExt},
|
||||
schema::{accounts, accounts_follows, accounts_preferences, notifications, posts},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_jobs::deliver::{
|
||||
accept::DeliverAccept,
|
||||
|
@ -37,7 +37,6 @@ use kitsune_jobs::deliver::{
|
|||
};
|
||||
use kitsune_url::UrlService;
|
||||
use kitsune_util::{sanitize::CleanHtmlExt, try_join};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use std::sync::Arc;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
@ -196,25 +195,19 @@ impl AccountService {
|
|||
///
|
||||
/// Tuple of two account models. First model is the account the followee account, the second model is the followed account
|
||||
pub async fn follow(&self, follow: Follow, notify: bool) -> Result<(Account, Account)> {
|
||||
let (account, follower) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let (account, follower) = with_connection!(self.db_pool, |db_conn| {
|
||||
let account_fut = accounts::table
|
||||
.find(follow.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
let follower_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let follower_fut = accounts::table
|
||||
.find(follow.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
try_join!(account_fut, follower_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(account_fut, follower_fut)
|
||||
})?;
|
||||
|
||||
let id = Uuid::now_v7();
|
||||
let url = self.url_service.follow_url(id);
|
||||
|
@ -232,28 +225,22 @@ impl AccountService {
|
|||
follow_model.approved_at = Some(Timestamp::now_utc());
|
||||
}
|
||||
|
||||
let follow_id = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::insert_into(accounts_follows::table)
|
||||
.values(follow_model)
|
||||
.returning(accounts_follows::id)
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow_id = with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::insert_into(accounts_follows::table)
|
||||
.values(follow_model)
|
||||
.returning(accounts_follows::id)
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
if account.local {
|
||||
let preferences = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts_preferences::table
|
||||
.find(follow.account_id)
|
||||
.select(Preferences::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let preferences = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts_preferences::table
|
||||
.find(follow.account_id)
|
||||
.select(Preferences::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
if (preferences.notify_on_follow && !account.locked)
|
||||
|| (preferences.notify_on_follow_request && account.locked)
|
||||
|
@ -268,15 +255,13 @@ impl AccountService {
|
|||
.follow(follower.id)
|
||||
};
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|mut db_conn| {
|
||||
diesel::insert_into(notifications::table)
|
||||
.values(notification)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(&mut db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::insert_into(notifications::table)
|
||||
.values(notification)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,24 +277,18 @@ impl AccountService {
|
|||
/// Get an account by its username and domain
|
||||
pub async fn get(&self, get_user: GetUser<'_>) -> Result<Option<Account>> {
|
||||
if let Some(domain) = get_user.domain {
|
||||
let account = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts::table
|
||||
.filter(
|
||||
accounts::username
|
||||
.eq(get_user.username)
|
||||
.and(accounts::domain.eq(domain)),
|
||||
)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let account = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.filter(
|
||||
accounts::username
|
||||
.eq(get_user.username)
|
||||
.and(accounts::domain.eq(domain)),
|
||||
)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(account) = account {
|
||||
return Ok(Some(account));
|
||||
|
@ -336,43 +315,33 @@ impl AccountService {
|
|||
.await
|
||||
.map_err(Error::Fetcher)
|
||||
} else {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts::table
|
||||
.filter(
|
||||
accounts::username
|
||||
.eq(get_user.username)
|
||||
.and(accounts::local.eq(true)),
|
||||
)
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.filter(
|
||||
accounts::username
|
||||
.eq(get_user.username)
|
||||
.and(accounts::local.eq(true)),
|
||||
)
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an account by its ID
|
||||
pub async fn get_by_id(&self, account_id: Uuid) -> Result<Option<Account>> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts::table
|
||||
.find(account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Get a stream of posts owned by the user
|
||||
|
@ -408,14 +377,9 @@ impl AccountService {
|
|||
query = query.filter(posts::id.gt(min_id)).order(posts::id.asc());
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
}.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
}
|
||||
|
||||
/// Undo the follow of an account
|
||||
|
@ -424,43 +388,31 @@ impl AccountService {
|
|||
///
|
||||
/// Tuple of two account models. First account is the account that was being followed, second account is the account that was following
|
||||
pub async fn unfollow(&self, unfollow: Unfollow) -> Result<(Account, Account)> {
|
||||
let (account, follower) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account_fut = accounts::table
|
||||
.find(unfollow.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let (account, follower) = with_connection!(self.db_pool, |db_conn| {
|
||||
let account_fut = accounts::table
|
||||
.find(unfollow.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
let follower_fut = accounts::table
|
||||
.find(unfollow.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let follower_fut = accounts::table
|
||||
.find(unfollow.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
try_join!(account_fut, follower_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(account_fut, follower_fut)
|
||||
})?;
|
||||
|
||||
let follow = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(account.id)
|
||||
.and(accounts_follows::follower_id.eq(follower.id)),
|
||||
)
|
||||
.get_result::<DbFollow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(account.id)
|
||||
.and(accounts_follows::follower_id.eq(follower.id)),
|
||||
)
|
||||
.get_result::<DbFollow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(follow) = follow {
|
||||
if !account.local {
|
||||
|
@ -505,73 +457,52 @@ impl AccountService {
|
|||
query = query.filter(accounts::id.lt(max_id));
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub async fn accept_follow_request(
|
||||
&self,
|
||||
follow_request: FollowRequest,
|
||||
) -> Result<Option<(Account, Account)>> {
|
||||
let (account, follower) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account_fut = accounts::table
|
||||
.find(follow_request.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let (account, follower) = with_connection!(self.db_pool, |db_conn| {
|
||||
let account_fut = accounts::table
|
||||
.find(follow_request.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
let follower_fut = accounts::table
|
||||
.find(follow_request.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let follower_fut = accounts::table
|
||||
.find(follow_request.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
try_join!(account_fut, follower_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(account_fut, follower_fut)
|
||||
})?;
|
||||
|
||||
let follow = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(account.id)
|
||||
.and(accounts_follows::follower_id.eq(follower.id)),
|
||||
)
|
||||
.get_result::<DbFollow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(account.id)
|
||||
.and(accounts_follows::follower_id.eq(follower.id)),
|
||||
)
|
||||
.get_result::<DbFollow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(follow) = follow {
|
||||
let now = Timestamp::now_utc();
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::update(&follow)
|
||||
.set((
|
||||
accounts_follows::approved_at.eq(now),
|
||||
accounts_follows::updated_at.eq(now),
|
||||
))
|
||||
.execute(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
if let Some(ref follow) = follow {
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::update(follow)
|
||||
.set((
|
||||
accounts_follows::approved_at.eq(now().nullable()),
|
||||
accounts_follows::updated_at.eq(now()),
|
||||
))
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
if !account.local {
|
||||
self.job_service
|
||||
|
@ -595,49 +526,37 @@ impl AccountService {
|
|||
&self,
|
||||
follow_request: FollowRequest,
|
||||
) -> Result<Option<(Account, Account)>> {
|
||||
let (account, follower) = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
let account_fut = accounts::table
|
||||
.find(follow_request.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let (account, follower) = with_connection!(self.db_pool, |db_conn| {
|
||||
let account_fut = accounts::table
|
||||
.find(follow_request.account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
let follower_fut = accounts::table
|
||||
.find(follow_request.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
let follower_fut = accounts::table
|
||||
.find(follow_request.follower_id)
|
||||
.select(Account::as_select())
|
||||
.get_result(db_conn);
|
||||
|
||||
try_join!(account_fut, follower_fut)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
try_join!(account_fut, follower_fut)
|
||||
})?;
|
||||
|
||||
let follow = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(account.id)
|
||||
.and(accounts_follows::follower_id.eq(follower.id)),
|
||||
)
|
||||
.get_result::<DbFollow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let follow = with_connection!(self.db_pool, |db_conn| {
|
||||
accounts_follows::table
|
||||
.filter(
|
||||
accounts_follows::account_id
|
||||
.eq(account.id)
|
||||
.and(accounts_follows::follower_id.eq(follower.id)),
|
||||
)
|
||||
.get_result::<DbFollow>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(follow) = follow {
|
||||
if account.local {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| diesel::delete(&follow).execute(db_conn).scoped())
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::delete(&follow).execute(db_conn).await
|
||||
})?;
|
||||
} else {
|
||||
self.job_service
|
||||
.enqueue(
|
||||
|
@ -704,16 +623,13 @@ impl AccountService {
|
|||
};
|
||||
}
|
||||
|
||||
let updated_account: Account = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::update(accounts::table.find(update.account_id))
|
||||
.set(changeset)
|
||||
.returning(Account::as_returning())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let updated_account: Account = with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::update(accounts::table.find(update.account_id))
|
||||
.set(changeset)
|
||||
.returning(Account::as_returning())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
self.job_service
|
||||
.enqueue(
|
||||
|
|
|
@ -10,12 +10,11 @@ use kitsune_core::consts::{MAX_MEDIA_DESCRIPTION_LENGTH, USER_AGENT};
|
|||
use kitsune_db::{
|
||||
model::media_attachment::{MediaAttachment, NewMediaAttachment, UpdateMediaAttachment},
|
||||
schema::media_attachments,
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_http_client::Client;
|
||||
use kitsune_storage::{AnyStorageBackend, BoxError, StorageBackend};
|
||||
use kitsune_url::UrlService;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -93,15 +92,10 @@ pub struct AttachmentService {
|
|||
|
||||
impl AttachmentService {
|
||||
pub async fn get_by_id(&self, id: Uuid) -> Result<MediaAttachment> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
media_attachments::table
|
||||
.find(id)
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
media_attachments::table.find(id).get_result(db_conn).await
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Get the URL to an attachment
|
||||
|
@ -161,21 +155,19 @@ impl AttachmentService {
|
|||
};
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::update(
|
||||
media_attachments::table.filter(
|
||||
media_attachments::id
|
||||
.eq(update.attachment_id)
|
||||
.and(media_attachments::account_id.eq(update.account_id)),
|
||||
),
|
||||
)
|
||||
.set(changeset)
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::update(
|
||||
media_attachments::table.filter(
|
||||
media_attachments::id
|
||||
.eq(update.attachment_id)
|
||||
.and(media_attachments::account_id.eq(update.account_id)),
|
||||
),
|
||||
)
|
||||
.set(changeset)
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub async fn upload<S>(&self, upload: Upload<S>) -> Result<MediaAttachment>
|
||||
|
@ -217,23 +209,20 @@ impl AttachmentService {
|
|||
}
|
||||
.map_err(Error::Storage)?;
|
||||
|
||||
let media_attachment = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: Uuid::now_v7(),
|
||||
content_type: upload.content_type.as_str(),
|
||||
account_id: upload.account_id,
|
||||
description: upload.description.as_deref(),
|
||||
blurhash: None,
|
||||
file_path: Some(upload.path.as_str()),
|
||||
remote_url: None,
|
||||
})
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let media_attachment = with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: Uuid::now_v7(),
|
||||
content_type: upload.content_type.as_str(),
|
||||
account_id: upload.account_id,
|
||||
description: upload.description.as_deref(),
|
||||
blurhash: None,
|
||||
file_path: Some(upload.path.as_str()),
|
||||
remote_url: None,
|
||||
})
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(media_attachment)
|
||||
}
|
||||
|
@ -241,12 +230,9 @@ impl AttachmentService {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
attachment::{AttachmentService, Upload},
|
||||
error::Error,
|
||||
};
|
||||
use crate::attachment::{AttachmentService, Upload};
|
||||
use bytes::{Bytes, BytesMut};
|
||||
use diesel_async::{AsyncConnection, AsyncPgConnection, RunQueryDsl};
|
||||
use diesel_async::{AsyncPgConnection, RunQueryDsl};
|
||||
use futures_util::{future, pin_mut, stream, StreamExt};
|
||||
use http::{Request, Response};
|
||||
use http_body_util::Empty;
|
||||
|
@ -261,12 +247,12 @@ mod test {
|
|||
media_attachment::MediaAttachment,
|
||||
},
|
||||
schema::accounts,
|
||||
with_connection_panicky,
|
||||
};
|
||||
use kitsune_http_client::Client;
|
||||
use kitsune_storage::fs::Storage;
|
||||
use kitsune_test::database_test;
|
||||
use kitsune_url::UrlService;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use std::convert::Infallible;
|
||||
use tempfile::TempDir;
|
||||
|
@ -277,12 +263,10 @@ mod test {
|
|||
database_test(|db_pool| async move {
|
||||
let client = Client::builder().service(service_fn(handle));
|
||||
|
||||
let account_id = db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move { Ok::<_, miette::Report>(prepare_db(db_conn).await) }.scoped()
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
let account_id = with_connection_panicky!(db_pool, |db_conn| {
|
||||
Ok::<_, eyre::Report>(prepare_db(db_conn).await)
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let temp_dir = TempDir::new().unwrap();
|
||||
let storage = Storage::new(temp_dir.path().to_owned());
|
||||
|
@ -350,38 +334,32 @@ mod test {
|
|||
|
||||
async fn prepare_db(db_conn: &mut AsyncPgConnection) -> Uuid {
|
||||
// Create a local user `@alice`
|
||||
db_conn
|
||||
.transaction(|tx| {
|
||||
async move {
|
||||
let account_id = Uuid::now_v7();
|
||||
diesel::insert_into(accounts::table)
|
||||
.values(NewAccount {
|
||||
id: account_id,
|
||||
display_name: None,
|
||||
username: "alice",
|
||||
locked: false,
|
||||
note: None,
|
||||
local: true,
|
||||
domain: "example.com",
|
||||
actor_type: ActorType::Person,
|
||||
url: "https://example.com/users/alice",
|
||||
featured_collection_url: None,
|
||||
followers_url: None,
|
||||
following_url: None,
|
||||
inbox_url: None,
|
||||
outbox_url: None,
|
||||
shared_inbox_url: None,
|
||||
public_key_id: "https://example.com/users/alice#main-key",
|
||||
public_key: "",
|
||||
created_at: None,
|
||||
})
|
||||
.execute(tx)
|
||||
.await?;
|
||||
Ok::<_, Error>(account_id)
|
||||
}
|
||||
.scope_boxed()
|
||||
let account_id = Uuid::now_v7();
|
||||
diesel::insert_into(accounts::table)
|
||||
.values(NewAccount {
|
||||
id: account_id,
|
||||
display_name: None,
|
||||
username: "alice",
|
||||
locked: false,
|
||||
note: None,
|
||||
local: true,
|
||||
domain: "example.com",
|
||||
actor_type: ActorType::Person,
|
||||
url: "https://example.com/users/alice",
|
||||
featured_collection_url: None,
|
||||
followers_url: None,
|
||||
following_url: None,
|
||||
inbox_url: None,
|
||||
outbox_url: None,
|
||||
shared_inbox_url: None,
|
||||
public_key_id: "https://example.com/users/alice#main-key",
|
||||
public_key: "",
|
||||
created_at: None,
|
||||
})
|
||||
.execute(db_conn)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
account_id
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,10 +13,9 @@ use kitsune_core::consts::MAX_EMOJI_SHORTCODE_LENGTH;
|
|||
use kitsune_db::{
|
||||
model::{custom_emoji::CustomEmoji, media_attachment::MediaAttachment},
|
||||
schema::{custom_emojis, media_attachments, posts, posts_custom_emojis},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_url::UrlService;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -80,12 +79,10 @@ impl CustomEmojiService {
|
|||
query = query.filter(custom_emojis::domain.eq(domain));
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move { query.first(db_conn).await.optional() }.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
query.first(db_conn).await.optional()
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub async fn get_by_id(&self, id: Uuid) -> Result<CustomEmoji> {
|
||||
|
@ -93,9 +90,7 @@ impl CustomEmojiService {
|
|||
.find(id)
|
||||
.select(CustomEmoji::as_select());
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| async move { query.get_result(db_conn).await }.scoped())
|
||||
.await
|
||||
with_connection!(self.db_pool, |db_conn| { query.get_result(db_conn).await })
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
|
@ -132,13 +127,9 @@ impl CustomEmojiService {
|
|||
))
|
||||
.limit(get_emoji_list.limit);
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move { Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from)) }
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn add_emoji<S>(&self, emoji_upload: EmojiUpload<S>) -> Result<CustomEmoji>
|
||||
|
@ -158,24 +149,21 @@ impl CustomEmojiService {
|
|||
let id = Uuid::now_v7();
|
||||
let remote_id = self.url_service.custom_emoji_url(id);
|
||||
|
||||
let custom_emoji = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id,
|
||||
remote_id,
|
||||
shortcode: emoji_upload.shortcode,
|
||||
domain: None,
|
||||
media_attachment_id: attachment.id,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let custom_emoji = with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id,
|
||||
remote_id,
|
||||
shortcode: emoji_upload.shortcode,
|
||||
domain: None,
|
||||
media_attachment_id: attachment.id,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(custom_emoji)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
use diesel_async::pooled_connection::bb8;
|
||||
use std::{
|
||||
error::Error as StdError,
|
||||
fmt::{Debug, Display},
|
||||
};
|
||||
use std::{error::Error as StdError, fmt::Debug};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type BoxError = Box<dyn StdError + Send + Sync>;
|
||||
|
@ -71,7 +68,7 @@ pub enum Error {
|
|||
Event(kitsune_messaging::BoxError),
|
||||
|
||||
#[error(transparent)]
|
||||
Fetcher(BoxError),
|
||||
Fetcher(eyre::Report),
|
||||
|
||||
#[error(transparent)]
|
||||
Http(#[from] http::Error),
|
||||
|
@ -101,7 +98,7 @@ pub enum Error {
|
|||
PostProcessing(post_process::BoxError),
|
||||
|
||||
#[error(transparent)]
|
||||
Resolver(BoxError),
|
||||
Resolver(eyre::Report),
|
||||
|
||||
#[error(transparent)]
|
||||
Rsa(#[from] rsa::Error),
|
||||
|
@ -130,15 +127,3 @@ pub enum Error {
|
|||
#[error(transparent)]
|
||||
Validate(#[from] garde::Report),
|
||||
}
|
||||
|
||||
impl<E> From<kitsune_db::PoolError<E>> for Error
|
||||
where
|
||||
E: Into<Error> + Debug + Display,
|
||||
{
|
||||
fn from(value: kitsune_db::PoolError<E>) -> Self {
|
||||
match value {
|
||||
kitsune_db::PoolError::Pool(err) => err.into(),
|
||||
kitsune_db::PoolError::User(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,8 @@ use diesel::{ExpressionMethods, QueryDsl};
|
|||
use diesel_async::RunQueryDsl;
|
||||
use kitsune_db::{
|
||||
schema::{accounts, posts, users},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use smol_str::SmolStr;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -37,39 +36,29 @@ impl InstanceService {
|
|||
}
|
||||
|
||||
pub async fn known_instances(&self) -> Result<u64> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
accounts::table
|
||||
.filter(accounts::local.eq(false))
|
||||
.select(accounts::domain)
|
||||
.distinct()
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.await
|
||||
.map(|count| count as u64)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.filter(accounts::local.eq(false))
|
||||
.select(accounts::domain)
|
||||
.distinct()
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.await
|
||||
.map(|count| count as u64)
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub async fn local_post_count(&self) -> Result<u64> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts::table
|
||||
.filter(posts::is_local.eq(true))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.await
|
||||
.map(|count| count as u64)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.filter(posts::is_local.eq(true))
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.await
|
||||
.map(|count| count as u64)
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
@ -78,18 +67,13 @@ impl InstanceService {
|
|||
}
|
||||
|
||||
pub async fn user_count(&self) -> Result<u64> {
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
users::table
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.await
|
||||
.map(|count| count as u64)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
users::table
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.await
|
||||
.map(|count| count as u64)
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,8 @@ use garde::Validate;
|
|||
use kitsune_db::{
|
||||
model::notification::{NewNotification, Notification, NotificationType},
|
||||
schema::{accounts, accounts_follows, accounts_preferences, notifications, posts},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -104,15 +103,9 @@ impl NotificationService {
|
|||
.order(notifications::id.asc());
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|mut db_conn| {
|
||||
async move {
|
||||
Ok::<_, Error>(query.load_stream(&mut db_conn).await?.map_err(Error::from))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn get_notification_by_id(
|
||||
|
@ -120,61 +113,45 @@ impl NotificationService {
|
|||
id: Uuid,
|
||||
account_id: Uuid,
|
||||
) -> Result<Option<Notification>> {
|
||||
self.db_pool
|
||||
.with_connection(|mut db_conn| {
|
||||
async move {
|
||||
notifications::table
|
||||
.filter(
|
||||
notifications::id
|
||||
.eq(id)
|
||||
.and(notifications::receiving_account_id.eq(account_id)),
|
||||
)
|
||||
.select(Notification::as_select())
|
||||
.first(&mut db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
notifications::table
|
||||
.filter(
|
||||
notifications::id
|
||||
.eq(id)
|
||||
.and(notifications::receiving_account_id.eq(account_id)),
|
||||
)
|
||||
.select(Notification::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
pub async fn dismiss(&self, id: Uuid, account_id: Uuid) -> Result<()> {
|
||||
self.db_pool
|
||||
.with_connection(|mut db_conn| {
|
||||
async move {
|
||||
diesel::delete(
|
||||
notifications::table.filter(
|
||||
notifications::id
|
||||
.eq(id)
|
||||
.and(notifications::receiving_account_id.eq(account_id)),
|
||||
),
|
||||
)
|
||||
.execute(&mut db_conn)
|
||||
.await
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::delete(
|
||||
notifications::table.filter(
|
||||
notifications::id
|
||||
.eq(id)
|
||||
.and(notifications::receiving_account_id.eq(account_id)),
|
||||
),
|
||||
)
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn clear_all(&self, account_id: Uuid) -> Result<()> {
|
||||
self.db_pool
|
||||
.with_connection(|mut db_conn| {
|
||||
async move {
|
||||
diesel::delete(
|
||||
notifications::table
|
||||
.filter(notifications::receiving_account_id.eq(account_id)),
|
||||
)
|
||||
.execute(&mut db_conn)
|
||||
.await
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
diesel::delete(
|
||||
notifications::table.filter(notifications::receiving_account_id.eq(account_id)),
|
||||
)
|
||||
.execute(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ use kitsune_db::{
|
|||
posts_custom_emojis, posts_favourites, posts_media_attachments, posts_mentions,
|
||||
users_roles,
|
||||
},
|
||||
PgPool,
|
||||
with_connection, with_transaction, PgPool,
|
||||
};
|
||||
use kitsune_embed::Client as EmbedClient;
|
||||
use kitsune_jobs::deliver::{
|
||||
|
@ -47,7 +47,6 @@ use kitsune_language::Language;
|
|||
use kitsune_search::SearchBackend;
|
||||
use kitsune_url::UrlService;
|
||||
use kitsune_util::{process, sanitize::CleanHtmlExt};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -478,59 +477,48 @@ impl PostService {
|
|||
let id = Uuid::now_v7();
|
||||
let url = self.url_service.post_url(id);
|
||||
|
||||
let post = self
|
||||
.db_pool
|
||||
.with_transaction(move |tx| {
|
||||
async move {
|
||||
let in_reply_to_id = if let Some(in_reply_to_id) = create_post.in_reply_to_id {
|
||||
(posts::table
|
||||
.find(in_reply_to_id)
|
||||
.count()
|
||||
.get_result::<i64>(tx)
|
||||
.await?
|
||||
!= 0)
|
||||
.then_some(in_reply_to_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let post = with_transaction!(self.db_pool, |tx| {
|
||||
let in_reply_to_id = if let Some(in_reply_to_id) = create_post.in_reply_to_id {
|
||||
(posts::table
|
||||
.find(in_reply_to_id)
|
||||
.count()
|
||||
.get_result::<i64>(tx)
|
||||
.await?
|
||||
!= 0)
|
||||
.then_some(in_reply_to_id)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let post: Post = diesel::insert_into(posts::table)
|
||||
.values(NewPost {
|
||||
id,
|
||||
account_id: create_post.author_id,
|
||||
in_reply_to_id,
|
||||
reposted_post_id: None,
|
||||
subject: subject.as_deref(),
|
||||
content: resolved.content.as_str(),
|
||||
content_source: content_source.as_str(),
|
||||
content_lang: content_lang.into(),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: create_post.sensitive,
|
||||
visibility: create_post.visibility,
|
||||
is_local: true,
|
||||
url: url.as_str(),
|
||||
created_at: None,
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
let post: Post = diesel::insert_into(posts::table)
|
||||
.values(NewPost {
|
||||
id,
|
||||
account_id: create_post.author_id,
|
||||
in_reply_to_id,
|
||||
reposted_post_id: None,
|
||||
subject: subject.as_deref(),
|
||||
content: resolved.content.as_str(),
|
||||
content_source: content_source.as_str(),
|
||||
content_lang: content_lang.into(),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: create_post.sensitive,
|
||||
visibility: create_post.visibility,
|
||||
is_local: true,
|
||||
url: url.as_str(),
|
||||
created_at: None,
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
|
||||
Self::process_mentions(
|
||||
tx,
|
||||
post.account_id,
|
||||
post.id,
|
||||
resolved.mentioned_accounts,
|
||||
)
|
||||
.await?;
|
||||
Self::process_custom_emojis(tx, post.id, resolved.custom_emojis).await?;
|
||||
Self::process_media_attachments(tx, post.id, &create_post.media_ids).await?;
|
||||
NotificationService::notify_on_new_post(tx, post.account_id, post.id).await?;
|
||||
Self::process_mentions(tx, post.account_id, post.id, resolved.mentioned_accounts)
|
||||
.await?;
|
||||
Self::process_custom_emojis(tx, post.id, resolved.custom_emojis).await?;
|
||||
Self::process_media_attachments(tx, post.id, &create_post.media_ids).await?;
|
||||
NotificationService::notify_on_new_post(tx, post.account_id, post.id).await?;
|
||||
|
||||
Ok::<_, Error>(post)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
Ok::<_, Error>(post)
|
||||
})?;
|
||||
|
||||
self.job_service
|
||||
.enqueue(
|
||||
|
@ -653,37 +641,29 @@ impl PostService {
|
|||
None
|
||||
};
|
||||
|
||||
let post = self
|
||||
.db_pool
|
||||
.with_transaction(move |tx| {
|
||||
async move {
|
||||
let post: Post = diesel::update(posts::table)
|
||||
.set(PartialPostChangeset {
|
||||
id: update_post.post_id,
|
||||
subject: subject.as_deref(),
|
||||
content: content.as_deref(),
|
||||
content_source: update_post.content.as_deref(),
|
||||
content_lang: content_lang.map(Into::into),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: update_post.sensitive,
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
let post = with_transaction!(self.db_pool, |tx| {
|
||||
let post: Post = diesel::update(posts::table)
|
||||
.set(PartialPostChangeset {
|
||||
id: update_post.post_id,
|
||||
subject: subject.as_deref(),
|
||||
content: content.as_deref(),
|
||||
content_source: update_post.content.as_deref(),
|
||||
content_lang: content_lang.map(Into::into),
|
||||
link_preview_url: link_preview_url.as_deref(),
|
||||
is_sensitive: update_post.sensitive,
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
|
||||
Self::process_mentions(tx, post.account_id, post.id, mentioned_account_ids)
|
||||
.await?;
|
||||
Self::process_custom_emojis(tx, post.id, custom_emojis).await?;
|
||||
Self::process_media_attachments(tx, post.id, &update_post.media_ids).await?;
|
||||
NotificationService::notify_on_update_post(tx, post.account_id, post.id)
|
||||
.await?;
|
||||
Self::process_mentions(tx, post.account_id, post.id, mentioned_account_ids).await?;
|
||||
Self::process_custom_emojis(tx, post.id, custom_emojis).await?;
|
||||
Self::process_media_attachments(tx, post.id, &update_post.media_ids).await?;
|
||||
NotificationService::notify_on_update_post(tx, post.account_id, post.id).await?;
|
||||
|
||||
Ok::<_, Error>(post)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
Ok::<_, Error>(post)
|
||||
})?;
|
||||
|
||||
self.job_service
|
||||
.enqueue(
|
||||
|
@ -723,84 +703,69 @@ impl PostService {
|
|||
.fetching_account_id(Some(repost_post.account_id))
|
||||
.build();
|
||||
|
||||
let existing_repost: Option<Post> = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
posts::table
|
||||
.filter(
|
||||
posts::reposted_post_id
|
||||
.eq(repost_post.post_id)
|
||||
.and(posts::account_id.eq(repost_post.account_id)),
|
||||
)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let existing_repost: Option<Post> = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.filter(
|
||||
posts::reposted_post_id
|
||||
.eq(repost_post.post_id)
|
||||
.and(posts::account_id.eq(repost_post.account_id)),
|
||||
)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(repost) = existing_repost {
|
||||
return Ok(repost);
|
||||
}
|
||||
|
||||
let post: Post = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.find(repost_post.post_id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post: Post = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(repost_post.post_id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
let id = Uuid::now_v7();
|
||||
let url = self.url_service.post_url(id);
|
||||
|
||||
let repost = self
|
||||
.db_pool
|
||||
.with_transaction(|tx| {
|
||||
async move {
|
||||
let new_repost = diesel::insert_into(posts::table)
|
||||
.values(NewPost {
|
||||
id,
|
||||
account_id: repost_post.account_id,
|
||||
in_reply_to_id: None,
|
||||
reposted_post_id: Some(post.id),
|
||||
subject: None,
|
||||
content: "",
|
||||
content_source: "",
|
||||
content_lang: post.content_lang,
|
||||
link_preview_url: None,
|
||||
is_sensitive: post.is_sensitive,
|
||||
visibility: repost_post.visibility,
|
||||
is_local: true,
|
||||
url: url.as_str(),
|
||||
created_at: Some(Timestamp::now_utc()),
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
let repost = with_transaction!(self.db_pool, |tx| {
|
||||
let new_repost = diesel::insert_into(posts::table)
|
||||
.values(NewPost {
|
||||
id,
|
||||
account_id: repost_post.account_id,
|
||||
in_reply_to_id: None,
|
||||
reposted_post_id: Some(post.id),
|
||||
subject: None,
|
||||
content: "",
|
||||
content_source: "",
|
||||
content_lang: post.content_lang,
|
||||
link_preview_url: None,
|
||||
is_sensitive: post.is_sensitive,
|
||||
visibility: repost_post.visibility,
|
||||
is_local: true,
|
||||
url: url.as_str(),
|
||||
created_at: Some(Timestamp::now_utc()),
|
||||
})
|
||||
.returning(Post::as_returning())
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
|
||||
NotificationService::notify_on_repost(
|
||||
tx,
|
||||
post.account_id,
|
||||
new_repost.account_id,
|
||||
post.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok::<_, Error>(new_repost)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
NotificationService::notify_on_repost(
|
||||
tx,
|
||||
post.account_id,
|
||||
new_repost.account_id,
|
||||
post.id,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok::<_, Error>(new_repost)
|
||||
})?;
|
||||
|
||||
self.job_service
|
||||
.enqueue(
|
||||
Enqueue::builder()
|
||||
|
@ -830,21 +795,18 @@ impl PostService {
|
|||
.fetching_account_id(Some(unrepost_post.account_id))
|
||||
.build();
|
||||
|
||||
let post: Post = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.filter(
|
||||
posts::account_id
|
||||
.eq(unrepost_post.account_id)
|
||||
.and(posts::reposted_post_id.eq(unrepost_post.post_id)),
|
||||
)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post: Post = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.filter(
|
||||
posts::account_id
|
||||
.eq(unrepost_post.account_id)
|
||||
.and(posts::reposted_post_id.eq(unrepost_post.post_id)),
|
||||
)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
self.job_service
|
||||
.enqueue(
|
||||
|
@ -875,65 +837,57 @@ impl PostService {
|
|||
.fetching_account_id(Some(favouriting_account_id))
|
||||
.build();
|
||||
|
||||
let post: Post = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.find(post_id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post: Post = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(post_id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
let id = Uuid::now_v7();
|
||||
let url = self.url_service.favourite_url(id);
|
||||
let favourite_id = self
|
||||
.db_pool
|
||||
.with_transaction(|tx| {
|
||||
async move {
|
||||
let favourite = diesel::insert_into(posts_favourites::table)
|
||||
.values(NewFavourite {
|
||||
id,
|
||||
account_id: favouriting_account_id,
|
||||
post_id: post.id,
|
||||
url,
|
||||
created_at: None,
|
||||
})
|
||||
.returning(posts_favourites::id)
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
|
||||
let account_id = accounts::table
|
||||
.inner_join(accounts_preferences::table)
|
||||
.filter(
|
||||
accounts::id
|
||||
.eq(post.account_id)
|
||||
.and(accounts_preferences::notify_on_favourite.eq(true)),
|
||||
)
|
||||
.select(accounts::id)
|
||||
.get_result::<Uuid>(tx)
|
||||
.await
|
||||
.optional()?;
|
||||
let favourite_id = with_transaction!(self.db_pool, |tx| {
|
||||
let favourite = diesel::insert_into(posts_favourites::table)
|
||||
.values(NewFavourite {
|
||||
id,
|
||||
account_id: favouriting_account_id,
|
||||
post_id: post.id,
|
||||
url,
|
||||
created_at: None,
|
||||
})
|
||||
.returning(posts_favourites::id)
|
||||
.get_result(tx)
|
||||
.await?;
|
||||
|
||||
if let Some(account_id) = account_id {
|
||||
diesel::insert_into(notifications::table)
|
||||
.values(
|
||||
NewNotification::builder()
|
||||
.receiving_account_id(account_id)
|
||||
.favourite(favouriting_account_id, post.id),
|
||||
)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(tx)
|
||||
.await?;
|
||||
}
|
||||
let account_id = accounts::table
|
||||
.inner_join(accounts_preferences::table)
|
||||
.filter(
|
||||
accounts::id
|
||||
.eq(post.account_id)
|
||||
.and(accounts_preferences::notify_on_favourite.eq(true)),
|
||||
)
|
||||
.select(accounts::id)
|
||||
.get_result::<Uuid>(tx)
|
||||
.await
|
||||
.optional()?;
|
||||
|
||||
Ok::<_, Error>(favourite)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
if let Some(account_id) = account_id {
|
||||
diesel::insert_into(notifications::table)
|
||||
.values(
|
||||
NewNotification::builder()
|
||||
.receiving_account_id(account_id)
|
||||
.favourite(favouriting_account_id, post.id),
|
||||
)
|
||||
.on_conflict_do_nothing()
|
||||
.execute(tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
Ok::<_, Error>(favourite)
|
||||
})?;
|
||||
|
||||
self.job_service
|
||||
.enqueue(
|
||||
|
@ -956,19 +910,13 @@ impl PostService {
|
|||
.get_by_id(post_id, Some(favouriting_account_id))
|
||||
.await?;
|
||||
|
||||
let favourite = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
Favourite::belonging_to(&post)
|
||||
.filter(posts_favourites::account_id.eq(favouriting_account_id))
|
||||
.get_result::<Favourite>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let favourite = with_connection!(self.db_pool, |db_conn| {
|
||||
Favourite::belonging_to(&post)
|
||||
.filter(posts_favourites::account_id.eq(favouriting_account_id))
|
||||
.get_result::<Favourite>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
})?;
|
||||
|
||||
if let Some(favourite) = favourite {
|
||||
self.job_service
|
||||
|
@ -1018,15 +966,9 @@ impl PostService {
|
|||
.order(posts_favourites::id.asc());
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get accounts that reblogged a post
|
||||
|
@ -1067,15 +1009,10 @@ impl PostService {
|
|||
.order(accounts::id.desc())
|
||||
.limit(get_reblogs.limit as i64);
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Get a post by its ID
|
||||
|
@ -1090,17 +1027,15 @@ impl PostService {
|
|||
.fetching_account_id(fetching_account_id)
|
||||
.build();
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.find(id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Get a post's source by its ID
|
||||
|
@ -1116,17 +1051,15 @@ impl PostService {
|
|||
.fetching_account_id(fetching_account_id)
|
||||
.build();
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.find(id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(PostSource::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(PostSource::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
})
|
||||
.map_err(Error::from)
|
||||
}
|
||||
|
||||
/// Get the ancestors of the post
|
||||
|
@ -1139,6 +1072,19 @@ impl PostService {
|
|||
id: Uuid,
|
||||
fetching_account_id: Option<Uuid>,
|
||||
) -> impl Stream<Item = Result<Post>> + '_ {
|
||||
let load_post = move |in_reply_to_id, permission_check| async move {
|
||||
let post = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(in_reply_to_id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok::<_, Error>(post)
|
||||
};
|
||||
|
||||
try_stream! {
|
||||
let mut last_post = self.get_by_id(id, fetching_account_id).await?;
|
||||
let permission_check = PermissionCheck::builder()
|
||||
|
@ -1146,16 +1092,7 @@ impl PostService {
|
|||
.build();
|
||||
|
||||
while let Some(in_reply_to_id) = last_post.in_reply_to_id {
|
||||
let post = self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.find(in_reply_to_id)
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post = load_post(in_reply_to_id, permission_check).await?;
|
||||
|
||||
yield post.clone();
|
||||
|
||||
|
@ -1175,22 +1112,25 @@ impl PostService {
|
|||
id: Uuid,
|
||||
fetching_account_id: Option<Uuid>,
|
||||
) -> BoxStream<'_, Result<Post>> {
|
||||
let load_post = move |id, permission_check| async move {
|
||||
let post = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.filter(posts::in_reply_to_id.eq(id))
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.load_stream::<Post>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok::<_, Error>(post)
|
||||
};
|
||||
|
||||
try_stream! {
|
||||
let permission_check = PermissionCheck::builder()
|
||||
.fetching_account_id(fetching_account_id)
|
||||
.build();
|
||||
|
||||
let descendant_stream = self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.filter(posts::in_reply_to_id.eq(id))
|
||||
.add_post_permission_check(permission_check)
|
||||
.select(Post::as_select())
|
||||
.load_stream::<Post>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
|
||||
let descendant_stream = load_post(id, permission_check).await?;
|
||||
for await descendant in descendant_stream {
|
||||
let descendant = descendant?;
|
||||
let descendant_id = descendant.id;
|
||||
|
@ -1212,33 +1152,27 @@ impl PostService {
|
|||
account_id: Uuid,
|
||||
user_id: Option<Uuid>,
|
||||
) -> Result<Post> {
|
||||
let post: Post = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
posts::table
|
||||
.find(post_id)
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let post: Post = with_connection!(self.db_pool, |db_conn| {
|
||||
posts::table
|
||||
.find(post_id)
|
||||
.select(Post::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
if post.account_id != account_id {
|
||||
if let Some(user_id) = user_id {
|
||||
let admin_role_count = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
users_roles::table
|
||||
.filter(
|
||||
users_roles::user_id
|
||||
.eq(user_id)
|
||||
.and(users_roles::role.eq(Role::Administrator)),
|
||||
)
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let admin_role_count = with_connection!(self.db_pool, |db_conn| {
|
||||
users_roles::table
|
||||
.filter(
|
||||
users_roles::user_id
|
||||
.eq(user_id)
|
||||
.and(users_roles::role.eq(Role::Administrator)),
|
||||
)
|
||||
.count()
|
||||
.get_result::<i64>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
if admin_role_count == 0 {
|
||||
return Err(PostError::Unauthorised.into());
|
||||
|
|
|
@ -126,6 +126,7 @@ mod test {
|
|||
account::Account, custom_emoji::CustomEmoji, media_attachment::NewMediaAttachment,
|
||||
},
|
||||
schema::{accounts, custom_emojis, media_attachments},
|
||||
with_connection_panicky,
|
||||
};
|
||||
use kitsune_federation_filter::FederationFilter;
|
||||
use kitsune_http_client::Client;
|
||||
|
@ -137,7 +138,6 @@ mod test {
|
|||
use kitsune_util::try_join;
|
||||
use kitsune_webfinger::Webfinger;
|
||||
use pretty_assertions::assert_eq;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use std::sync::Arc;
|
||||
use tower::service_fn;
|
||||
|
@ -221,69 +221,63 @@ mod test {
|
|||
|
||||
let emoji_ids = (Uuid::now_v7(), Uuid::now_v7());
|
||||
let media_attachment_ids = (Uuid::now_v7(), Uuid::now_v7());
|
||||
db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
let media_fut = diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: media_attachment_ids.0,
|
||||
content_type: "image/jpeg",
|
||||
account_id: None,
|
||||
description: None,
|
||||
blurhash: None,
|
||||
file_path: None,
|
||||
remote_url: None,
|
||||
})
|
||||
.execute(db_conn);
|
||||
let emoji_fut = diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id: emoji_ids.0,
|
||||
shortcode: String::from("blobhaj_happy"),
|
||||
domain: None,
|
||||
remote_id: String::from("https://local.domain/emoji/blobhaj_happy"),
|
||||
media_attachment_id: media_attachment_ids.0,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc()
|
||||
})
|
||||
.execute(db_conn);
|
||||
try_join!(media_fut, emoji_fut)
|
||||
}.scoped()
|
||||
})
|
||||
.await
|
||||
.expect("Failed to insert the local emoji");
|
||||
|
||||
db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async {
|
||||
let media_fut = diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: media_attachment_ids.1,
|
||||
content_type: "image/jpeg",
|
||||
account_id: None,
|
||||
description: None,
|
||||
blurhash: None,
|
||||
file_path: None,
|
||||
remote_url: Some("https://media.example.com/emojis/blobhaj.jpeg"),
|
||||
})
|
||||
.execute(db_conn);
|
||||
let emoji_fut = diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id: emoji_ids.1,
|
||||
shortcode: String::from("blobhaj_sad"),
|
||||
domain: Some(String::from("example.com")),
|
||||
remote_id: String::from("https://example.com/emojis/1"),
|
||||
media_attachment_id: media_attachment_ids.1,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.execute(db_conn);
|
||||
try_join!(media_fut, emoji_fut)
|
||||
}.scoped()
|
||||
})
|
||||
.await
|
||||
.expect("Failed to insert the remote emoji");
|
||||
with_connection_panicky!(db_pool, |db_conn| {
|
||||
let media_fut = diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: media_attachment_ids.0,
|
||||
content_type: "image/jpeg",
|
||||
account_id: None,
|
||||
description: None,
|
||||
blurhash: None,
|
||||
file_path: None,
|
||||
remote_url: None,
|
||||
})
|
||||
.execute(db_conn);
|
||||
let emoji_fut = diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id: emoji_ids.0,
|
||||
shortcode: String::from("blobhaj_happy"),
|
||||
domain: None,
|
||||
remote_id: String::from("https://local.domain/emoji/blobhaj_happy"),
|
||||
media_attachment_id: media_attachment_ids.0,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc()
|
||||
})
|
||||
.execute(db_conn);
|
||||
|
||||
try_join!(media_fut, emoji_fut)
|
||||
})
|
||||
.expect("Failed to insert the local emoji");
|
||||
|
||||
with_connection_panicky!(db_pool, |db_conn| {
|
||||
let media_fut = diesel::insert_into(media_attachments::table)
|
||||
.values(NewMediaAttachment {
|
||||
id: media_attachment_ids.1,
|
||||
content_type: "image/jpeg",
|
||||
account_id: None,
|
||||
description: None,
|
||||
blurhash: None,
|
||||
file_path: None,
|
||||
remote_url: Some("https://media.example.com/emojis/blobhaj.jpeg"),
|
||||
})
|
||||
.execute(db_conn);
|
||||
let emoji_fut = diesel::insert_into(custom_emojis::table)
|
||||
.values(CustomEmoji {
|
||||
id: emoji_ids.1,
|
||||
shortcode: String::from("blobhaj_sad"),
|
||||
domain: Some(String::from("example.com")),
|
||||
remote_id: String::from("https://example.com/emojis/1"),
|
||||
media_attachment_id: media_attachment_ids.1,
|
||||
endorsed: false,
|
||||
created_at: Timestamp::now_utc(),
|
||||
updated_at: Timestamp::now_utc(),
|
||||
})
|
||||
.execute(db_conn);
|
||||
try_join!(media_fut, emoji_fut)
|
||||
})
|
||||
.expect("Failed to insert the remote emoji");
|
||||
|
||||
let post_resolver = PostResolver::builder()
|
||||
.account(account_service)
|
||||
|
@ -300,16 +294,14 @@ mod test {
|
|||
assert_eq!(resolved.custom_emojis.len(), 2);
|
||||
|
||||
let (account_id, _mention_text) = &resolved.mentioned_accounts[0];
|
||||
let mentioned_account = db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.find(account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result::<Account>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.expect("Failed to fetch account");
|
||||
let mentioned_account = with_connection_panicky!(db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.find(account_id)
|
||||
.select(Account::as_select())
|
||||
.get_result::<Account>(db_conn)
|
||||
.await
|
||||
})
|
||||
.expect("Failed to fetch account");
|
||||
|
||||
assert_eq!(mentioned_account.username, "0x0");
|
||||
assert_eq!(mentioned_account.domain, "corteximplant.com");
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use eyre::WrapErr;
|
||||
use kitsune_cache::{ArcCache, InMemoryCache, NoopCache, RedisCache};
|
||||
use kitsune_captcha::AnyCaptcha;
|
||||
use kitsune_captcha::{hcaptcha::Captcha as HCaptcha, mcaptcha::Captcha as MCaptcha};
|
||||
|
@ -12,7 +13,6 @@ use kitsune_messaging::{
|
|||
};
|
||||
use kitsune_search::{AnySearchBackend, NoopSearchService, SqlSearchService};
|
||||
use kitsune_storage::{fs::Storage as FsStorage, s3::Storage as S3Storage, AnyStorageBackend};
|
||||
use miette::{Context, IntoDiagnostic};
|
||||
use multiplex_pool::RoundRobinStrategy;
|
||||
use redis::aio::ConnectionManager;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
|
@ -22,7 +22,7 @@ use tokio::sync::OnceCell;
|
|||
pub async fn cache<K, V>(
|
||||
config: &cache::Configuration,
|
||||
cache_name: &str,
|
||||
) -> miette::Result<ArcCache<K, V>>
|
||||
) -> eyre::Result<ArcCache<K, V>>
|
||||
where
|
||||
K: Display + Send + Sync + ?Sized + 'static,
|
||||
V: Clone + DeserializeOwned + Serialize + Send + Sync + 'static,
|
||||
|
@ -45,8 +45,7 @@ where
|
|||
)
|
||||
.await
|
||||
})
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
RedisCache::builder()
|
||||
.prefix(cache_name)
|
||||
|
@ -79,7 +78,7 @@ pub fn captcha(config: &captcha::Configuration) -> AnyCaptcha {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn storage(config: &storage::Configuration) -> miette::Result<AnyStorageBackend> {
|
||||
pub fn storage(config: &storage::Configuration) -> eyre::Result<AnyStorageBackend> {
|
||||
let storage = match config {
|
||||
storage::Configuration::Fs(ref fs_config) => {
|
||||
FsStorage::new(fs_config.upload_dir.as_str().into()).into()
|
||||
|
@ -96,12 +95,11 @@ pub fn storage(config: &storage::Configuration) -> miette::Result<AnyStorageBack
|
|||
s3_config.secret_access_key.as_str(),
|
||||
);
|
||||
let s3_bucket = rusty_s3::Bucket::new(
|
||||
s3_config.endpoint_url.parse().into_diagnostic()?,
|
||||
s3_config.endpoint_url.parse()?,
|
||||
path_style,
|
||||
s3_config.bucket_name.to_string(),
|
||||
s3_config.region.to_string(),
|
||||
)
|
||||
.into_diagnostic()?;
|
||||
)?;
|
||||
|
||||
S3Storage::new(s3_bucket, s3_credentials).into()
|
||||
}
|
||||
|
@ -112,13 +110,12 @@ pub fn storage(config: &storage::Configuration) -> miette::Result<AnyStorageBack
|
|||
|
||||
pub fn mail_sender(
|
||||
config: &email::Configuration,
|
||||
) -> miette::Result<MailSender<AsyncSmtpTransport<Tokio1Executor>>> {
|
||||
) -> eyre::Result<MailSender<AsyncSmtpTransport<Tokio1Executor>>> {
|
||||
let transport_builder = if config.starttls {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(config.host.as_str())
|
||||
} else {
|
||||
AsyncSmtpTransport::<Tokio1Executor>::relay(config.host.as_str())
|
||||
}
|
||||
.into_diagnostic()?;
|
||||
}?;
|
||||
|
||||
let transport = transport_builder
|
||||
.credentials((config.username.as_str(), config.password.as_str()).into())
|
||||
|
@ -126,11 +123,11 @@ pub fn mail_sender(
|
|||
|
||||
Ok(MailSender::builder()
|
||||
.backend(transport)
|
||||
.from_mailbox(Mailbox::from_str(config.from_address.as_str()).into_diagnostic()?)
|
||||
.from_mailbox(Mailbox::from_str(config.from_address.as_str())?)
|
||||
.build())
|
||||
}
|
||||
|
||||
pub async fn messaging(config: &messaging::Configuration) -> miette::Result<MessagingHub> {
|
||||
pub async fn messaging(config: &messaging::Configuration) -> eyre::Result<MessagingHub> {
|
||||
let backend = match config {
|
||||
messaging::Configuration::InProcess => {
|
||||
MessagingHub::new(TokioBroadcastMessagingBackend::default())
|
||||
|
@ -138,7 +135,6 @@ pub async fn messaging(config: &messaging::Configuration) -> miette::Result<Mess
|
|||
messaging::Configuration::Redis(ref redis_config) => {
|
||||
let redis_messaging_backend = RedisMessagingBackend::new(&redis_config.url)
|
||||
.await
|
||||
.into_diagnostic()
|
||||
.wrap_err("Failed to initialise Redis messaging backend")?;
|
||||
|
||||
MessagingHub::new(redis_messaging_backend)
|
||||
|
@ -153,7 +149,7 @@ pub async fn search(
|
|||
search_config: &search::Configuration,
|
||||
language_detection_config: language_detection::Configuration,
|
||||
db_pool: &PgPool,
|
||||
) -> miette::Result<AnySearchBackend> {
|
||||
) -> eyre::Result<AnySearchBackend> {
|
||||
let service = match search_config {
|
||||
search::Configuration::Meilisearch(_config) => {
|
||||
#[cfg(not(feature = "meilisearch"))]
|
||||
|
|
|
@ -8,10 +8,9 @@ use kitsune_core::{consts::API_MAX_LIMIT, traits::Fetcher};
|
|||
use kitsune_db::{
|
||||
model::{account::Account, post::Post},
|
||||
schema::{accounts, posts},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use kitsune_search::{SearchBackend, SearchIndex};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use std::sync::Arc;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
@ -97,30 +96,27 @@ impl SearchService {
|
|||
.try_concat()
|
||||
.await?;
|
||||
|
||||
let search_backend_results = self
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
result_references
|
||||
.iter()
|
||||
.map(|result| match result.index {
|
||||
SearchIndex::Account => accounts::table
|
||||
.find(result.id)
|
||||
.select(Account::as_select())
|
||||
.get_result::<Account>(db_conn)
|
||||
.map_ok(SearchResult::Account)
|
||||
.left_future(),
|
||||
SearchIndex::Post => posts::table
|
||||
.find(result.id)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.map_ok(SearchResult::Post)
|
||||
.right_future(),
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<SearchResult>>()
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let search_backend_results = with_connection!(self.db_pool, |db_conn| {
|
||||
result_references
|
||||
.iter()
|
||||
.map(|result| match result.index {
|
||||
SearchIndex::Account => accounts::table
|
||||
.find(result.id)
|
||||
.select(Account::as_select())
|
||||
.get_result::<Account>(db_conn)
|
||||
.map_ok(SearchResult::Account)
|
||||
.left_future(),
|
||||
SearchIndex::Post => posts::table
|
||||
.find(result.id)
|
||||
.select(Post::as_select())
|
||||
.get_result::<Post>(db_conn)
|
||||
.map_ok(SearchResult::Post)
|
||||
.right_future(),
|
||||
})
|
||||
.collect::<FuturesUnordered<_>>()
|
||||
.try_collect::<Vec<SearchResult>>()
|
||||
.await
|
||||
})?;
|
||||
|
||||
results.extend(search_backend_results);
|
||||
|
||||
|
|
|
@ -8,9 +8,8 @@ use kitsune_db::{
|
|||
model::post::{Post, Visibility},
|
||||
post_permission_check::{PermissionCheck, PostPermissionCheckExt},
|
||||
schema::{accounts_follows, posts, posts_mentions},
|
||||
PgPool,
|
||||
with_connection, PgPool,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
||||
|
@ -121,15 +120,9 @@ impl TimelineService {
|
|||
query = query.filter(posts::id.gt(min_id)).order(posts::id.asc());
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a stream of public posts
|
||||
|
@ -169,14 +162,8 @@ impl TimelineService {
|
|||
query = query.filter(posts::is_local.eq(false));
|
||||
}
|
||||
|
||||
self.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
with_connection!(self.db_pool, |db_conn| {
|
||||
Ok::<_, Error>(query.load_stream(db_conn).await?.map_err(Error::from))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use kitsune_db::{
|
|||
user::{NewUser, User},
|
||||
},
|
||||
schema::{accounts, accounts_preferences, users},
|
||||
PgPool,
|
||||
with_transaction, PgPool,
|
||||
};
|
||||
use kitsune_jobs::mailing::confirmation::SendConfirmationMail;
|
||||
use kitsune_url::UrlService;
|
||||
|
@ -24,7 +24,6 @@ use rsa::{
|
|||
pkcs8::{EncodePrivateKey, EncodePublicKey, LineEnding},
|
||||
RsaPrivateKey,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use std::fmt::Write;
|
||||
use typed_builder::TypedBuilder;
|
||||
|
@ -167,67 +166,61 @@ impl UserService {
|
|||
let url = self.url_service.user_url(account_id);
|
||||
let public_key_id = self.url_service.public_key_id(account_id);
|
||||
|
||||
let new_user = self
|
||||
.db_pool
|
||||
.with_transaction(|tx| {
|
||||
async move {
|
||||
let account_fut = diesel::insert_into(accounts::table)
|
||||
.values(NewAccount {
|
||||
id: account_id,
|
||||
display_name: None,
|
||||
username: register.username.as_str(),
|
||||
locked: false,
|
||||
note: None,
|
||||
local: true,
|
||||
domain: domain.as_str(),
|
||||
actor_type: ActorType::Person,
|
||||
url: url.as_str(),
|
||||
featured_collection_url: None,
|
||||
followers_url: None,
|
||||
following_url: None,
|
||||
inbox_url: None,
|
||||
outbox_url: None,
|
||||
shared_inbox_url: None,
|
||||
public_key_id: public_key_id.as_str(),
|
||||
public_key: public_key_str.as_str(),
|
||||
created_at: None,
|
||||
})
|
||||
.execute(tx);
|
||||
let new_user = with_transaction!(self.db_pool, |tx| {
|
||||
let account_fut = diesel::insert_into(accounts::table)
|
||||
.values(NewAccount {
|
||||
id: account_id,
|
||||
display_name: None,
|
||||
username: register.username.as_str(),
|
||||
locked: false,
|
||||
note: None,
|
||||
local: true,
|
||||
domain: domain.as_str(),
|
||||
actor_type: ActorType::Person,
|
||||
url: url.as_str(),
|
||||
featured_collection_url: None,
|
||||
followers_url: None,
|
||||
following_url: None,
|
||||
inbox_url: None,
|
||||
outbox_url: None,
|
||||
shared_inbox_url: None,
|
||||
public_key_id: public_key_id.as_str(),
|
||||
public_key: public_key_str.as_str(),
|
||||
created_at: None,
|
||||
})
|
||||
.execute(tx);
|
||||
|
||||
let confirmation_token = generate_secret();
|
||||
let user_fut = diesel::insert_into(users::table)
|
||||
.values(NewUser {
|
||||
id: Uuid::now_v7(),
|
||||
account_id,
|
||||
username: register.username.as_str(),
|
||||
oidc_id: register.oidc_id.as_deref(),
|
||||
email: register.email.as_str(),
|
||||
password: hashed_password.as_deref(),
|
||||
domain: domain.as_str(),
|
||||
private_key: private_key_str.as_str(),
|
||||
confirmation_token: confirmation_token.as_str(),
|
||||
})
|
||||
.get_result::<User>(tx);
|
||||
let confirmation_token = generate_secret();
|
||||
let user_fut = diesel::insert_into(users::table)
|
||||
.values(NewUser {
|
||||
id: Uuid::now_v7(),
|
||||
account_id,
|
||||
username: register.username.as_str(),
|
||||
oidc_id: register.oidc_id.as_deref(),
|
||||
email: register.email.as_str(),
|
||||
password: hashed_password.as_deref(),
|
||||
domain: domain.as_str(),
|
||||
private_key: private_key_str.as_str(),
|
||||
confirmation_token: confirmation_token.as_str(),
|
||||
})
|
||||
.get_result::<User>(tx);
|
||||
|
||||
let preferences_fut = diesel::insert_into(accounts_preferences::table)
|
||||
.values(Preferences {
|
||||
account_id,
|
||||
notify_on_follow: true,
|
||||
notify_on_follow_request: true,
|
||||
notify_on_repost: false,
|
||||
notify_on_favourite: false,
|
||||
notify_on_mention: true,
|
||||
notify_on_post_update: true,
|
||||
})
|
||||
.execute(tx);
|
||||
let preferences_fut = diesel::insert_into(accounts_preferences::table)
|
||||
.values(Preferences {
|
||||
account_id,
|
||||
notify_on_follow: true,
|
||||
notify_on_follow_request: true,
|
||||
notify_on_repost: false,
|
||||
notify_on_favourite: false,
|
||||
notify_on_mention: true,
|
||||
notify_on_post_update: true,
|
||||
})
|
||||
.execute(tx);
|
||||
|
||||
let (_, user, _) = try_join!(account_fut, user_fut, preferences_fut)?;
|
||||
let (_, user, _) = try_join!(account_fut, user_fut, preferences_fut)?;
|
||||
|
||||
Ok::<_, Error>(user)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
Ok::<_, Error>(user)
|
||||
})?;
|
||||
|
||||
self.job_service
|
||||
.enqueue(
|
||||
|
|
|
@ -11,12 +11,12 @@ derive_more = { version = "1.0.0-beta.6", features = ["from"] }
|
|||
futures-util = "0.3.30"
|
||||
kitsune-s3 = { path = "../kitsune-s3" }
|
||||
rusty-s3 = { version = "0.5.0", default-features = false }
|
||||
tokio = { version = "1.36.0", features = ["fs", "io-util"] }
|
||||
tokio = { version = "1.37.0", features = ["fs", "io-util"] }
|
||||
tokio-util = { version = "0.7.10", features = ["io"] }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.10.1"
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -16,7 +16,7 @@ kitsune-config = { path = "../kitsune-config" }
|
|||
kitsune-db = { path = "../kitsune-db" }
|
||||
kitsune-s3 = { path = "../kitsune-s3" }
|
||||
multiplex-pool = { path = "../../lib/multiplex-pool" }
|
||||
pin-project-lite = "0.2.13"
|
||||
pin-project-lite = "0.2.14"
|
||||
rand = "0.8.5"
|
||||
redis = { version = "0.25.2", default-features = false, features = [
|
||||
"connection-manager",
|
||||
|
@ -29,7 +29,7 @@ testcontainers-modules = { version = "0.3.6", features = [
|
|||
"postgres",
|
||||
"redis",
|
||||
] }
|
||||
tokio = { version = "1.36.0", features = ["time"] }
|
||||
tokio = { version = "1.37.0", features = ["time"] }
|
||||
url = "2.5.0"
|
||||
uuid = { version = "1.8.0", features = ["fast-rng", "v4"] }
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ pulldown-cmark = { version = "0.10.0", default-features = false, features = [
|
|||
] }
|
||||
rand = "0.8.5"
|
||||
speedy-uuid = { path = "../../lib/speedy-uuid" }
|
||||
tokio = { version = "1.36.0", features = ["macros"] }
|
||||
tokio = { version = "1.37.0", features = ["macros"] }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -8,14 +8,14 @@ build = "build.rs"
|
|||
|
||||
[dependencies]
|
||||
async-trait = "0.1.79"
|
||||
color-eyre = "0.6.3"
|
||||
derive_more = { version = "1.0.0-beta.6", features = ["from"] }
|
||||
enum_dispatch = "0.3.12"
|
||||
enum_dispatch = "0.3.13"
|
||||
futures-util = { version = "0.3.30", default-features = false, features = [
|
||||
"alloc",
|
||||
] }
|
||||
kitsune-config = { path = "../kitsune-config" }
|
||||
kitsune-type = { path = "../kitsune-type" }
|
||||
miette = "7.2.0"
|
||||
mrf-manifest = { path = "../../lib/mrf-manifest", features = ["decode"] }
|
||||
multiplex-pool = { path = "../../lib/multiplex-pool" }
|
||||
redis = { version = "0.25.2", default-features = false, features = [
|
||||
|
@ -27,7 +27,7 @@ slab = "0.4.9"
|
|||
sled = "0.34.7"
|
||||
smol_str = "0.2.1"
|
||||
thiserror = "1.0.58"
|
||||
tokio = { version = "1.36.0", features = ["fs"] }
|
||||
tokio = { version = "1.37.0", features = ["fs"] }
|
||||
tracing = "0.1.40"
|
||||
typed-builder = "0.18.1"
|
||||
walkdir = "2.5.0"
|
||||
|
@ -44,7 +44,7 @@ wasmtime-wasi = { version = "19.0.0", default-features = false }
|
|||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.10.1"
|
||||
tokio = { version = "1.36.0", features = ["macros", "rt"] }
|
||||
tokio = { version = "1.37.0", features = ["macros", "rt"] }
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
[lints]
|
||||
|
|
|
@ -11,7 +11,7 @@ crate-type = ["cdylib"]
|
|||
|
||||
[dependencies]
|
||||
rand = "0.8.5"
|
||||
wit-bindgen = "0.22.0"
|
||||
wit-bindgen = "0.23.0"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use miette::Diagnostic;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[derive(Debug, Error)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
Json(#[from] simd_json::Error),
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::{Backend, BoxError, BucketBackend};
|
||||
use miette::IntoDiagnostic;
|
||||
use color_eyre::eyre;
|
||||
use std::path::Path;
|
||||
|
||||
pub struct FsBackend {
|
||||
|
@ -7,12 +7,12 @@ pub struct FsBackend {
|
|||
}
|
||||
|
||||
impl FsBackend {
|
||||
pub fn from_path<P>(path: P) -> miette::Result<Self>
|
||||
pub fn from_path<P>(path: P) -> eyre::Result<Self>
|
||||
where
|
||||
P: AsRef<Path>,
|
||||
{
|
||||
Ok(Self {
|
||||
inner: sled::open(path).into_diagnostic()?,
|
||||
inner: sled::open(path)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use super::BoxError;
|
||||
use miette::IntoDiagnostic;
|
||||
use color_eyre::eyre;
|
||||
use redis::{aio::ConnectionManager, AsyncCommands};
|
||||
|
||||
const REDIS_NAMESPACE: &str = "MRF-KV-STORE";
|
||||
|
@ -9,14 +9,13 @@ pub struct RedisBackend {
|
|||
}
|
||||
|
||||
impl RedisBackend {
|
||||
pub async fn from_client(client: redis::Client, pool_size: usize) -> miette::Result<Self> {
|
||||
pub async fn from_client(client: redis::Client, pool_size: usize) -> eyre::Result<Self> {
|
||||
let pool = multiplex_pool::Pool::from_producer(
|
||||
|| client.get_connection_manager(),
|
||||
pool_size,
|
||||
multiplex_pool::RoundRobinStrategy::default(),
|
||||
)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
Ok(Self { pool })
|
||||
}
|
||||
|
|
|
@ -5,12 +5,12 @@ use self::{
|
|||
ctx::{construct_store, Context},
|
||||
mrf_wit::v1::fep::mrf::types::{Direction, Error as MrfError},
|
||||
};
|
||||
use futures_util::{stream::FuturesUnordered, Stream, StreamExt, TryFutureExt, TryStreamExt};
|
||||
use color_eyre::{eyre, Section};
|
||||
use futures_util::{stream::FuturesUnordered, Stream, TryFutureExt, TryStreamExt};
|
||||
use kitsune_config::mrf::{
|
||||
Configuration as MrfConfiguration, FsKvStorage, KvStorage, RedisKvStorage,
|
||||
};
|
||||
use kitsune_type::ap::Activity;
|
||||
use miette::{Diagnostic, IntoDiagnostic};
|
||||
use mrf_manifest::{Manifest, ManifestV1};
|
||||
use smol_str::SmolStr;
|
||||
use std::{
|
||||
|
@ -20,7 +20,6 @@ use std::{
|
|||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
};
|
||||
use thiserror::Error;
|
||||
use tokio::fs;
|
||||
use typed_builder::TypedBuilder;
|
||||
use walkdir::WalkDir;
|
||||
|
@ -64,14 +63,11 @@ fn load_mrf_module(
|
|||
engine: &Engine,
|
||||
module_path: &Path,
|
||||
bytes: &[u8],
|
||||
) -> miette::Result<Option<(ManifestV1<'static>, Component)>> {
|
||||
let component = Component::new(engine, bytes).map_err(|err| {
|
||||
miette::Report::new(ComponentParseError {
|
||||
path_help: format!("path to the module: {}", module_path.display()),
|
||||
advice: "Did you make the WASM file a component via `wasm-tools`?",
|
||||
})
|
||||
.wrap_err(err)
|
||||
})?;
|
||||
) -> eyre::Result<Option<(ManifestV1<'static>, Component)>> {
|
||||
let component = Component::new(engine, bytes)
|
||||
.map_err(eyre::Report::msg)
|
||||
.with_note(|| format!("path to the module: {}", module_path.display()))
|
||||
.suggestion("Did you make the WASM file a component via `wasm-tools`?")?;
|
||||
|
||||
let Some((manifest, _section_range)) = mrf_manifest::decode(bytes)? else {
|
||||
error!("missing manifest. skipping load.");
|
||||
|
@ -93,14 +89,6 @@ pub enum Outcome<'a> {
|
|||
Reject,
|
||||
}
|
||||
|
||||
#[derive(Debug, Diagnostic, Error)]
|
||||
#[error("{path_help}")]
|
||||
struct ComponentParseError {
|
||||
path_help: String,
|
||||
#[help]
|
||||
advice: &'static str,
|
||||
}
|
||||
|
||||
pub struct MrfModule {
|
||||
pub component: Component,
|
||||
pub config: SmolStr,
|
||||
|
@ -121,11 +109,11 @@ impl MrfService {
|
|||
engine: Engine,
|
||||
modules: Vec<MrfModule>,
|
||||
storage: kv_storage::BackendDispatch,
|
||||
) -> miette::Result<Self> {
|
||||
) -> eyre::Result<Self> {
|
||||
let mut linker = Linker::<Context>::new(&engine);
|
||||
|
||||
mrf_wit::v1::Mrf::add_to_linker(&mut linker, |ctx| ctx).map_err(miette::Report::msg)?;
|
||||
wasmtime_wasi::command::add_to_linker(&mut linker).map_err(miette::Report::msg)?;
|
||||
mrf_wit::v1::Mrf::add_to_linker(&mut linker, |ctx| ctx).map_err(eyre::Report::msg)?;
|
||||
wasmtime_wasi::command::add_to_linker(&mut linker).map_err(eyre::Report::msg)?;
|
||||
|
||||
Ok(Self {
|
||||
engine,
|
||||
|
@ -136,13 +124,13 @@ impl MrfService {
|
|||
}
|
||||
|
||||
#[instrument(skip_all, fields(module_dir = %config.module_dir))]
|
||||
pub async fn from_config(config: &MrfConfiguration) -> miette::Result<Self> {
|
||||
pub async fn from_config(config: &MrfConfiguration) -> eyre::Result<Self> {
|
||||
let storage = match config.storage {
|
||||
KvStorage::Fs(FsKvStorage { ref path }) => {
|
||||
kv_storage::FsBackend::from_path(path.as_str())?.into()
|
||||
}
|
||||
KvStorage::Redis(RedisKvStorage { ref url, pool_size }) => {
|
||||
let client = redis::Client::open(url.as_str()).into_diagnostic()?;
|
||||
let client = redis::Client::open(url.as_str())?;
|
||||
kv_storage::RedisBackend::from_client(client, pool_size.get())
|
||||
.await?
|
||||
.into()
|
||||
|
@ -155,9 +143,9 @@ impl MrfService {
|
|||
.async_support(true)
|
||||
.wasm_component_model(true);
|
||||
|
||||
let engine = Engine::new(&engine_config).map_err(miette::Report::msg)?;
|
||||
let engine = Engine::new(&engine_config).map_err(eyre::Report::msg)?;
|
||||
let wasm_data_stream = find_mrf_modules(config.module_dir.as_str())
|
||||
.map(IntoDiagnostic::into_diagnostic)
|
||||
.map_err(eyre::Report::from)
|
||||
.and_then(|(module_path, wasm_data)| {
|
||||
let engine = &engine;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ license.workspace = true
|
|||
[dependencies]
|
||||
async-trait = "0.1.79"
|
||||
autometrics = { version = "1.0.1", default-features = false }
|
||||
eyre = "0.6.12"
|
||||
futures-util = "0.3.30"
|
||||
http = "1.1.0"
|
||||
kitsune-cache = { path = "../kitsune-cache" }
|
||||
|
@ -28,7 +29,7 @@ http-body-util = "0.1.1"
|
|||
hyper = "1.2.0"
|
||||
pretty_assertions = "1.4.0"
|
||||
simd-json = "0.13.9"
|
||||
tokio = { version = "1.36.0", features = ["macros"] }
|
||||
tokio = { version = "1.37.0", features = ["macros"] }
|
||||
tower = { version = "0.4.13", default-features = false, features = ["util"] }
|
||||
|
||||
[lints]
|
||||
|
|
|
@ -6,9 +6,8 @@ use autometrics::autometrics;
|
|||
use futures_util::future::{FutureExt, OptionFuture};
|
||||
use http::{HeaderValue, StatusCode};
|
||||
use kitsune_cache::{ArcCache, CacheBackend, RedisCache};
|
||||
use kitsune_core::consts::USER_AGENT;
|
||||
use kitsune_core::{
|
||||
error::BoxError,
|
||||
consts::USER_AGENT,
|
||||
traits::{resolver::AccountResource, Resolver},
|
||||
};
|
||||
use kitsune_http_client::Client;
|
||||
|
@ -73,7 +72,7 @@ impl Resolver for Webfinger {
|
|||
&self,
|
||||
username: &str,
|
||||
domain: &str,
|
||||
) -> Result<Option<AccountResource>, BoxError> {
|
||||
) -> eyre::Result<Option<AccountResource>> {
|
||||
// XXX: Assigning the arguments to local bindings because the `#[instrument]` attribute
|
||||
// desugars to an `async move {}` block, inside which mutating the function arguments would
|
||||
// upset the borrowck
|
||||
|
|
18
flake.lock
18
flake.lock
|
@ -71,11 +71,11 @@
|
|||
"pre-commit-hooks": "pre-commit-hooks_2"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1711122363,
|
||||
"narHash": "sha256-Yw/t9HCY9U/EpkXlzW+5o/WEpZKUNgrSbcuHOZFpAXU=",
|
||||
"lastModified": 1711676035,
|
||||
"narHash": "sha256-m8AHpQR0WVVEoWWOpXVNvqfB5cXiMRnHfcYYm2zg7tk=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "63c7109f20b5ded0bc07f95ece9518bbb7fdea5b",
|
||||
"rev": "19098329b4e79f60705d7fb241a3617266658f98",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -468,11 +468,11 @@
|
|||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1711001935,
|
||||
"narHash": "sha256-URtGpHue7HHZK0mrHnSf8wJ6OmMKYSsoLmJybrOLFSQ=",
|
||||
"lastModified": 1711523803,
|
||||
"narHash": "sha256-UKcYiHWHQynzj6CN/vTcix4yd1eCu1uFdsuarupdCQQ=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "20f77aa09916374aa3141cbc605c955626762c9a",
|
||||
"rev": "2726f127c15a4cc9810843b96cad73c7eb39e443",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -581,11 +581,11 @@
|
|||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1711073443,
|
||||
"narHash": "sha256-PpNb4xq7U5Q/DdX40qe7CijUsqhVVM3VZrhN0+c6Lcw=",
|
||||
"lastModified": 1711678273,
|
||||
"narHash": "sha256-7lIB0hMRnfzx/9oSIwTnwXmVnbvVGRoadOCW+1HI5zY=",
|
||||
"owner": "oxalica",
|
||||
"repo": "rust-overlay",
|
||||
"rev": "eec55ba9fcde6be4c63942827247e42afef7fafe",
|
||||
"rev": "42a168449605950935f15ea546f6f770e5f7f629",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -14,16 +14,16 @@ eula = false
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.4", features = ["derive", "wrap_help"] }
|
||||
color-eyre = "0.6.3"
|
||||
diesel = "2.1.5"
|
||||
diesel-async = "0.4.1"
|
||||
dotenvy = "0.15.7"
|
||||
envy = "0.4.2"
|
||||
kitsune-config = { path = "../crates/kitsune-config" }
|
||||
kitsune-db = { path = "../crates/kitsune-db" }
|
||||
miette = { version = "7.2.0", features = ["fancy"] }
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
speedy-uuid = { path = "../lib/speedy-uuid" }
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tracing-subscriber = "0.3.18"
|
||||
|
||||
[build-dependencies]
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use self::{config::Configuration, role::RoleSubcommand};
|
||||
use clap::{Parser, Subcommand};
|
||||
use diesel_async::scoped_futures::ScopedFutureExt;
|
||||
use color_eyre::eyre::Result;
|
||||
use kitsune_config::database::Configuration as DatabaseConfig;
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
|
||||
mod config;
|
||||
mod role;
|
||||
|
@ -24,11 +23,11 @@ struct App {
|
|||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
miette::set_panic_hook();
|
||||
color_eyre::install()?;
|
||||
dotenvy::dotenv().ok();
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let config: Configuration = envy::from_env().into_diagnostic()?;
|
||||
let config: Configuration = envy::from_env()?;
|
||||
let db_conn = kitsune_db::connect(&DatabaseConfig {
|
||||
url: config.database_url.into(),
|
||||
max_connections: 1,
|
||||
|
@ -38,18 +37,10 @@ async fn main() -> Result<()> {
|
|||
|
||||
let cmd = App::parse();
|
||||
|
||||
db_conn
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
match cmd.subcommand {
|
||||
AppSubcommand::Role(cmd) => self::role::handle(cmd, db_conn).await?,
|
||||
}
|
||||
|
||||
Ok::<_, miette::Report>(())
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let mut db_conn = db_conn.get().await?;
|
||||
match cmd.subcommand {
|
||||
AppSubcommand::Role(cmd) => self::role::handle(cmd, &mut db_conn).await?,
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use clap::{Args, Subcommand, ValueEnum};
|
||||
use color_eyre::eyre::Result;
|
||||
use diesel::{BelongingToDsl, BoolExpressionMethods, ExpressionMethods, QueryDsl};
|
||||
use diesel_async::{AsyncPgConnection, RunQueryDsl};
|
||||
use kitsune_db::model::{
|
||||
user::User,
|
||||
user_role::{NewUserRole, Role as DbRole, UserRole},
|
||||
};
|
||||
use miette::{IntoDiagnostic, Result};
|
||||
use speedy_uuid::Uuid;
|
||||
|
||||
#[derive(Subcommand)]
|
||||
|
@ -57,8 +57,7 @@ async fn add_role(db_conn: &mut AsyncPgConnection, username_str: &str, role: Rol
|
|||
let user = users
|
||||
.filter(username.eq(username_str))
|
||||
.first::<User>(db_conn)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
let new_role = NewUserRole {
|
||||
id: Uuid::now_v7(),
|
||||
|
@ -69,8 +68,7 @@ async fn add_role(db_conn: &mut AsyncPgConnection, username_str: &str, role: Rol
|
|||
diesel::insert_into(users_roles::table)
|
||||
.values(&new_role)
|
||||
.execute(db_conn)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -81,13 +79,11 @@ async fn list_roles(db_conn: &mut AsyncPgConnection, username_str: &str) -> Resu
|
|||
let user: User = users::table
|
||||
.filter(users::username.eq(username_str))
|
||||
.first(db_conn)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
let roles = UserRole::belonging_to(&user)
|
||||
.load::<UserRole>(db_conn)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
println!("User \"{username_str}\" has the following roles:");
|
||||
for role in roles {
|
||||
|
@ -107,8 +103,7 @@ async fn remove_role(
|
|||
let user = users::table
|
||||
.filter(users::username.eq(username_str))
|
||||
.first::<User>(db_conn)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
diesel::delete(
|
||||
users_roles::table.filter(
|
||||
|
@ -118,8 +113,7 @@ async fn remove_role(
|
|||
),
|
||||
)
|
||||
.execute(db_conn)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ eula = false
|
|||
[dependencies]
|
||||
athena = { path = "../lib/athena" }
|
||||
clap = { version = "4.5.4", features = ["derive", "wrap_help"] }
|
||||
color-eyre = "0.6.3"
|
||||
just-retry = { path = "../lib/just-retry" }
|
||||
kitsune-config = { path = "../crates/kitsune-config" }
|
||||
kitsune-core = { path = "../crates/kitsune-core" }
|
||||
|
@ -26,7 +27,6 @@ kitsune-observability = { path = "../crates/kitsune-observability" }
|
|||
kitsune-service = { path = "../crates/kitsune-service" }
|
||||
kitsune-url = { path = "../crates/kitsune-url" }
|
||||
kitsune-wasm-mrf = { path = "../crates/kitsune-wasm-mrf" }
|
||||
miette = { version = "7.2.0", features = ["fancy"] }
|
||||
mimalloc = "0.1.39"
|
||||
multiplex-pool = { path = "../lib/multiplex-pool" }
|
||||
redis = { version = "0.25.2", default-features = false, features = [
|
||||
|
@ -34,7 +34,7 @@ redis = { version = "0.25.2", default-features = false, features = [
|
|||
"connection-manager",
|
||||
"tokio-rustls-comp",
|
||||
] }
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tracing = "0.1.40"
|
||||
typed-builder = "0.18.1"
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use clap::Parser;
|
||||
use color_eyre::eyre;
|
||||
use kitsune_config::Configuration;
|
||||
use kitsune_core::consts::VERSION;
|
||||
use kitsune_federation_filter::FederationFilter;
|
||||
|
@ -6,7 +7,6 @@ use kitsune_job_runner::JobDispatcherState;
|
|||
use kitsune_service::{attachment::AttachmentService, prepare};
|
||||
use kitsune_url::UrlService;
|
||||
use kitsune_wasm_mrf::MrfService;
|
||||
use miette::IntoDiagnostic;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[global_allocator]
|
||||
|
@ -22,8 +22,8 @@ struct Args {
|
|||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> miette::Result<()> {
|
||||
miette::set_panic_hook();
|
||||
async fn main() -> eyre::Result<()> {
|
||||
color_eyre::install()?;
|
||||
|
||||
let args = Args::parse();
|
||||
let config = Configuration::load(args.config).await?;
|
||||
|
@ -31,9 +31,8 @@ async fn main() -> miette::Result<()> {
|
|||
kitsune_observability::initialise(env!("CARGO_PKG_NAME"), &config)?;
|
||||
|
||||
let db_pool = kitsune_db::connect(&config.database).await?;
|
||||
let job_queue = kitsune_job_runner::prepare_job_queue(db_pool.clone(), &config.job_queue)
|
||||
.await
|
||||
.into_diagnostic()?;
|
||||
let job_queue =
|
||||
kitsune_job_runner::prepare_job_queue(db_pool.clone(), &config.job_queue).await?;
|
||||
|
||||
let mrf_service = MrfService::from_config(&config.mrf).await?;
|
||||
let url_service = UrlService::builder()
|
||||
|
|
|
@ -69,7 +69,6 @@ kitsune-util = { path = "../crates/kitsune-util" }
|
|||
kitsune-wasm-mrf = { path = "../crates/kitsune-wasm-mrf" }
|
||||
kitsune-webfinger = { path = "../crates/kitsune-webfinger" }
|
||||
metrics = "=0.22.0"
|
||||
miette = { version = "7.2.0", features = ["fancy"] }
|
||||
mimalloc = "0.1.39"
|
||||
mime = "0.3.17"
|
||||
mime_guess = { version = "2.0.4", default-features = false }
|
||||
|
@ -90,7 +89,7 @@ strum = { version = "0.26.2", features = ["derive", "phf"] }
|
|||
tempfile = "3.10.1"
|
||||
thiserror = "1.0.58"
|
||||
time = "0.3.34"
|
||||
tokio = { version = "1.36.0", features = ["full"] }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
tokio-util = { version = "0.7.10", features = ["compat"] }
|
||||
tower-stop-using-brave = { path = "../lib/tower-stop-using-brave" }
|
||||
tower-x-clacks-overhead = { path = "../lib/tower-x-clacks-overhead" }
|
||||
|
@ -126,6 +125,7 @@ kitsune-mastodon = { path = "../crates/kitsune-mastodon", optional = true }
|
|||
|
||||
# "oidc" feature
|
||||
kitsune-oidc = { path = "../crates/kitsune-oidc", optional = true }
|
||||
color-eyre = "0.6.3"
|
||||
|
||||
[build-dependencies]
|
||||
camino = "1.1.6"
|
||||
|
|
|
@ -4,14 +4,12 @@ use axum::{
|
|||
extract::multipart::MultipartError,
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use color_eyre::eyre;
|
||||
use diesel_async::pooled_connection::bb8;
|
||||
use http::StatusCode;
|
||||
use kitsune_core::error::{BoxError, HttpError};
|
||||
use kitsune_core::error::HttpError;
|
||||
use kitsune_service::error::{Error as ServiceError, PostError};
|
||||
use std::{
|
||||
fmt::{Debug, Display},
|
||||
str::ParseBoolError,
|
||||
};
|
||||
use std::{fmt::Debug, str::ParseBoolError};
|
||||
use thiserror::Error;
|
||||
|
||||
pub type Result<T, E = Error> = std::result::Result<T, E>;
|
||||
|
@ -41,7 +39,7 @@ pub enum Error {
|
|||
Der(#[from] der::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Fetcher(BoxError),
|
||||
Fetcher(eyre::Report),
|
||||
|
||||
#[error(transparent)]
|
||||
Http(#[from] http::Error),
|
||||
|
@ -115,18 +113,6 @@ pub enum OAuth2Error {
|
|||
Web(#[from] oxide_auth_axum::WebError),
|
||||
}
|
||||
|
||||
impl<E> From<kitsune_db::PoolError<E>> for Error
|
||||
where
|
||||
E: Into<Error> + Debug + Display,
|
||||
{
|
||||
fn from(value: kitsune_db::PoolError<E>) -> Self {
|
||||
match value {
|
||||
kitsune_db::PoolError::Pool(err) => err.into(),
|
||||
kitsune_db::PoolError::User(err) => err.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Error> for Response {
|
||||
fn from(err: Error) -> Response {
|
||||
err.into_response()
|
||||
|
|
|
@ -11,10 +11,11 @@ use diesel_async::RunQueryDsl;
|
|||
use headers::{authorization::Bearer, Authorization};
|
||||
use http::request::Parts;
|
||||
use kitsune_db::{
|
||||
catch_error,
|
||||
model::{account::Account, user::User},
|
||||
schema::{accounts, oauth2_access_tokens, users},
|
||||
with_connection,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
/// Mastodon-specific auth extractor alias
|
||||
|
@ -62,16 +63,14 @@ impl<const ENFORCE_EXPIRATION: bool> FromRequestParts<Zustand>
|
|||
.filter(oauth2_access_tokens::expires_at.gt(OffsetDateTime::now_utc()));
|
||||
}
|
||||
|
||||
let (user, account) = state
|
||||
.db_pool
|
||||
.with_connection(|db_conn| {
|
||||
user_account_query
|
||||
.select(<(User, Account)>::as_select())
|
||||
.get_result(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
let (user, account) = catch_error!(with_connection!(state.db_pool, |db_conn| {
|
||||
user_account_query
|
||||
.select(<(User, Account)>::as_select())
|
||||
.get_result(db_conn)
|
||||
.await
|
||||
.map_err(Error::from)
|
||||
}))
|
||||
.map_err(Error::from)??;
|
||||
|
||||
Ok(Self(UserData { account, user }))
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use diesel_async::RunQueryDsl;
|
|||
use http::StatusCode;
|
||||
use http_body_util::BodyExt;
|
||||
use kitsune_core::{error::HttpError, traits::fetcher::AccountFetchOptions};
|
||||
use kitsune_db::{model::account::Account, schema::accounts, PgPool};
|
||||
use kitsune_db::{model::account::Account, schema::accounts, with_connection, PgPool};
|
||||
use kitsune_type::ap::Activity;
|
||||
use kitsune_wasm_mrf::Outcome;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
|
@ -116,20 +116,18 @@ impl FromRequest<Zustand, Body> for SignedActivity {
|
|||
|
||||
async fn verify_signature(
|
||||
req: &http::Request<()>,
|
||||
db_conn: &PgPool,
|
||||
db_pool: &PgPool,
|
||||
expected_account: Option<&Account>,
|
||||
) -> Result<bool> {
|
||||
let is_valid = http_signatures::cavage::easy::verify(req, |key_id| {
|
||||
async move {
|
||||
let remote_user: Account = db_conn
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.filter(accounts::public_key_id.eq(key_id))
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let remote_user: Account = with_connection!(db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.filter(accounts::public_key_id.eq(key_id))
|
||||
.select(Account::as_select())
|
||||
.first(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
// If we have an expected account, which we have in the case of an incoming new activity,
|
||||
// then we do this comparison.
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::http::graphql::{types::Instance, ContextExt};
|
||||
use async_graphql::{Context, Object, Result};
|
||||
use kitsune_core::consts::VERSION;
|
||||
use std::convert::Into;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct InstanceQuery;
|
||||
|
|
|
@ -12,9 +12,9 @@ use kitsune_db::{
|
|||
account::Account as DbAccount, media_attachment::MediaAttachment as DbMediaAttachment,
|
||||
},
|
||||
schema::media_attachments,
|
||||
with_connection,
|
||||
};
|
||||
use kitsune_service::account::GetPosts;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
|
@ -41,20 +41,15 @@ impl Account {
|
|||
let db_pool = &ctx.state().db_pool;
|
||||
|
||||
if let Some(avatar_id) = self.avatar_id {
|
||||
db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
media_attachments::table
|
||||
.find(avatar_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
.map(|attachment| attachment.map(Into::into))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
with_connection!(db_pool, |db_conn| {
|
||||
media_attachments::table
|
||||
.find(avatar_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
.map(|attachment| attachment.map(Into::into))
|
||||
})
|
||||
.map_err(Into::into)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -64,20 +59,15 @@ impl Account {
|
|||
let db_pool = &ctx.state().db_pool;
|
||||
|
||||
if let Some(header_id) = self.header_id {
|
||||
db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
media_attachments::table
|
||||
.find(header_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
.map(|attachment| attachment.map(Into::into))
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
with_connection!(db_pool, |db_conn| {
|
||||
media_attachments::table
|
||||
.find(header_id)
|
||||
.get_result::<DbMediaAttachment>(db_conn)
|
||||
.await
|
||||
.optional()
|
||||
.map(|attachment| attachment.map(Into::into))
|
||||
})
|
||||
.map_err(Into::into)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
|
|
@ -7,8 +7,8 @@ use futures_util::TryStreamExt;
|
|||
use kitsune_db::{
|
||||
model::{media_attachment::MediaAttachment as DbMediaAttachment, post::Post as DbPost},
|
||||
schema::{media_attachments, posts_media_attachments},
|
||||
with_connection,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
|
@ -44,22 +44,17 @@ impl Post {
|
|||
|
||||
pub async fn attachments(&self, ctx: &Context<'_>) -> Result<Vec<MediaAttachment>> {
|
||||
let db_pool = &ctx.state().db_pool;
|
||||
let attachments = db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
media_attachments::table
|
||||
.inner_join(posts_media_attachments::table)
|
||||
.filter(posts_media_attachments::post_id.eq(self.id))
|
||||
.select(DbMediaAttachment::as_select())
|
||||
.load_stream(db_conn)
|
||||
.await?
|
||||
.map_ok(Into::into)
|
||||
.try_collect()
|
||||
.await
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let attachments = with_connection!(db_pool, |db_conn| {
|
||||
media_attachments::table
|
||||
.inner_join(posts_media_attachments::table)
|
||||
.filter(posts_media_attachments::post_id.eq(self.id))
|
||||
.select(DbMediaAttachment::as_select())
|
||||
.load_stream(db_conn)
|
||||
.await?
|
||||
.map_ok(Into::into)
|
||||
.try_collect()
|
||||
.await
|
||||
})?;
|
||||
|
||||
Ok(attachments)
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@ use diesel_async::RunQueryDsl;
|
|||
use kitsune_db::{
|
||||
model::{account::Account as DbAccount, user::User as DbUser},
|
||||
schema::{accounts, users},
|
||||
with_connection,
|
||||
};
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use speedy_uuid::Uuid;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
|
@ -27,21 +27,16 @@ pub struct User {
|
|||
impl User {
|
||||
pub async fn account(&self, ctx: &Context<'_>) -> Result<Account> {
|
||||
let db_pool = &ctx.state().db_pool;
|
||||
db_pool
|
||||
.with_connection(|db_conn| {
|
||||
async move {
|
||||
users::table
|
||||
.find(self.id)
|
||||
.inner_join(accounts::table)
|
||||
.select(DbAccount::as_select())
|
||||
.get_result::<DbAccount>(db_conn)
|
||||
.await
|
||||
.map(Into::into)
|
||||
}
|
||||
.scoped()
|
||||
})
|
||||
.await
|
||||
.map_err(Into::into)
|
||||
with_connection!(db_pool, |db_conn| {
|
||||
users::table
|
||||
.find(self.id)
|
||||
.inner_join(accounts::table)
|
||||
.select(DbAccount::as_select())
|
||||
.get_result::<DbAccount>(db_conn)
|
||||
.await
|
||||
.map(Into::into)
|
||||
})
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,10 +7,9 @@ use axum_extra::extract::Query;
|
|||
use diesel::{ExpressionMethods, QueryDsl, SelectableHelper};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use futures_util::StreamExt;
|
||||
use kitsune_db::{model::account::Account, schema::accounts, PgPool};
|
||||
use kitsune_db::{model::account::Account, schema::accounts, with_connection, PgPool};
|
||||
use kitsune_mastodon::MastodonMapper;
|
||||
use kitsune_type::mastodon::relationship::Relationship;
|
||||
use scoped_futures::ScopedFutureExt;
|
||||
use serde::Deserialize;
|
||||
use speedy_uuid::Uuid;
|
||||
use utoipa::IntoParams;
|
||||
|
@ -39,15 +38,13 @@ pub async fn get(
|
|||
State(mastodon_mapper): State<MastodonMapper>,
|
||||
Query(query): Query<RelationshipQuery>,
|
||||
) -> Result<Json<Vec<Relationship>>> {
|
||||
let mut account_stream = db_pool
|
||||
.with_connection(|db_conn| {
|
||||
accounts::table
|
||||
.filter(accounts::id.eq_any(&query.id))
|
||||
.select(Account::as_select())
|
||||
.load_stream::<Account>(db_conn)
|
||||
.scoped()
|
||||
})
|
||||
.await?;
|
||||
let mut account_stream = with_connection!(db_pool, |db_conn| {
|
||||
accounts::table
|
||||
.filter(accounts::id.eq_any(&query.id))
|
||||
.select(Account::as_select())
|
||||
.load_stream::<Account>(db_conn)
|
||||
.await
|
||||
})?;
|
||||
|
||||
let mut relationships = Vec::with_capacity(query.id.len());
|
||||
while let Some(account) = account_stream.next().await.transpose()? {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue