1
0
Fork 0
mirror of https://github.com/Nutomic/ibis.git synced 2025-02-02 23:01:35 +00:00

Rename IbisData to IbisContext

This commit is contained in:
Felix Ableitner 2025-01-21 15:50:24 +01:00
parent 696ba19a2f
commit 3d1beefe14
40 changed files with 598 additions and 550 deletions

View file

@ -5,7 +5,7 @@ use crate::{
article::DbArticleForm,
conflict::{DbConflict, DbConflictForm},
edit::DbEditForm,
IbisData,
IbisContext,
},
federation::activities::{create_article::CreateArticle, submit_article_update},
utils::{
@ -50,13 +50,13 @@ use diffy::create_patch;
#[debug_handler]
pub(in crate::backend::api) async fn create_article(
user: Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(mut params): Form<CreateArticleParams>,
) -> MyResult<Json<DbArticleView>> {
params.title = validate_article_title(&params.title)?;
validate_not_empty(&params.text)?;
let local_instance = DbInstance::read_local(&data)?;
let local_instance = DbInstance::read_local(&context)?;
let ap_id = ObjectId::parse(&format!(
"{}://{}/article/{}",
http_protocol_str(),
@ -70,23 +70,23 @@ pub(in crate::backend::api) async fn create_article(
instance_id: local_instance.id,
local: true,
protected: false,
approved: !data.config.options.article_approval,
approved: !context.config.options.article_approval,
};
let article = DbArticle::create(form, &data)?;
let article = DbArticle::create(form, &context)?;
let edit_data = EditArticleParams {
article_id: article.id,
new_text: params.text,
summary: params.summary,
previous_version_id: article.latest_edit_version(&data)?,
previous_version_id: article.latest_edit_version(&context)?,
resolve_conflict_id: None,
};
let _ = edit_article(user, data.reset_request_count(), Form(edit_data)).await?;
let _ = edit_article(user, context.reset_request_count(), Form(edit_data)).await?;
// allow reading unapproved article here
let article_view = DbArticle::read_view(article.id, &data)?;
CreateArticle::send_to_followers(article_view.article.clone(), &data).await?;
let article_view = DbArticle::read_view(article.id, &context)?;
CreateArticle::send_to_followers(article_view.article.clone(), &context).await?;
Ok(Json(article_view))
}
@ -103,15 +103,15 @@ pub(in crate::backend::api) async fn create_article(
#[debug_handler]
pub(in crate::backend::api) async fn edit_article(
Extension(user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(mut params): Form<EditArticleParams>,
) -> MyResult<Json<Option<ApiConflict>>> {
validate_not_empty(&params.new_text)?;
// resolve conflict if any
if let Some(resolve_conflict_id) = params.resolve_conflict_id {
DbConflict::delete(resolve_conflict_id, user.person.id, &data)?;
DbConflict::delete(resolve_conflict_id, user.person.id, &context)?;
}
let original_article = DbArticle::read_view(params.article_id, &data)?;
let original_article = DbArticle::read_view(params.article_id, &context)?;
if params.new_text == original_article.article.text {
return Err(anyhow!("Edit contains no changes").into());
}
@ -123,7 +123,7 @@ pub(in crate::backend::api) async fn edit_article(
if !params.new_text.ends_with('\n') {
params.new_text.push('\n');
}
let local_link = format!("](https://{}", data.config.federation.domain);
let local_link = format!("](https://{}", context.config.federation.domain);
if params.new_text.contains(&local_link) {
return Err(anyhow!("Links to local instance don't work over federation").into());
}
@ -139,18 +139,18 @@ pub(in crate::backend::api) async fn edit_article(
params.previous_version_id,
&original_article.article,
user.person.id,
&data,
&context,
)
.await?;
Ok(Json(None))
} else {
// There have been other changes since this edit was initiated. Get the common ancestor
// version and generate a diff to find out what exactly has changed.
let edits = DbEdit::list_for_article(original_article.article.id, &data)?;
let edits = DbEdit::list_for_article(original_article.article.id, &context)?;
let ancestor = generate_article_version(&edits, &params.previous_version_id)?;
let patch = create_patch(&ancestor, &new_text);
let previous_version = DbEdit::read(&params.previous_version_id, &data)?;
let previous_version = DbEdit::read(&params.previous_version_id, &context)?;
let form = DbConflictForm {
hash: EditVersion::new(&patch.to_string()),
diff: patch.to_string(),
@ -159,8 +159,8 @@ pub(in crate::backend::api) async fn edit_article(
article_id: original_article.article.id,
previous_version_id: previous_version.hash,
};
let conflict = DbConflict::create(&form, &data)?;
Ok(Json(conflict.to_api_conflict(&data).await?))
let conflict = DbConflict::create(&form, &context)?;
Ok(Json(conflict.to_api_conflict(&context).await?))
}
}
@ -168,19 +168,19 @@ pub(in crate::backend::api) async fn edit_article(
#[debug_handler]
pub(in crate::backend::api) async fn get_article(
Query(query): Query<GetArticleParams>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<DbArticleView>> {
match (query.title, query.id) {
(Some(title), None) => Ok(Json(DbArticle::read_view_title(
&title,
query.domain,
&data,
&context,
)?)),
(None, Some(id)) => {
if query.domain.is_some() {
return Err(anyhow!("Cant combine id and instance_domain").into());
}
let article = DbArticle::read_view(id, &data)?;
let article = DbArticle::read_view(id, &context)?;
Ok(Json(article))
}
_ => Err(anyhow!("Must pass exactly one of title, id").into()),
@ -190,12 +190,12 @@ pub(in crate::backend::api) async fn get_article(
#[debug_handler]
pub(in crate::backend::api) async fn list_articles(
Query(query): Query<ListArticlesParams>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<Vec<DbArticle>>> {
Ok(Json(DbArticle::read_all(
query.only_local,
query.instance_id,
&data,
&context,
)?))
}
@ -204,14 +204,14 @@ pub(in crate::backend::api) async fn list_articles(
#[debug_handler]
pub(in crate::backend::api) async fn fork_article(
Extension(_user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(mut params): Form<ForkArticleParams>,
) -> MyResult<Json<DbArticleView>> {
// TODO: lots of code duplicated from create_article(), can move it into helper
let original_article = DbArticle::read_view(params.article_id, &data)?;
let original_article = DbArticle::read_view(params.article_id, &context)?;
params.new_title = validate_article_title(&params.new_title)?;
let local_instance = DbInstance::read_local(&data)?;
let local_instance = DbInstance::read_local(&context)?;
let ap_id = ObjectId::parse(&format!(
"{}://{}/article/{}",
http_protocol_str(),
@ -225,14 +225,14 @@ pub(in crate::backend::api) async fn fork_article(
instance_id: local_instance.id,
local: true,
protected: false,
approved: !data.config.options.article_approval,
approved: !context.config.options.article_approval,
};
let article = DbArticle::create(form, &data)?;
let article = DbArticle::create(form, &context)?;
// copy edits to new article
// this could also be done in sql
let edits = DbEdit::list_for_article(original_article.article.id, &data)?;
let edits = DbEdit::list_for_article(original_article.article.id, &context)?;
for e in edits {
let ap_id = DbEditForm::generate_ap_id(&article, &e.hash)?;
let form = DbEditForm {
@ -246,12 +246,12 @@ pub(in crate::backend::api) async fn fork_article(
published: Utc::now(),
pending: false,
};
DbEdit::create(&form, &data)?;
DbEdit::create(&form, &context)?;
}
CreateArticle::send_to_followers(article.clone(), &data).await?;
CreateArticle::send_to_followers(article.clone(), &context).await?;
Ok(Json(DbArticle::read_view(article.id, &data)?))
Ok(Json(DbArticle::read_view(article.id, &context)?))
}
/// Fetch a remote article, including edits collection. Allows viewing and editing. Note that new
@ -259,12 +259,12 @@ pub(in crate::backend::api) async fn fork_article(
#[debug_handler]
pub(super) async fn resolve_article(
Query(query): Query<ResolveObjectParams>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<DbArticleView>> {
let article: DbArticle = ObjectId::from(query.id).dereference(&data).await?;
let instance = DbInstance::read(article.instance_id, &data)?;
let comments = DbComment::read_for_article(article.id, &data)?;
let latest_version = article.latest_edit_version(&data)?;
let article: DbArticle = ObjectId::from(query.id).dereference(&context).await?;
let instance = DbInstance::read(article.instance_id, &context)?;
let comments = DbComment::read_for_article(article.id, &context)?;
let latest_version = article.latest_edit_version(&context)?;
Ok(Json(DbArticleView {
article,
instance,
@ -277,23 +277,23 @@ pub(super) async fn resolve_article(
#[debug_handler]
pub(super) async fn search_article(
Query(query): Query<SearchArticleParams>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<Vec<DbArticle>>> {
if query.query.is_empty() {
return Err(anyhow!("Query is empty").into());
}
let article = DbArticle::search(&query.query, &data)?;
let article = DbArticle::search(&query.query, &context)?;
Ok(Json(article))
}
#[debug_handler]
pub(in crate::backend::api) async fn protect_article(
Extension(user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(params): Form<ProtectArticleParams>,
) -> MyResult<Json<DbArticle>> {
check_is_admin(&user)?;
let article = DbArticle::update_protected(params.article_id, params.protected, &data)?;
let article = DbArticle::update_protected(params.article_id, params.protected, &context)?;
Ok(Json(article))
}
@ -301,14 +301,14 @@ pub(in crate::backend::api) async fn protect_article(
#[debug_handler]
pub async fn approve_article(
Extension(user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(params): Form<ApproveArticleParams>,
) -> MyResult<Json<()>> {
check_is_admin(&user)?;
if params.approve {
DbArticle::update_approved(params.article_id, true, &data)?;
DbArticle::update_approved(params.article_id, true, &context)?;
} else {
DbArticle::delete(params.article_id, &data)?;
DbArticle::delete(params.article_id, &context)?;
}
Ok(Json(()))
}
@ -317,9 +317,9 @@ pub async fn approve_article(
#[debug_handler]
pub async fn delete_conflict(
Extension(user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(params): Form<DeleteConflictParams>,
) -> MyResult<Json<()>> {
DbConflict::delete(params.conflict_id, user.person.id, &data)?;
DbConflict::delete(params.conflict_id, user.person.id, &context)?;
Ok(Json(()))
}

View file

@ -2,7 +2,7 @@ use crate::{
backend::{
database::{
comment::{DbCommentInsertForm, DbCommentUpdateForm},
IbisData,
IbisContext,
},
federation::activities::comment::{
create_or_update_comment::CreateOrUpdateComment,
@ -29,13 +29,13 @@ use chrono::Utc;
#[debug_handler]
pub(in crate::backend::api) async fn create_comment(
user: Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(params): Form<CreateCommentParams>,
) -> MyResult<Json<DbCommentView>> {
validate_not_empty(&params.content)?;
let mut depth = 0;
if let Some(parent_id) = params.parent_id {
let parent = DbComment::read(parent_id, &data)?;
let parent = DbComment::read(parent_id, &context)?;
if parent.deleted {
return Err(anyhow!("Cant reply to deleted comment").into());
}
@ -57,18 +57,18 @@ pub(in crate::backend::api) async fn create_comment(
published: Utc::now(),
updated: None,
};
let comment = DbComment::create(form, &data)?;
let comment = DbComment::create(form, &context)?;
// Set the ap_id which contains db id (so it is not know before inserting)
let proto = http_protocol_str();
let ap_id = format!("{}://{}/comment/{}", proto, data.domain(), comment.id.0).parse()?;
let ap_id = format!("{}://{}/comment/{}", proto, context.domain(), comment.id.0).parse()?;
let form = DbCommentUpdateForm {
ap_id: Some(ap_id),
..Default::default()
};
let comment = DbComment::update(form, comment.id, &data)?;
let comment = DbComment::update(form, comment.id, &context)?;
CreateOrUpdateComment::send(&comment.comment, &data).await?;
CreateOrUpdateComment::send(&comment.comment, &context).await?;
Ok(Json(comment))
}
@ -76,7 +76,7 @@ pub(in crate::backend::api) async fn create_comment(
#[debug_handler]
pub(in crate::backend::api) async fn edit_comment(
user: Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(params): Form<EditCommentParams>,
) -> MyResult<Json<DbCommentView>> {
if let Some(content) = &params.content {
@ -85,7 +85,7 @@ pub(in crate::backend::api) async fn edit_comment(
if params.content.is_none() && params.deleted.is_none() {
return Err(anyhow!("Edit has no parameters").into());
}
let orig_comment = DbComment::read(params.id, &data)?;
let orig_comment = DbComment::read(params.id, &context)?;
if orig_comment.creator_id != user.person.id {
return Err(anyhow!("Cannot edit comment created by another user").into());
}
@ -95,17 +95,17 @@ pub(in crate::backend::api) async fn edit_comment(
updated: Some(Utc::now()),
..Default::default()
};
let comment = DbComment::update(form, params.id, &data)?;
let comment = DbComment::update(form, params.id, &context)?;
// federate
if orig_comment.content != comment.comment.content {
CreateOrUpdateComment::send(&comment.comment, &data).await?;
CreateOrUpdateComment::send(&comment.comment, &context).await?;
}
if !orig_comment.deleted && comment.comment.deleted {
DeleteComment::send(&comment.comment, &data).await?;
DeleteComment::send(&comment.comment, &context).await?;
}
if orig_comment.deleted && !comment.comment.deleted {
UndoDeleteComment::send(&comment.comment, &data).await?;
UndoDeleteComment::send(&comment.comment, &context).await?;
}
Ok(Json(comment))

View file

@ -1,5 +1,9 @@
use crate::{
backend::{database::IbisData, federation::activities::follow::Follow, utils::error::MyResult},
backend::{
database::IbisContext,
federation::activities::follow::Follow,
utils::error::MyResult,
},
common::{
instance::{DbInstance, FollowInstanceParams, GetInstanceParams, InstanceView},
user::LocalUserView,
@ -14,10 +18,10 @@ use axum_macros::debug_handler;
/// Retrieve details about an instance. If no id is provided, return local instance.
#[debug_handler]
pub(in crate::backend::api) async fn get_instance(
data: Data<IbisData>,
context: Data<IbisContext>,
Form(params): Form<GetInstanceParams>,
) -> MyResult<Json<InstanceView>> {
let local_instance = DbInstance::read_view(params.id, &data)?;
let local_instance = DbInstance::read_view(params.id, &context)?;
Ok(Json(local_instance))
}
@ -26,14 +30,14 @@ pub(in crate::backend::api) async fn get_instance(
#[debug_handler]
pub(in crate::backend::api) async fn follow_instance(
Extension(user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
Form(params): Form<FollowInstanceParams>,
) -> MyResult<Json<SuccessResponse>> {
let target = DbInstance::read(params.id, &data)?;
let target = DbInstance::read(params.id, &context)?;
let pending = !target.local;
DbInstance::follow(&user.person, &target, pending, &data)?;
let instance = DbInstance::read(params.id, &data)?;
Follow::send(user.person, &instance, &data).await?;
DbInstance::follow(&user.person, &target, pending, &context)?;
let instance = DbInstance::read(params.id, &context)?;
Follow::send(user.person, &instance, &context).await?;
Ok(Json(SuccessResponse::default()))
}
@ -42,16 +46,16 @@ pub(in crate::backend::api) async fn follow_instance(
#[debug_handler]
pub(super) async fn resolve_instance(
Query(params): Query<ResolveObjectParams>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<DbInstance>> {
let instance: DbInstance = ObjectId::from(params.id).dereference(&data).await?;
let instance: DbInstance = ObjectId::from(params.id).dereference(&context).await?;
Ok(Json(instance))
}
#[debug_handler]
pub(in crate::backend::api) async fn list_remote_instances(
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<Vec<DbInstance>>> {
let instances = DbInstance::read_remote(&data)?;
let instances = DbInstance::read_remote(&context)?;
Ok(Json(instances))
}

View file

@ -16,7 +16,7 @@ use crate::{
instance::{follow_instance, get_instance, resolve_instance},
user::{get_user, login_user, logout_user, register_user},
},
database::IbisData,
database::IbisContext,
utils::error::MyResult,
},
common::{
@ -83,12 +83,12 @@ fn check_is_admin(user: &LocalUserView) -> MyResult<()> {
#[debug_handler]
pub(in crate::backend::api) async fn site_view(
data: Data<IbisData>,
context: Data<IbisContext>,
user: Option<Extension<LocalUserView>>,
) -> MyResult<Json<SiteView>> {
Ok(Json(SiteView {
my_profile: user.map(|u| u.0),
config: data.config.options.clone(),
config: context.config.options.clone(),
}))
}
@ -97,7 +97,7 @@ pub(in crate::backend::api) async fn site_view(
pub async fn edit_list(
Query(query): Query<GetEditList>,
user: Option<Extension<LocalUserView>>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<Vec<EditView>>> {
let params = if let Some(article_id) = query.article_id {
ViewEditParams::ArticleId(article_id)
@ -106,7 +106,7 @@ pub async fn edit_list(
} else {
return Err(anyhow!("Must provide article_id or person_id").into());
};
Ok(Json(DbEdit::view(params, &user.map(|u| u.0), &data)?))
Ok(Json(DbEdit::view(params, &user.map(|u| u.0), &context)?))
}
/// Trims the string param, and converts to None if it is empty

View file

@ -1,7 +1,7 @@
use super::{check_is_admin, empty_to_none};
use crate::{
backend::{
database::{conflict::DbConflict, read_jwt_secret, IbisData},
database::{conflict::DbConflict, read_jwt_secret, IbisContext},
utils::{
error::MyResult,
validate::{validate_display_name, validate_user_name},
@ -54,8 +54,8 @@ struct Claims {
pub exp: u64,
}
fn generate_login_token(person: &DbPerson, data: &Data<IbisData>) -> MyResult<String> {
let hostname = data.domain().to_string();
fn generate_login_token(person: &DbPerson, context: &Data<IbisContext>) -> MyResult<String> {
let hostname = context.domain().to_string();
let claims = Claims {
sub: person.username.clone(),
iss: hostname,
@ -63,58 +63,58 @@ fn generate_login_token(person: &DbPerson, data: &Data<IbisData>) -> MyResult<St
exp: get_current_timestamp() + 60 * 60 * 24 * 365,
};
let secret = read_jwt_secret(data)?;
let secret = read_jwt_secret(context)?;
let key = EncodingKey::from_secret(secret.as_bytes());
let jwt = encode(&Header::default(), &claims, &key)?;
Ok(jwt)
}
pub async fn validate(jwt: &str, data: &IbisData) -> MyResult<LocalUserView> {
pub async fn validate(jwt: &str, context: &IbisContext) -> MyResult<LocalUserView> {
let validation = Validation::default();
let secret = read_jwt_secret(data)?;
let secret = read_jwt_secret(context)?;
let key = DecodingKey::from_secret(secret.as_bytes());
let claims = decode::<Claims>(jwt, &key, &validation)?;
DbPerson::read_local_from_name(&claims.claims.sub, data)
DbPerson::read_local_from_name(&claims.claims.sub, context)
}
#[debug_handler]
pub(in crate::backend::api) async fn register_user(
data: Data<IbisData>,
context: Data<IbisContext>,
jar: CookieJar,
Form(params): Form<RegisterUserParams>,
) -> MyResult<(CookieJar, Json<LocalUserView>)> {
if !data.config.options.registration_open {
if !context.config.options.registration_open {
return Err(anyhow!("Registration is closed").into());
}
validate_user_name(&params.username)?;
let user = DbPerson::create_local(params.username, params.password, false, &data)?;
let token = generate_login_token(&user.person, &data)?;
let jar = jar.add(create_cookie(token, &data));
let user = DbPerson::create_local(params.username, params.password, false, &context)?;
let token = generate_login_token(&user.person, &context)?;
let jar = jar.add(create_cookie(token, &context));
Ok((jar, Json(user)))
}
#[debug_handler]
pub(in crate::backend::api) async fn login_user(
data: Data<IbisData>,
context: Data<IbisContext>,
jar: CookieJar,
Form(params): Form<LoginUserParams>,
) -> MyResult<(CookieJar, Json<LocalUserView>)> {
let user = DbPerson::read_local_from_name(&params.username, &data)?;
let user = DbPerson::read_local_from_name(&params.username, &context)?;
let valid = verify(&params.password, &user.local_user.password_encrypted)?;
if !valid {
return Err(anyhow!("Invalid login").into());
}
let token = generate_login_token(&user.person, &data)?;
let jar = jar.add(create_cookie(token, &data));
let token = generate_login_token(&user.person, &context)?;
let jar = jar.add(create_cookie(token, &context));
Ok((jar, Json(user)))
}
fn create_cookie(jwt: String, data: &Data<IbisData>) -> Cookie<'static> {
fn create_cookie(jwt: String, context: &Data<IbisContext>) -> Cookie<'static> {
let mut cookie = Cookie::build((AUTH_COOKIE, jwt));
// Must not set cookie domain on localhost
// https://stackoverflow.com/a/1188145
let domain = data.domain().to_string();
let domain = context.domain().to_string();
if !domain.starts_with("localhost") && !domain.starts_with("127.0.0.1") {
cookie = cookie.domain(domain);
}
@ -131,45 +131,45 @@ fn create_cookie(jwt: String, data: &Data<IbisData>) -> Cookie<'static> {
#[debug_handler]
pub(in crate::backend::api) async fn logout_user(
data: Data<IbisData>,
context: Data<IbisContext>,
jar: CookieJar,
) -> MyResult<(CookieJar, Json<SuccessResponse>)> {
let jar = jar.remove(create_cookie(String::new(), &data));
let jar = jar.remove(create_cookie(String::new(), &context));
Ok((jar, Json(SuccessResponse::default())))
}
#[debug_handler]
pub(in crate::backend::api) async fn get_user(
params: Query<GetUserParams>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<DbPerson>> {
Ok(Json(DbPerson::read_from_name(
&params.name,
&params.domain,
&data,
&context,
)?))
}
#[debug_handler]
pub(in crate::backend::api) async fn update_user_profile(
data: Data<IbisData>,
context: Data<IbisContext>,
Form(mut params): Form<UpdateUserParams>,
) -> MyResult<Json<SuccessResponse>> {
empty_to_none(&mut params.display_name);
empty_to_none(&mut params.bio);
validate_display_name(&params.display_name)?;
DbPerson::update_profile(&params, &data)?;
DbPerson::update_profile(&params, &context)?;
Ok(Json(SuccessResponse::default()))
}
#[debug_handler]
pub(crate) async fn list_notifications(
Extension(user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<Vec<Notification>>> {
let conflicts = DbConflict::list(&user.person, &data)?;
let conflicts = DbConflict::list(&user.person, &context)?;
let conflicts: Vec<_> = try_join_all(conflicts.into_iter().map(|c| {
let data = data.reset_request_count();
let data = context.reset_request_count();
async move { c.to_api_conflict(&data).await }
}))
.await?;
@ -180,7 +180,7 @@ pub(crate) async fn list_notifications(
.collect();
if check_is_admin(&user).is_ok() {
let articles = DbArticle::list_approval_required(&data)?;
let articles = DbArticle::list_approval_required(&context)?;
notifications.extend(
articles
.into_iter()
@ -195,13 +195,13 @@ pub(crate) async fn list_notifications(
#[debug_handler]
pub(crate) async fn count_notifications(
Extension(user): Extension<LocalUserView>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<Json<usize>> {
let mut count = 0;
let conflicts = DbConflict::list(&user.person, &data)?;
let conflicts = DbConflict::list(&user.person, &context)?;
count += conflicts.len();
if check_is_admin(&user).is_ok() {
let articles = DbArticle::list_approval_required(&data)?;
let articles = DbArticle::list_approval_required(&context)?;
count += articles.len();
}

View file

@ -2,7 +2,7 @@ use crate::{
backend::{
database::{
schema::{article, edit, instance},
IbisData,
IbisContext,
},
federation::objects::edits_collection::DbEditCollection,
utils::error::MyResult,
@ -46,15 +46,15 @@ impl DbArticle {
Ok(CollectionId::parse(&format!("{}/edits", self.ap_id))?)
}
pub fn create(form: DbArticleForm, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create(form: DbArticleForm, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(article::table)
.values(form)
.get_result(conn.deref_mut())?)
}
pub fn create_or_update(form: DbArticleForm, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create_or_update(form: DbArticleForm, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(article::table)
.values(&form)
.on_conflict(article::dsl::ap_id)
@ -63,48 +63,48 @@ impl DbArticle {
.get_result(conn.deref_mut())?)
}
pub fn update_text(id: ArticleId, text: &str, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn update_text(id: ArticleId, text: &str, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::text.eq(text))
.get_result::<Self>(conn.deref_mut())?)
}
pub fn update_protected(id: ArticleId, locked: bool, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn update_protected(id: ArticleId, locked: bool, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::protected.eq(locked))
.get_result::<Self>(conn.deref_mut())?)
}
pub fn update_approved(id: ArticleId, approved: bool, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn update_approved(id: ArticleId, approved: bool, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(diesel::update(article::dsl::article.find(id))
.set(article::dsl::approved.eq(approved))
.get_result::<Self>(conn.deref_mut())?)
}
pub fn delete(id: ArticleId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn delete(id: ArticleId, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(diesel::delete(article::dsl::article.find(id)).get_result::<Self>(conn.deref_mut())?)
}
pub fn read(id: ArticleId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read(id: ArticleId, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(article::table
.find(id)
.get_result::<Self>(conn.deref_mut())?)
}
pub fn read_view(id: ArticleId, data: &IbisData) -> MyResult<DbArticleView> {
let mut conn = data.db_pool.get()?;
pub fn read_view(id: ArticleId, context: &IbisContext) -> MyResult<DbArticleView> {
let mut conn = context.db_pool.get()?;
let query = article::table
.find(id)
.inner_join(instance::table)
.into_boxed();
let (article, instance): (DbArticle, DbInstance) = query.get_result(conn.deref_mut())?;
let comments = DbComment::read_for_article(article.id, data)?;
let latest_version = article.latest_edit_version(data)?;
let comments = DbComment::read_for_article(article.id, context)?;
let latest_version = article.latest_edit_version(context)?;
Ok(DbArticleView {
article,
instance,
@ -116,9 +116,9 @@ impl DbArticle {
pub fn read_view_title(
title: &str,
domain: Option<String>,
data: &IbisData,
context: &IbisContext,
) -> MyResult<DbArticleView> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
let (article, instance): (DbArticle, DbInstance) = {
let query = article::table
.inner_join(instance::table)
@ -131,8 +131,8 @@ impl DbArticle {
};
query.get_result(conn.deref_mut())?
};
let comments = DbComment::read_for_article(article.id, data)?;
let latest_version = article.latest_edit_version(data)?;
let comments = DbComment::read_for_article(article.id, context)?;
let latest_version = article.latest_edit_version(context)?;
Ok(DbArticleView {
article,
instance,
@ -141,8 +141,8 @@ impl DbArticle {
})
}
pub fn read_from_ap_id(ap_id: &ObjectId<DbArticle>, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read_from_ap_id(ap_id: &ObjectId<DbArticle>, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(article::table
.filter(article::dsl::ap_id.eq(ap_id))
.get_result(conn.deref_mut())?)
@ -154,9 +154,9 @@ impl DbArticle {
pub fn read_all(
only_local: Option<bool>,
instance_id: Option<InstanceId>,
data: &IbisData,
context: &IbisContext,
) -> MyResult<Vec<Self>> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
let mut query = article::table
.inner_join(edit::table)
.inner_join(instance::table)
@ -175,8 +175,8 @@ impl DbArticle {
Ok(query.get_results(&mut conn)?)
}
pub fn search(query: &str, data: &IbisData) -> MyResult<Vec<Self>> {
let mut conn = data.db_pool.get()?;
pub fn search(query: &str, context: &IbisContext) -> MyResult<Vec<Self>> {
let mut conn = context.db_pool.get()?;
let replaced = query
.replace('%', "\\%")
.replace('_', "\\_")
@ -191,8 +191,8 @@ impl DbArticle {
.get_results(conn.deref_mut())?)
}
pub fn latest_edit_version(&self, data: &IbisData) -> MyResult<EditVersion> {
let mut conn = data.db_pool.get()?;
pub fn latest_edit_version(&self, context: &IbisContext) -> MyResult<EditVersion> {
let mut conn = context.db_pool.get()?;
let latest_version: Option<EditVersion> = edit::table
.filter(edit::dsl::article_id.eq(self.id))
.order_by(edit::dsl::id.desc())
@ -206,8 +206,8 @@ impl DbArticle {
}
}
pub fn list_approval_required(data: &IbisData) -> MyResult<Vec<Self>> {
let mut conn = data.db_pool.get()?;
pub fn list_approval_required(context: &IbisContext) -> MyResult<Vec<Self>> {
let mut conn = context.db_pool.get()?;
let query = article::table
.group_by(article::dsl::id)
.filter(article::dsl::approved.eq(false))

View file

@ -1,6 +1,6 @@
use super::{
schema::{comment, person},
IbisData,
IbisContext,
};
use crate::{
backend::utils::error::MyResult,
@ -48,8 +48,8 @@ pub struct DbCommentUpdateForm {
}
impl DbComment {
pub fn create(form: DbCommentInsertForm, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create(form: DbCommentInsertForm, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(comment::table)
.values(form)
.get_result(conn.deref_mut())?)
@ -58,18 +58,18 @@ impl DbComment {
pub fn update(
form: DbCommentUpdateForm,
id: CommentId,
data: &IbisData,
context: &IbisContext,
) -> MyResult<DbCommentView> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
let comment: DbComment = update(comment::table.find(id))
.set(form)
.get_result(conn.deref_mut())?;
let creator = DbPerson::read(comment.creator_id, data)?;
let creator = DbPerson::read(comment.creator_id, context)?;
Ok(DbCommentView { comment, creator })
}
pub fn create_or_update(form: DbCommentInsertForm, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create_or_update(form: DbCommentInsertForm, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(comment::table)
.values(&form)
.on_conflict(comment::dsl::ap_id)
@ -78,24 +78,24 @@ impl DbComment {
.get_result(conn.deref_mut())?)
}
pub fn read(id: CommentId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read(id: CommentId, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(comment::table
.find(id)
.get_result::<Self>(conn.deref_mut())?)
}
pub fn read_view(id: CommentId, data: &IbisData) -> MyResult<DbCommentView> {
let mut conn = data.db_pool.get()?;
pub fn read_view(id: CommentId, context: &IbisContext) -> MyResult<DbCommentView> {
let mut conn = context.db_pool.get()?;
let comment = comment::table
.find(id)
.get_result::<Self>(conn.deref_mut())?;
let creator = DbPerson::read(comment.creator_id, data)?;
let creator = DbPerson::read(comment.creator_id, context)?;
Ok(DbCommentView { comment, creator })
}
pub fn read_from_ap_id(ap_id: &ObjectId<DbComment>, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read_from_ap_id(ap_id: &ObjectId<DbComment>, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(comment::table
.filter(comment::dsl::ap_id.eq(ap_id))
.get_result(conn.deref_mut())?)
@ -103,9 +103,9 @@ impl DbComment {
pub fn read_for_article(
article_id: ArticleId,
data: &IbisData,
context: &IbisContext,
) -> MyResult<Vec<DbCommentView>> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
let comments = comment::table
.inner_join(person::table)
.filter(comment::article_id.eq(article_id))

View file

@ -2,7 +2,7 @@ use crate::{
backend::{
database::{
schema::{conflict, edit},
IbisData,
IbisContext,
},
federation::activities::submit_article_update,
utils::{error::MyResult, generate_article_version},
@ -57,23 +57,23 @@ pub struct DbConflictForm {
}
impl DbConflict {
pub fn create(form: &DbConflictForm, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create(form: &DbConflictForm, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(conflict::table)
.values(form)
.get_result(conn.deref_mut())?)
}
pub fn list(person: &DbPerson, data: &IbisData) -> MyResult<Vec<Self>> {
let mut conn = data.db_pool.get()?;
pub fn list(person: &DbPerson, context: &IbisContext) -> MyResult<Vec<Self>> {
let mut conn = context.db_pool.get()?;
Ok(conflict::table
.filter(conflict::dsl::creator_id.eq(person.id))
.get_results(conn.deref_mut())?)
}
/// Delete merge conflict which was created by specific user
pub fn delete(id: ConflictId, creator_id: PersonId, data: &IbisData) -> MyResult<()> {
let mut conn = data.db_pool.get()?;
pub fn delete(id: ConflictId, creator_id: PersonId, context: &IbisContext) -> MyResult<()> {
let mut conn = context.db_pool.get()?;
let conflict: Self = delete(
conflict::table
.filter(conflict::dsl::creator_id.eq(creator_id))
@ -89,13 +89,16 @@ impl DbConflict {
Ok(())
}
pub async fn to_api_conflict(&self, data: &Data<IbisData>) -> MyResult<Option<ApiConflict>> {
let article = DbArticle::read_view(self.article_id, data)?;
pub async fn to_api_conflict(
&self,
context: &Data<IbisContext>,
) -> MyResult<Option<ApiConflict>> {
let article = DbArticle::read_view(self.article_id, context)?;
// Make sure to get latest version from origin so that all conflicts can be resolved
let original_article = article.article.ap_id.dereference_forced(data).await?;
let original_article = article.article.ap_id.dereference_forced(context).await?;
// create common ancestor version
let edits = DbEdit::list_for_article(original_article.id, data)?;
let edits = DbEdit::list_for_article(original_article.id, context)?;
let ancestor = generate_article_version(&edits, &self.previous_version_id)?;
let patch = Patch::from_str(&self.diff)?;
@ -111,10 +114,10 @@ impl DbConflict {
self.previous_version_id.clone(),
&original_article,
self.creator_id,
data,
context,
)
.await?;
DbConflict::delete(self.id, self.creator_id, data)?;
DbConflict::delete(self.id, self.creator_id, context)?;
Ok(None)
}
Err(three_way_merge) => {
@ -125,7 +128,7 @@ impl DbConflict {
three_way_merge,
summary: self.summary.clone(),
article: original_article.clone(),
previous_version_id: original_article.latest_edit_version(data)?,
previous_version_id: original_article.latest_edit_version(context)?,
published: self.published,
}))
}

View file

@ -2,7 +2,7 @@ use crate::{
backend::{
database::schema::{article, edit, person},
utils::error::MyResult,
IbisData,
IbisContext,
},
common::{
article::{DbArticle, DbEdit, EditVersion, EditView},
@ -77,8 +77,8 @@ impl DbEditForm {
}
impl DbEdit {
pub fn create(form: &DbEditForm, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create(form: &DbEditForm, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(edit::table)
.values(form)
.on_conflict(edit::dsl::ap_id)
@ -87,22 +87,22 @@ impl DbEdit {
.get_result(conn.deref_mut())?)
}
pub fn read(version: &EditVersion, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read(version: &EditVersion, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(edit::table
.filter(edit::dsl::hash.eq(version))
.get_result(conn.deref_mut())?)
}
pub fn read_from_ap_id(ap_id: &ObjectId<DbEdit>, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read_from_ap_id(ap_id: &ObjectId<DbEdit>, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(edit::table
.filter(edit::dsl::ap_id.eq(ap_id))
.get_result(conn.deref_mut())?)
}
pub fn list_for_article(id: ArticleId, data: &IbisData) -> MyResult<Vec<Self>> {
let mut conn = data.db_pool.get()?;
pub fn list_for_article(id: ArticleId, context: &IbisContext) -> MyResult<Vec<Self>> {
let mut conn = context.db_pool.get()?;
Ok(edit::table
.filter(edit::article_id.eq(id))
.order(edit::published)
@ -112,9 +112,9 @@ impl DbEdit {
pub fn view(
params: ViewEditParams,
user: &Option<LocalUserView>,
data: &IbisData,
context: &IbisContext,
) -> MyResult<Vec<EditView>> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
let person_id = user.as_ref().map(|u| u.person.id).unwrap_or(PersonId(-1));
let query = edit::table
.inner_join(article::table)

View file

@ -2,7 +2,7 @@ use crate::{
backend::{
database::{
schema::{article, comment, instance, instance_follow},
IbisData,
IbisContext,
},
federation::objects::{
articles_collection::DbArticleCollection,
@ -48,8 +48,8 @@ pub struct DbInstanceForm {
}
impl DbInstance {
pub fn create(form: &DbInstanceForm, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create(form: &DbInstanceForm, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(instance::table)
.values(form)
.on_conflict(instance::ap_id)
@ -58,34 +58,37 @@ impl DbInstance {
.get_result(conn.deref_mut())?)
}
pub fn read(id: InstanceId, data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read(id: InstanceId, context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(instance::table.find(id).get_result(conn.deref_mut())?)
}
pub fn read_from_ap_id(
ap_id: &ObjectId<DbInstance>,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<DbInstance> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
Ok(instance::table
.filter(instance::ap_id.eq(ap_id))
.get_result(conn.deref_mut())?)
}
pub fn read_local(data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read_local(context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(instance::table
.filter(instance::local.eq(true))
.get_result(conn.deref_mut())?)
}
pub fn read_view(id: Option<InstanceId>, data: &Data<IbisData>) -> MyResult<InstanceView> {
pub fn read_view(
id: Option<InstanceId>,
context: &Data<IbisContext>,
) -> MyResult<InstanceView> {
let instance = match id {
Some(id) => DbInstance::read(id, data),
None => DbInstance::read_local(data),
Some(id) => DbInstance::read(id, context),
None => DbInstance::read_local(context),
}?;
let followers = DbInstance::read_followers(instance.id, data)?;
let followers = DbInstance::read_followers(instance.id, context)?;
Ok(InstanceView {
instance,
@ -97,10 +100,10 @@ impl DbInstance {
follower: &DbPerson,
instance: &DbInstance,
pending_: bool,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<()> {
use instance_follow::dsl::{follower_id, instance_id, pending};
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
let form = (
instance_id.eq(instance.id),
follower_id.eq(follower.id),
@ -116,10 +119,10 @@ impl DbInstance {
Ok(())
}
pub fn read_followers(id_: InstanceId, data: &IbisData) -> MyResult<Vec<DbPerson>> {
pub fn read_followers(id_: InstanceId, context: &IbisContext) -> MyResult<Vec<DbPerson>> {
use crate::backend::database::schema::person;
use instance_follow::dsl::{follower_id, instance_id};
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
Ok(instance_follow::table
.inner_join(person::table.on(follower_id.eq(person::id)))
.filter(instance_id.eq(id_))
@ -127,8 +130,8 @@ impl DbInstance {
.get_results(conn.deref_mut())?)
}
pub fn read_remote(data: &Data<IbisData>) -> MyResult<Vec<DbInstance>> {
let mut conn = data.db_pool.get()?;
pub fn read_remote(context: &Data<IbisContext>) -> MyResult<Vec<DbInstance>> {
let mut conn = context.db_pool.get()?;
Ok(instance::table
.filter(instance::local.eq(false))
.get_results(conn.deref_mut())?)
@ -136,8 +139,11 @@ impl DbInstance {
/// Read the instance where an article is hosted, based on a comment id.
/// Note this may be different from the instance where the comment is hosted.
pub fn read_for_comment(comment_id: CommentId, data: &Data<IbisData>) -> MyResult<DbInstance> {
let mut conn = data.db_pool.get()?;
pub fn read_for_comment(
comment_id: CommentId,
context: &Data<IbisContext>,
) -> MyResult<DbInstance> {
let mut conn = context.db_pool.get()?;
Ok(instance::table
.inner_join(article::table)
.inner_join(comment::table.on(comment::article_id.eq(article::id)))

View file

@ -1,5 +1,5 @@
use super::schema::instance_stats;
use crate::backend::{IbisData, MyResult};
use crate::backend::{IbisContext, MyResult};
use diesel::{query_dsl::methods::FindDsl, Queryable, RunQueryDsl, Selectable};
use std::ops::DerefMut;
@ -15,8 +15,8 @@ pub struct InstanceStats {
}
impl InstanceStats {
pub fn read(data: &IbisData) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn read(context: &IbisContext) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(instance_stats::table.find(1).get_result(conn.deref_mut())?)
}
}

View file

@ -19,13 +19,13 @@ pub mod user;
pub type DbPool = Pool<ConnectionManager<PgConnection>>;
#[derive(Clone)]
pub struct IbisData {
pub struct IbisContext {
pub db_pool: DbPool,
pub config: IbisConfig,
}
pub fn read_jwt_secret(data: &IbisData) -> MyResult<String> {
let mut conn = data.db_pool.get()?;
pub fn read_jwt_secret(context: &IbisContext) -> MyResult<String> {
let mut conn = context.db_pool.get()?;
Ok(jwt_secret::table
.select(jwt_secret::dsl::secret)
.first(conn.deref_mut())?)

View file

@ -2,7 +2,7 @@ use crate::{
backend::{
database::{
schema::{instance, instance_follow, local_user, person},
IbisData,
IbisContext,
},
utils::{error::MyResult, generate_keypair},
},
@ -51,8 +51,8 @@ pub struct DbPersonForm {
}
impl DbPerson {
pub fn create(person_form: &DbPersonForm, data: &Data<IbisData>) -> MyResult<Self> {
let mut conn = data.db_pool.get()?;
pub fn create(person_form: &DbPersonForm, context: &Data<IbisContext>) -> MyResult<Self> {
let mut conn = context.db_pool.get()?;
Ok(insert_into(person::table)
.values(person_form)
.on_conflict(person::dsl::ap_id)
@ -61,8 +61,8 @@ impl DbPerson {
.get_result::<DbPerson>(conn.deref_mut())?)
}
pub fn read(id: PersonId, data: &IbisData) -> MyResult<DbPerson> {
let mut conn = data.db_pool.get()?;
pub fn read(id: PersonId, context: &IbisContext) -> MyResult<DbPerson> {
let mut conn = context.db_pool.get()?;
Ok(person::table.find(id).get_result(conn.deref_mut())?)
}
@ -70,10 +70,10 @@ impl DbPerson {
username: String,
password: String,
admin: bool,
data: &IbisData,
context: &IbisContext,
) -> MyResult<LocalUserView> {
let mut conn = data.db_pool.get()?;
let domain = &data.config.federation.domain;
let mut conn = context.db_pool.get()?;
let domain = &context.config.federation.domain;
let ap_id = ObjectId::parse(&format!(
"{}://{domain}/user/{username}",
http_protocol_str()
@ -115,9 +115,9 @@ impl DbPerson {
pub fn read_from_ap_id(
ap_id: &ObjectId<DbPerson>,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<DbPerson> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
Ok(person::table
.filter(person::dsl::ap_id.eq(ap_id))
.get_result(conn.deref_mut())?)
@ -126,9 +126,9 @@ impl DbPerson {
pub fn read_from_name(
username: &str,
domain: &Option<String>,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<DbPerson> {
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
let mut query = person::table
.filter(person::username.eq(username))
.select(person::all_columns)
@ -144,8 +144,8 @@ impl DbPerson {
Ok(query.get_result(conn.deref_mut())?)
}
pub fn update_profile(params: &UpdateUserParams, data: &Data<IbisData>) -> MyResult<()> {
let mut conn = data.db_pool.get()?;
pub fn update_profile(params: &UpdateUserParams, context: &Data<IbisContext>) -> MyResult<()> {
let mut conn = context.db_pool.get()?;
diesel::update(person::table.find(params.person_id))
.set((
person::dsl::display_name.eq(&params.display_name),
@ -155,15 +155,15 @@ impl DbPerson {
Ok(())
}
pub fn read_local_from_name(username: &str, data: &IbisData) -> MyResult<LocalUserView> {
let mut conn = data.db_pool.get()?;
pub fn read_local_from_name(username: &str, context: &IbisContext) -> MyResult<LocalUserView> {
let mut conn = context.db_pool.get()?;
let (person, local_user) = person::table
.inner_join(local_user::table)
.filter(person::dsl::local)
.filter(person::dsl::username.eq(username))
.get_result::<(DbPerson, DbLocalUser)>(conn.deref_mut())?;
// TODO: handle this in single query
let following = Self::read_following(person.id, data)?;
let following = Self::read_following(person.id, context)?;
Ok(LocalUserView {
person,
local_user,
@ -171,9 +171,9 @@ impl DbPerson {
})
}
fn read_following(id_: PersonId, data: &IbisData) -> MyResult<Vec<DbInstance>> {
fn read_following(id_: PersonId, context: &IbisContext) -> MyResult<Vec<DbInstance>> {
use instance_follow::dsl::{follower_id, instance_id};
let mut conn = data.db_pool.get()?;
let mut conn = context.db_pool.get()?;
Ok(instance_follow::table
.inner_join(instance::table.on(instance_id.eq(instance::dsl::id)))
.filter(follower_id.eq(id_))
@ -182,13 +182,13 @@ impl DbPerson {
}
/// Ghost user serves as placeholder for deleted accounts
pub fn ghost(data: &Data<IbisData>) -> MyResult<DbPerson> {
pub fn ghost(context: &Data<IbisContext>) -> MyResult<DbPerson> {
let username = "ghost";
let read = DbPerson::read_from_name(username, &None, data);
let read = DbPerson::read_from_name(username, &None, context);
if read.is_ok() {
read
} else {
let domain = &data.config.federation.domain;
let domain = &context.config.federation.domain;
let ap_id = ObjectId::parse(&format!(
"{}://{domain}/user/{username}",
http_protocol_str()
@ -206,7 +206,7 @@ impl DbPerson {
display_name: None,
bio: None,
};
DbPerson::create(&person_form, data)
DbPerson::create(&person_form, context)
}
}
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::{activities::follow::Follow, send_activity},
utils::{
error::{Error, MyResult},
@ -32,10 +32,10 @@ impl Accept {
pub async fn send(
local_instance: DbInstance,
object: Follow,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<()> {
let id = generate_activity_id(data)?;
let follower = object.actor.dereference(data).await?;
let id = generate_activity_id(context)?;
let follower = object.actor.dereference(context).await?;
let accept = Accept {
actor: local_instance.ap_id.clone(),
object,
@ -46,7 +46,7 @@ impl Accept {
&local_instance,
accept,
vec![follower.shared_inbox_or_inbox()],
data,
context,
)
.await?;
Ok(())
@ -55,7 +55,7 @@ impl Accept {
#[async_trait::async_trait]
impl ActivityHandler for Accept {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -66,15 +66,15 @@ impl ActivityHandler for Accept {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
// add to follows
let person = self.object.actor.dereference_local(data).await?;
let instance = self.actor.dereference(data).await?;
DbInstance::follow(&person, &instance, false, data)?;
let person = self.object.actor.dereference_local(context).await?;
let instance = self.actor.dereference(context).await?;
DbInstance::follow(&person, &instance, false, context)?;
Ok(())
}
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::{routes::AnnouncableActivities, send_activity},
utils::{
error::{Error, MyResult},
@ -32,7 +32,7 @@ pub struct AnnounceActivity {
}
impl AnnounceActivity {
pub async fn send(object: AnnouncableActivities, context: &Data<IbisData>) -> MyResult<()> {
pub async fn send(object: AnnouncableActivities, context: &Data<IbisContext>) -> MyResult<()> {
let id = generate_activity_id(context)?;
let instance = DbInstance::read_local(context)?;
let announce = AnnounceActivity {
@ -56,7 +56,7 @@ impl AnnounceActivity {
#[async_trait::async_trait]
impl ActivityHandler for AnnounceActivity {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {

View file

@ -1,7 +1,7 @@
use super::generate_comment_activity_to;
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::{
objects::comment::ApubComment,
routes::AnnouncableActivities,
@ -40,16 +40,16 @@ pub struct CreateOrUpdateComment {
}
impl CreateOrUpdateComment {
pub async fn send(comment: &DbComment, data: &Data<IbisData>) -> MyResult<()> {
let instance = DbInstance::read_for_comment(comment.id, data)?;
pub async fn send(comment: &DbComment, context: &Data<IbisContext>) -> MyResult<()> {
let instance = DbInstance::read_for_comment(comment.id, context)?;
let kind = if comment.updated.is_none() {
CreateOrUpdateType::Create
} else {
CreateOrUpdateType::Update
};
let object = comment.clone().into_json(data).await?;
let id = generate_activity_id(data)?;
let object = comment.clone().into_json(context).await?;
let id = generate_activity_id(context)?;
let activity = Self {
actor: object.attributed_to.clone(),
object,
@ -58,15 +58,15 @@ impl CreateOrUpdateComment {
id,
};
let activity = AnnouncableActivities::CreateOrUpdateComment(activity);
let creator = DbPerson::read(comment.creator_id, data)?;
send_activity_to_instance(&creator, activity, &instance, data).await?;
let creator = DbPerson::read(comment.creator_id, context)?;
send_activity_to_instance(&creator, activity, &instance, context).await?;
Ok(())
}
}
#[async_trait::async_trait]
impl ActivityHandler for CreateOrUpdateComment {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -77,18 +77,18 @@ impl ActivityHandler for CreateOrUpdateComment {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
verify_domains_match(&self.id, self.object.id.inner())?;
verify_domains_match(&self.id, self.actor.inner())?;
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let comment = DbComment::from_json(self.object, data).await?;
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let comment = DbComment::from_json(self.object, context).await?;
let instance = DbInstance::read_for_comment(comment.id, data)?;
let instance = DbInstance::read_for_comment(comment.id, context)?;
if instance.local {
Self::send(&comment, data).await?;
Self::send(&comment, context).await?;
}
Ok(())
}

View file

@ -1,7 +1,7 @@
use super::generate_comment_activity_to;
use crate::{
backend::{
database::{comment::DbCommentUpdateForm, IbisData},
database::{comment::DbCommentUpdateForm, IbisContext},
federation::{routes::AnnouncableActivities, send_activity_to_instance},
utils::{
error::{Error, MyResult},
@ -38,9 +38,9 @@ impl DeleteComment {
comment: &DbComment,
creator: &DbPerson,
instance: &DbInstance,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<Self> {
let id = generate_activity_id(data)?;
let id = generate_activity_id(context)?;
Ok(DeleteComment {
actor: creator.ap_id.clone(),
object: comment.ap_id.clone(),
@ -49,19 +49,19 @@ impl DeleteComment {
id,
})
}
pub async fn send(comment: &DbComment, data: &Data<IbisData>) -> MyResult<()> {
let instance = DbInstance::read_for_comment(comment.id, data)?;
let creator = DbPerson::read(comment.creator_id, data)?;
let activity = Self::new(comment, &creator, &instance, data)?;
pub async fn send(comment: &DbComment, context: &Data<IbisContext>) -> MyResult<()> {
let instance = DbInstance::read_for_comment(comment.id, context)?;
let creator = DbPerson::read(comment.creator_id, context)?;
let activity = Self::new(comment, &creator, &instance, context)?;
let activity = AnnouncableActivities::DeleteComment(activity);
send_activity_to_instance(&creator, activity, &instance, data).await?;
send_activity_to_instance(&creator, activity, &instance, context).await?;
Ok(())
}
}
#[async_trait::async_trait]
impl ActivityHandler for DeleteComment {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -72,24 +72,24 @@ impl ActivityHandler for DeleteComment {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
verify_domains_match(self.actor.inner(), &self.id)?;
verify_domains_match(self.actor.inner(), self.object.inner())?;
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let form = DbCommentUpdateForm {
deleted: Some(true),
updated: Some(Utc::now()),
..Default::default()
};
let comment = self.object.dereference(data).await?;
DbComment::update(form, comment.id, data)?;
let comment = self.object.dereference(context).await?;
DbComment::update(form, comment.id, context)?;
let instance = DbInstance::read_for_comment(comment.id, data)?;
let instance = DbInstance::read_for_comment(comment.id, context)?;
if instance.local {
Self::send(&comment, data).await?;
Self::send(&comment, context).await?;
}
Ok(())
}

View file

@ -1,7 +1,7 @@
use super::{delete_comment::DeleteComment, generate_comment_activity_to};
use crate::{
backend::{
database::{comment::DbCommentUpdateForm, IbisData},
database::{comment::DbCommentUpdateForm, IbisContext},
federation::{routes::AnnouncableActivities, send_activity_to_instance},
utils::{
error::{Error, MyResult},
@ -37,11 +37,11 @@ pub struct UndoDeleteComment {
}
impl UndoDeleteComment {
pub async fn send(comment: &DbComment, data: &Data<IbisData>) -> MyResult<()> {
let instance = DbInstance::read_for_comment(comment.id, data)?;
let id = generate_activity_id(data)?;
let creator = DbPerson::read(comment.creator_id, data)?;
let object = DeleteComment::new(comment, &creator, &instance, data)?;
pub async fn send(comment: &DbComment, context: &Data<IbisContext>) -> MyResult<()> {
let instance = DbInstance::read_for_comment(comment.id, context)?;
let id = generate_activity_id(context)?;
let creator = DbPerson::read(comment.creator_id, context)?;
let object = DeleteComment::new(comment, &creator, &instance, context)?;
let activity = UndoDeleteComment {
actor: creator.ap_id.clone(),
object,
@ -50,14 +50,14 @@ impl UndoDeleteComment {
id,
};
let activity = AnnouncableActivities::UndoDeleteComment(activity);
send_activity_to_instance(&creator, activity, &instance, data).await?;
send_activity_to_instance(&creator, activity, &instance, context).await?;
Ok(())
}
}
#[async_trait::async_trait]
impl ActivityHandler for UndoDeleteComment {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -68,24 +68,24 @@ impl ActivityHandler for UndoDeleteComment {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
verify_domains_match(self.actor.inner(), &self.id)?;
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let form = DbCommentUpdateForm {
deleted: Some(false),
updated: Some(Utc::now()),
..Default::default()
};
let comment = self.object.object.dereference(data).await?;
DbComment::update(form, comment.id, data)?;
let comment = self.object.object.dereference(context).await?;
DbComment::update(form, comment.id, context)?;
let instance = DbInstance::read_for_comment(comment.id, data)?;
let instance = DbInstance::read_for_comment(comment.id, context)?;
if instance.local {
Self::send(&comment, data).await?;
Self::send(&comment, context).await?;
}
Ok(())
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::objects::article::ApubArticle,
utils::{
error::{Error, MyResult},
@ -32,11 +32,14 @@ pub struct CreateArticle {
}
impl CreateArticle {
pub async fn send_to_followers(article: DbArticle, data: &Data<IbisData>) -> MyResult<()> {
let local_instance = DbInstance::read_local(data)?;
let object = article.clone().into_json(data).await?;
let id = generate_activity_id(data)?;
let to = local_instance.follower_ids(data)?;
pub async fn send_to_followers(
article: DbArticle,
context: &Data<IbisContext>,
) -> MyResult<()> {
let local_instance = DbInstance::read_local(context)?;
let object = article.clone().into_json(context).await?;
let id = generate_activity_id(context)?;
let to = local_instance.follower_ids(context)?;
let create = CreateArticle {
actor: local_instance.ap_id.clone(),
to,
@ -45,14 +48,14 @@ impl CreateArticle {
id,
};
local_instance
.send_to_followers(create, vec![], data)
.send_to_followers(create, vec![], context)
.await?;
Ok(())
}
}
#[async_trait::async_trait]
impl ActivityHandler for CreateArticle {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -63,15 +66,17 @@ impl ActivityHandler for CreateArticle {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let article = DbArticle::from_json(self.object.clone(), data).await?;
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let article = DbArticle::from_json(self.object.clone(), context).await?;
if article.local {
let local_instance = DbInstance::read_local(data)?;
local_instance.send_to_followers(self, vec![], data).await?;
let local_instance = DbInstance::read_local(context)?;
local_instance
.send_to_followers(self, vec![], context)
.await?;
}
Ok(())
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::{activities::accept::Accept, send_activity},
generate_activity_id,
utils::error::{Error, MyResult},
@ -28,22 +28,26 @@ pub struct Follow {
}
impl Follow {
pub async fn send(actor: DbPerson, to: &DbInstance, data: &Data<IbisData>) -> MyResult<()> {
let id = generate_activity_id(data)?;
pub async fn send(
actor: DbPerson,
to: &DbInstance,
context: &Data<IbisContext>,
) -> MyResult<()> {
let id = generate_activity_id(context)?;
let follow = Follow {
actor: actor.ap_id.clone(),
object: to.ap_id.clone(),
kind: Default::default(),
id,
};
send_activity(&actor, follow, vec![to.shared_inbox_or_inbox()], data).await?;
send_activity(&actor, follow, vec![to.shared_inbox_or_inbox()], context).await?;
Ok(())
}
}
#[async_trait::async_trait]
impl ActivityHandler for Follow {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -54,18 +58,18 @@ impl ActivityHandler for Follow {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let actor = self.actor.dereference(data).await?;
let local_instance = DbInstance::read_local(data)?;
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let actor = self.actor.dereference(context).await?;
let local_instance = DbInstance::read_local(context)?;
verify_urls_match(self.object.inner(), local_instance.ap_id.inner())?;
DbInstance::follow(&actor, &local_instance, false, data)?;
DbInstance::follow(&actor, &local_instance, false, context)?;
// send back an accept
Accept::send(local_instance, self, data).await?;
Accept::send(local_instance, self, context).await?;
Ok(())
}
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::{edit::DbEditForm, IbisData},
database::{edit::DbEditForm, IbisContext},
federation::activities::{
update_local_article::UpdateLocalArticle,
update_remote_article::UpdateRemoteArticle,
@ -30,7 +30,7 @@ pub async fn submit_article_update(
previous_version: EditVersion,
original_article: &DbArticle,
creator_id: PersonId,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> Result<(), Error> {
let mut form = DbEditForm::new(
original_article,
@ -41,16 +41,16 @@ pub async fn submit_article_update(
false,
)?;
if original_article.local {
let edit = DbEdit::create(&form, data)?;
let updated_article = DbArticle::update_text(edit.article_id, &new_text, data)?;
let edit = DbEdit::create(&form, context)?;
let updated_article = DbArticle::update_text(edit.article_id, &new_text, context)?;
UpdateLocalArticle::send(updated_article, vec![], data).await?;
UpdateLocalArticle::send(updated_article, vec![], context).await?;
} else {
// insert edit as pending, so only the creator can see it
form.pending = true;
let edit = DbEdit::create(&form, data)?;
let instance = DbInstance::read(original_article.instance_id, data)?;
UpdateRemoteArticle::send(edit, instance, data).await?;
let edit = DbEdit::create(&form, context)?;
let instance = DbInstance::read(original_article.instance_id, context)?;
UpdateRemoteArticle::send(edit, instance, context).await?;
}
Ok(())
}

View file

@ -2,7 +2,7 @@ use crate::{
backend::{
database::{
conflict::{DbConflict, DbConflictForm},
IbisData,
IbisContext,
},
federation::{objects::edit::ApubEdit, send_activity},
utils::{
@ -38,10 +38,10 @@ impl RejectEdit {
pub async fn send(
edit: ApubEdit,
user_instance: DbInstance,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<()> {
let local_instance = DbInstance::read_local(data)?;
let id = generate_activity_id(data)?;
let local_instance = DbInstance::read_local(context)?;
let id = generate_activity_id(context)?;
let reject = RejectEdit {
actor: local_instance.ap_id.clone(),
to: vec![user_instance.ap_id.into_inner()],
@ -53,7 +53,7 @@ impl RejectEdit {
&local_instance,
reject,
vec![Url::parse(&user_instance.inbox_url)?],
data,
context,
)
.await?;
Ok(())
@ -62,7 +62,7 @@ impl RejectEdit {
#[async_trait::async_trait]
impl ActivityHandler for RejectEdit {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -73,14 +73,14 @@ impl ActivityHandler for RejectEdit {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
// cant convert this to DbEdit as it tries to apply patch and fails
let article = self.object.object.dereference(data).await?;
let creator = self.object.attributed_to.dereference(data).await?;
let article = self.object.object.dereference(context).await?;
let creator = self.object.attributed_to.dereference(context).await?;
let form = DbConflictForm {
hash: EditVersion::new(&self.object.content),
diff: self.object.content,
@ -89,7 +89,7 @@ impl ActivityHandler for RejectEdit {
article_id: article.id,
previous_version_id: self.object.previous_version,
};
DbConflict::create(&form, data)?;
DbConflict::create(&form, context)?;
Ok(())
}
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::objects::article::ApubArticle,
utils::{
error::{Error, MyResult},
@ -36,22 +36,22 @@ impl UpdateLocalArticle {
pub async fn send(
article: DbArticle,
extra_recipients: Vec<DbInstance>,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<()> {
debug_assert!(article.local);
let local_instance = DbInstance::read_local(data)?;
let id = generate_activity_id(data)?;
let mut to = local_instance.follower_ids(data)?;
let local_instance = DbInstance::read_local(context)?;
let id = generate_activity_id(context)?;
let mut to = local_instance.follower_ids(context)?;
to.extend(extra_recipients.iter().map(|i| i.ap_id.inner().clone()));
let update = UpdateLocalArticle {
actor: local_instance.ap_id.clone(),
to,
object: article.into_json(data).await?,
object: article.into_json(context).await?,
kind: Default::default(),
id,
};
local_instance
.send_to_followers(update, extra_recipients, data)
.send_to_followers(update, extra_recipients, context)
.await?;
Ok(())
}
@ -59,7 +59,7 @@ impl UpdateLocalArticle {
#[async_trait::async_trait]
impl ActivityHandler for UpdateLocalArticle {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -70,13 +70,13 @@ impl ActivityHandler for UpdateLocalArticle {
self.actor.inner()
}
async fn verify(&self, _data: &Data<Self::DataType>) -> Result<(), Self::Error> {
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), Self::Error> {
Ok(())
}
/// Received on article follower instances (where article is always remote)
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
DbArticle::from_json(self.object, data).await?;
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
DbArticle::from_json(self.object, context).await?;
Ok(())
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::{
activities::{reject::RejectEdit, update_local_article::UpdateLocalArticle},
objects::edit::ApubEdit,
@ -45,14 +45,14 @@ impl UpdateRemoteArticle {
pub async fn send(
edit: DbEdit,
article_instance: DbInstance,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<()> {
let local_instance = DbInstance::read_local(data)?;
let id = generate_activity_id(data)?;
let local_instance = DbInstance::read_local(context)?;
let id = generate_activity_id(context)?;
let update = UpdateRemoteArticle {
actor: local_instance.ap_id.clone(),
to: vec![article_instance.ap_id.into_inner()],
object: edit.into_json(data).await?,
object: edit.into_json(context).await?,
kind: Default::default(),
id,
};
@ -60,7 +60,7 @@ impl UpdateRemoteArticle {
&local_instance,
update,
vec![Url::parse(&article_instance.inbox_url)?],
data,
context,
)
.await?;
Ok(())
@ -69,7 +69,7 @@ impl UpdateRemoteArticle {
#[async_trait::async_trait]
impl ActivityHandler for UpdateRemoteArticle {
type DataType = IbisData;
type DataType = IbisContext;
type Error = Error;
fn id(&self) -> &Url {
@ -80,27 +80,31 @@ impl ActivityHandler for UpdateRemoteArticle {
self.actor.inner()
}
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let article = DbArticle::read_from_ap_id(&self.object.object, data)?;
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let article = DbArticle::read_from_ap_id(&self.object.object, context)?;
can_edit_article(&article, false)?;
Ok(())
}
/// Received on article origin instance
async fn receive(self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
let local_article = DbArticle::read_from_ap_id(&self.object.object, data)?;
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
let local_article = DbArticle::read_from_ap_id(&self.object.object, context)?;
let patch = Patch::from_str(&self.object.content)?;
match apply(&local_article.text, &patch) {
Ok(applied) => {
let edit = DbEdit::from_json(self.object.clone(), data).await?;
let article = DbArticle::update_text(edit.article_id, &applied, data)?;
UpdateLocalArticle::send(article, vec![self.actor.dereference(data).await?], data)
.await?;
let edit = DbEdit::from_json(self.object.clone(), context).await?;
let article = DbArticle::update_text(edit.article_id, &applied, context)?;
UpdateLocalArticle::send(
article,
vec![self.actor.dereference(context).await?],
context,
)
.await?;
}
Err(_e) => {
let user_instance = self.actor.dereference(data).await?;
RejectEdit::send(self.object.clone(), user_instance, data).await?;
let user_instance = self.actor.dereference(context).await?;
RejectEdit::send(self.object.clone(), user_instance, context).await?;
}
}

View file

@ -1,6 +1,6 @@
use super::utils::error::MyResult;
use crate::{
backend::{config::IbisConfig, database::IbisData},
backend::{config::IbisConfig, database::IbisContext},
common::{instance::DbInstance, user::DbPerson},
};
use activities::announce::AnnounceActivity;
@ -25,14 +25,14 @@ pub async fn send_activity<Activity, ActorType: Actor>(
actor: &ActorType,
activity: Activity,
recipients: Vec<Url>,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> Result<(), <Activity as ActivityHandler>::Error>
where
Activity: ActivityHandler + Serialize + Debug + Send + Sync,
<Activity as ActivityHandler>::Error: From<activitypub_federation::error::Error>,
{
let activity = WithContext::new_default(activity);
queue_activity(&activity, actor, recipients, data).await?;
queue_activity(&activity, actor, recipients, context).await?;
Ok(())
}
@ -40,13 +40,13 @@ pub async fn send_activity_to_instance(
actor: &DbPerson,
activity: AnnouncableActivities,
instance: &DbInstance,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> MyResult<()> {
if instance.local {
AnnounceActivity::send(activity, data).await?;
AnnounceActivity::send(activity, context).await?;
} else {
let inbox_url = instance.inbox_url.parse()?;
send_activity(actor, activity, vec![inbox_url], data).await?;
send_activity(actor, activity, vec![inbox_url], context).await?;
}
Ok(())
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::{article::DbArticleForm, IbisData},
database::{article::DbArticleForm, IbisContext},
federation::objects::edits_collection::DbEditCollection,
utils::{error::Error, validate::validate_article_title},
},
@ -40,27 +40,27 @@ pub struct ApubArticle {
#[async_trait::async_trait]
impl Object for DbArticle {
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ApubArticle;
type Error = Error;
async fn read_from_id(
object_id: Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
let article = DbArticle::read_from_ap_id(&object_id.into(), data).ok();
let article = DbArticle::read_from_ap_id(&object_id.into(), context).ok();
Ok(article)
}
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let local_instance = DbInstance::read_local(data)?;
async fn into_json(self, context: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let local_instance = DbInstance::read_local(context)?;
Ok(ApubArticle {
kind: Default::default(),
id: self.ap_id.clone(),
attributed_to: local_instance.ap_id.clone(),
to: vec![public(), local_instance.followers_url()?],
edits: self.edits_id()?,
latest_version: self.latest_edit_version(data)?,
latest_version: self.latest_edit_version(context)?,
content: self.text,
name: self.title,
protected: self.protected,
@ -70,15 +70,18 @@ impl Object for DbArticle {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(json.id.inner(), expected_domain)?;
verify_is_remote_object(&json.id, data)?;
verify_is_remote_object(&json.id, context)?;
Ok(())
}
async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
let instance = json.attributed_to.dereference(data).await?;
async fn from_json(
json: Self::Kind,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
let instance = json.attributed_to.dereference(context).await?;
let mut form = DbArticleForm {
title: json.name,
text: json.content,
@ -89,9 +92,9 @@ impl Object for DbArticle {
approved: true,
};
form.title = validate_article_title(&form.title)?;
let article = DbArticle::create_or_update(form, data)?;
let article = DbArticle::create_or_update(form, context)?;
json.edits.dereference(&article, data).await?;
json.edits.dereference(&article, context).await?;
Ok(article)
}

View file

@ -1,7 +1,7 @@
use super::{article::ApubArticle, comment::ApubComment};
use crate::{
backend::{
database::IbisData,
database::IbisContext,
utils::error::{Error, MyResult},
},
common::{article::DbArticle, comment::DbComment},
@ -26,7 +26,7 @@ pub enum ApubArticleOrComment {
#[async_trait::async_trait]
impl Object for DbArticleOrComment {
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ApubArticleOrComment;
type Error = Error;
@ -34,38 +34,41 @@ impl Object for DbArticleOrComment {
None
}
async fn read_from_id(object_id: Url, data: &Data<Self::DataType>) -> MyResult<Option<Self>> {
let post = DbArticle::read_from_id(object_id.clone(), data).await?;
async fn read_from_id(
object_id: Url,
context: &Data<Self::DataType>,
) -> MyResult<Option<Self>> {
let post = DbArticle::read_from_id(object_id.clone(), context).await?;
Ok(match post {
Some(o) => Some(Self::Article(o)),
None => DbComment::read_from_id(object_id, data)
None => DbComment::read_from_id(object_id, context)
.await?
.map(Self::Comment),
})
}
async fn delete(self, data: &Data<Self::DataType>) -> MyResult<()> {
async fn delete(self, context: &Data<Self::DataType>) -> MyResult<()> {
match self {
Self::Article(p) => p.delete(data).await,
Self::Comment(c) => c.delete(data).await,
Self::Article(p) => p.delete(context).await,
Self::Comment(c) => c.delete(context).await,
}
}
async fn into_json(self, data: &Data<Self::DataType>) -> MyResult<Self::Kind> {
async fn into_json(self, context: &Data<Self::DataType>) -> MyResult<Self::Kind> {
Ok(match self {
Self::Article(p) => Self::Kind::Article(Box::new(p.into_json(data).await?)),
Self::Comment(c) => Self::Kind::Comment(Box::new(c.into_json(data).await?)),
Self::Article(p) => Self::Kind::Article(Box::new(p.into_json(context).await?)),
Self::Comment(c) => Self::Kind::Comment(Box::new(c.into_json(context).await?)),
})
}
async fn verify(
apub: &Self::Kind,
expected_domain: &Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> MyResult<()> {
match apub {
Self::Kind::Article(a) => DbArticle::verify(a, expected_domain, data).await,
Self::Kind::Comment(a) => DbComment::verify(a, expected_domain, data).await,
Self::Kind::Article(a) => DbArticle::verify(a, expected_domain, context).await,
Self::Kind::Comment(a) => DbComment::verify(a, expected_domain, context).await,
}
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::objects::article::ApubArticle,
utils::error::{Error, MyResult},
},
@ -40,25 +40,25 @@ pub fn local_articles_url(domain: &str) -> MyResult<CollectionId<DbArticleCollec
#[async_trait::async_trait]
impl Collection for DbArticleCollection {
type Owner = ();
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ArticleCollection;
type Error = Error;
async fn read_local(
_owner: &Self::Owner,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Self::Kind, Self::Error> {
let local_articles = DbArticle::read_all(Some(true), None, data)?;
let local_articles = DbArticle::read_all(Some(true), None, context)?;
let articles = try_join_all(
local_articles
.into_iter()
.map(|a| a.into_json(data))
.map(|a| a.into_json(context))
.collect::<Vec<_>>(),
)
.await?;
let collection = ArticleCollection {
r#type: Default::default(),
id: local_articles_url(&data.config.federation.domain)?.into(),
id: local_articles_url(&context.config.federation.domain)?.into(),
total_items: articles.len() as i32,
items: articles,
};
@ -68,7 +68,7 @@ impl Collection for DbArticleCollection {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
_data: &Data<Self::DataType>,
_context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(&json.id, expected_domain)?;
Ok(())
@ -77,20 +77,20 @@ impl Collection for DbArticleCollection {
async fn from_json(
apub: Self::Kind,
_owner: &Self::Owner,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
let articles =
apub.items
.into_iter()
.filter(|i| !i.id.is_local(data))
.map(|article| async {
let id = article.id.clone();
let res = DbArticle::from_json(article, data).await;
if let Err(e) = &res {
warn!("Failed to synchronize article {id}: {e}");
}
res
});
let articles = apub
.items
.into_iter()
.filter(|i| !i.id.is_local(context))
.map(|article| async {
let id = article.id.clone();
let res = DbArticle::from_json(article, context).await;
if let Err(e) = &res {
warn!("Failed to synchronize article {id}: {e}");
}
res
});
join_all(articles).await;
Ok(DbArticleCollection(()))

View file

@ -1,7 +1,7 @@
use super::article_or_comment::DbArticleOrComment;
use crate::{
backend::{
database::{comment::DbCommentInsertForm, IbisData},
database::{comment::DbCommentInsertForm, IbisContext},
utils::{error::Error, validate::validate_comment_max_depth},
},
common::{article::DbArticle, comment::DbComment, user::DbPerson},
@ -37,24 +37,24 @@ pub struct ApubComment {
#[async_trait::async_trait]
impl Object for DbComment {
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ApubComment;
type Error = Error;
async fn read_from_id(
object_id: Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
Ok(DbComment::read_from_ap_id(&object_id.into(), data).ok())
Ok(DbComment::read_from_ap_id(&object_id.into(), context).ok())
}
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let creator = DbPerson::read(self.creator_id, data)?;
async fn into_json(self, context: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let creator = DbPerson::read(self.creator_id, context)?;
let in_reply_to = if let Some(parent_comment_id) = self.parent_id {
let comment = DbComment::read(parent_comment_id, data)?;
let comment = DbComment::read(parent_comment_id, context)?;
comment.ap_id.into_inner().into()
} else {
let article = DbArticle::read(self.article_id, data)?;
let article = DbArticle::read(self.article_id, context)?;
article.ap_id.into_inner().into()
};
Ok(ApubComment {
@ -72,15 +72,18 @@ impl Object for DbComment {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(json.id.inner(), expected_domain)?;
verify_is_remote_object(&json.id, data)?;
verify_is_remote_object(&json.id, context)?;
Ok(())
}
async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
let parent = json.in_reply_to.dereference(data).await?;
async fn from_json(
json: Self::Kind,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
let parent = json.in_reply_to.dereference(context).await?;
let (article_id, parent_id, depth) = match parent {
DbArticleOrComment::Article(db_article) => (db_article.id, None, 0),
DbArticleOrComment::Comment(db_comment) => (
@ -89,7 +92,7 @@ impl Object for DbComment {
db_comment.depth + 1,
),
};
let creator = json.attributed_to.dereference(data).await?;
let creator = json.attributed_to.dereference(context).await?;
validate_comment_max_depth(depth)?;
let form = DbCommentInsertForm {
@ -105,6 +108,6 @@ impl Object for DbComment {
depth,
};
Ok(DbComment::create_or_update(form, data)?)
Ok(DbComment::create_or_update(form, context)?)
}
}

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::{edit::DbEditForm, IbisData},
database::{edit::DbEditForm, IbisContext},
utils::error::Error,
},
common::{
@ -43,20 +43,20 @@ pub struct ApubEdit {
#[async_trait::async_trait]
impl Object for DbEdit {
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ApubEdit;
type Error = Error;
async fn read_from_id(
object_id: Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
Ok(DbEdit::read_from_ap_id(&object_id.into(), data).ok())
Ok(DbEdit::read_from_ap_id(&object_id.into(), context).ok())
}
async fn into_json(self, data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let article = DbArticle::read_view(self.article_id, data)?;
let creator = DbPerson::read(self.creator_id, data)?;
async fn into_json(self, context: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
let article = DbArticle::read_view(self.article_id, context)?;
let creator = DbPerson::read(self.creator_id, context)?;
Ok(ApubEdit {
kind: PatchType::Patch,
id: self.ap_id,
@ -73,21 +73,24 @@ impl Object for DbEdit {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(json.id.inner(), expected_domain)?;
verify_is_remote_object(&json.id, data)?;
verify_is_remote_object(&json.id, context)?;
Ok(())
}
async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
let article = json.object.dereference(data).await?;
let creator = match json.attributed_to.dereference(data).await {
async fn from_json(
json: Self::Kind,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
let article = json.object.dereference(context).await?;
let creator = match json.attributed_to.dereference(context).await {
Ok(c) => c,
Err(e) => {
// If actor couldnt be fetched, use ghost as placeholder
warn!("Failed to fetch user {}: {e}", json.attributed_to);
DbPerson::ghost(data)?
DbPerson::ghost(context)?
}
};
let form = DbEditForm {
@ -101,7 +104,7 @@ impl Object for DbEdit {
published: json.published,
pending: false,
};
let edit = DbEdit::create(&form, data)?;
let edit = DbEdit::create(&form, context)?;
Ok(edit)
}
}

View file

@ -1,5 +1,5 @@
use crate::{
backend::{database::IbisData, federation::objects::edit::ApubEdit, utils::error::Error},
backend::{database::IbisContext, federation::objects::edit::ApubEdit, utils::error::Error},
common::article::{DbArticle, DbEdit},
};
use activitypub_federation::{
@ -28,20 +28,20 @@ pub struct DbEditCollection();
#[async_trait::async_trait]
impl Collection for DbEditCollection {
type Owner = DbArticle;
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ApubEditCollection;
type Error = Error;
async fn read_local(
article: &Self::Owner,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Self::Kind, Self::Error> {
let article = DbArticle::read(article.id, data)?;
let edits = DbEdit::list_for_article(article.id, data)?;
let article = DbArticle::read(article.id, context)?;
let edits = DbEdit::list_for_article(article.id, context)?;
let edits = future::try_join_all(
edits
.into_iter()
.map(|e| e.into_json(data))
.map(|e| e.into_json(context))
.collect::<Vec<_>>(),
)
.await?;
@ -57,7 +57,7 @@ impl Collection for DbEditCollection {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
_data: &Data<Self::DataType>,
_context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(&json.id, expected_domain)?;
Ok(())
@ -66,12 +66,16 @@ impl Collection for DbEditCollection {
async fn from_json(
apub: Self::Kind,
owner: &Self::Owner,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
try_join_all(apub.items.into_iter().map(|i| DbEdit::from_json(i, data)))
.await
.map_err(|e| warn!("Failed to synchronize edits for {}: {e}", owner.ap_id))
.ok();
try_join_all(
apub.items
.into_iter()
.map(|i| DbEdit::from_json(i, context)),
)
.await
.map_err(|e| warn!("Failed to synchronize edits for {}: {e}", owner.ap_id))
.ok();
Ok(DbEditCollection())
}
}

View file

@ -1,7 +1,7 @@
use super::instance_collection::DbInstanceCollection;
use crate::{
backend::{
database::{instance::DbInstanceForm, IbisData},
database::{instance::DbInstanceForm, IbisContext},
federation::{objects::articles_collection::DbArticleCollection, send_activity},
utils::error::{Error, MyResult},
},
@ -40,8 +40,8 @@ impl DbInstance {
Ok(Url::parse(&format!("{}/followers", self.ap_id.inner()))?)
}
pub fn follower_ids(&self, data: &Data<IbisData>) -> MyResult<Vec<Url>> {
Ok(DbInstance::read_followers(self.id, data)?
pub fn follower_ids(&self, context: &Data<IbisContext>) -> MyResult<Vec<Url>> {
Ok(DbInstance::read_followers(self.id, context)?
.into_iter()
.map(|f| f.ap_id.into())
.collect())
@ -51,26 +51,26 @@ impl DbInstance {
&self,
activity: Activity,
extra_recipients: Vec<DbInstance>,
data: &Data<IbisData>,
context: &Data<IbisContext>,
) -> Result<(), <Activity as ActivityHandler>::Error>
where
Activity: ActivityHandler + Serialize + Debug + Send + Sync,
<Activity as ActivityHandler>::Error: From<activitypub_federation::error::Error>,
<Activity as ActivityHandler>::Error: From<Error>,
{
let mut inboxes: Vec<_> = DbInstance::read_followers(self.id, data)?
let mut inboxes: Vec<_> = DbInstance::read_followers(self.id, context)?
.iter()
.map(|f| f.inbox_url())
.collect();
inboxes.extend(extra_recipients.into_iter().map(|i| i.inbox_url()));
send_activity(self, activity, inboxes, data).await?;
send_activity(self, activity, inboxes, context).await?;
Ok(())
}
}
#[async_trait::async_trait]
impl Object for DbInstance {
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ApubInstance;
type Error = Error;
@ -80,12 +80,12 @@ impl Object for DbInstance {
async fn read_from_id(
object_id: Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
Ok(DbInstance::read_from_ap_id(&object_id.into(), data).ok())
Ok(DbInstance::read_from_ap_id(&object_id.into(), context).ok())
}
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
async fn into_json(self, _context: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
Ok(ApubInstance {
kind: Default::default(),
id: self.ap_id.clone(),
@ -100,14 +100,17 @@ impl Object for DbInstance {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(json.id.inner(), expected_domain)?;
verify_is_remote_object(&json.id, data)?;
verify_is_remote_object(&json.id, context)?;
Ok(())
}
async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
async fn from_json(
json: Self::Kind,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
let domain = extract_domain(&json.id);
let form = DbInstanceForm {
domain,
@ -121,20 +124,20 @@ impl Object for DbInstance {
last_refreshed_at: Utc::now(),
local: false,
};
let instance = DbInstance::create(&form, data)?;
let instance = DbInstance::create(&form, context)?;
// TODO: very inefficient to sync all articles every time
let instance_ = instance.clone();
let data_ = data.reset_request_count();
let context_ = context.reset_request_count();
tokio::spawn(async move {
if let Some(articles_url) = &instance_.articles_url {
let res = articles_url.dereference(&(), &data_).await;
let res = articles_url.dereference(&(), &context_).await;
if let Err(e) = res {
tracing::warn!("error in spawn: {e}");
}
}
if let Some(instances_url) = &instance_.instances_url {
let res = instances_url.dereference(&(), &data_).await;
let res = instances_url.dereference(&(), &context_).await;
if let Err(e) = res {
tracing::warn!("error in spawn: {e}");
}

View file

@ -1,7 +1,7 @@
use super::instance::ApubInstance;
use crate::{
backend::{
database::IbisData,
database::IbisContext,
utils::error::{Error, MyResult},
},
common::{instance::DbInstance, utils::http_protocol_str},
@ -40,25 +40,25 @@ pub fn linked_instances_url(domain: &str) -> MyResult<CollectionId<DbInstanceCol
#[async_trait::async_trait]
impl Collection for DbInstanceCollection {
type Owner = ();
type DataType = IbisData;
type DataType = IbisContext;
type Kind = InstanceCollection;
type Error = Error;
async fn read_local(
_owner: &Self::Owner,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Self::Kind, Self::Error> {
let instances = DbInstance::read_remote(data)?;
let instances = DbInstance::read_remote(context)?;
let instances = future::try_join_all(
instances
.into_iter()
.map(|i| i.into_json(data))
.map(|i| i.into_json(context))
.collect::<Vec<_>>(),
)
.await?;
let collection = InstanceCollection {
r#type: Default::default(),
id: linked_instances_url(&data.config.federation.domain)?.into(),
id: linked_instances_url(&context.config.federation.domain)?.into(),
total_items: instances.len() as i32,
items: instances,
};
@ -68,7 +68,7 @@ impl Collection for DbInstanceCollection {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
_data: &Data<Self::DataType>,
_context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(&json.id, expected_domain)?;
Ok(())
@ -77,20 +77,20 @@ impl Collection for DbInstanceCollection {
async fn from_json(
apub: Self::Kind,
_owner: &Self::Owner,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
let instances =
apub.items
.into_iter()
.filter(|i| !i.id.is_local(data))
.map(|instance| async {
let id = instance.id.clone();
let res = DbInstance::from_json(instance, data).await;
if let Err(e) = &res {
warn!("Failed to synchronize article {id}: {e}");
}
res
});
let instances = apub
.items
.into_iter()
.filter(|i| !i.id.is_local(context))
.map(|instance| async {
let id = instance.id.clone();
let res = DbInstance::from_json(instance, context).await;
if let Err(e) = &res {
warn!("Failed to synchronize article {id}: {e}");
}
res
});
join_all(instances).await;
Ok(DbInstanceCollection(()))

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::{user::DbPersonForm, IbisData},
database::{user::DbPersonForm, IbisContext},
utils::error::Error,
},
common::user::DbPerson,
@ -33,7 +33,7 @@ pub struct ApubUser {
#[async_trait::async_trait]
impl Object for DbPerson {
type DataType = IbisData;
type DataType = IbisContext;
type Kind = ApubUser;
type Error = Error;
@ -43,12 +43,12 @@ impl Object for DbPerson {
async fn read_from_id(
object_id: Url,
data: &Data<Self::DataType>,
context: &Data<Self::DataType>,
) -> Result<Option<Self>, Self::Error> {
Ok(DbPerson::read_from_ap_id(&object_id.into(), data).ok())
Ok(DbPerson::read_from_ap_id(&object_id.into(), context).ok())
}
async fn into_json(self, _data: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
async fn into_json(self, _context: &Data<Self::DataType>) -> Result<Self::Kind, Self::Error> {
Ok(ApubUser {
kind: Default::default(),
id: __self.ap_id.clone(),
@ -63,13 +63,16 @@ impl Object for DbPerson {
async fn verify(
json: &Self::Kind,
expected_domain: &Url,
_data: &Data<Self::DataType>,
_context: &Data<Self::DataType>,
) -> Result<(), Self::Error> {
verify_domains_match(json.id.inner(), expected_domain)?;
Ok(())
}
async fn from_json(json: Self::Kind, data: &Data<Self::DataType>) -> Result<Self, Self::Error> {
async fn from_json(
json: Self::Kind,
context: &Data<Self::DataType>,
) -> Result<Self, Self::Error> {
let form = DbPersonForm {
username: json.preferred_username,
ap_id: json.id,
@ -81,7 +84,7 @@ impl Object for DbPerson {
display_name: json.name,
bio: json.summary,
};
DbPerson::create(&form, data)
DbPerson::create(&form, context)
}
}

View file

@ -11,7 +11,7 @@ use super::{
};
use crate::{
backend::{
database::IbisData,
database::IbisContext,
federation::{
activities::{
accept::Accept,
@ -74,66 +74,66 @@ pub fn federation_routes() -> Router<()> {
#[debug_handler]
async fn http_get_instance(
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<FederationJson<WithContext<ApubInstance>>> {
let local_instance = DbInstance::read_local(&data)?;
let json_instance = local_instance.into_json(&data).await?;
let local_instance = DbInstance::read_local(&context)?;
let json_instance = local_instance.into_json(&context).await?;
Ok(FederationJson(WithContext::new_default(json_instance)))
}
#[debug_handler]
async fn http_get_person(
Path(name): Path<String>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<FederationJson<WithContext<ApubUser>>> {
let person = DbPerson::read_local_from_name(&name, &data)?.person;
let json_person = person.into_json(&data).await?;
let person = DbPerson::read_local_from_name(&name, &context)?.person;
let json_person = person.into_json(&context).await?;
Ok(FederationJson(WithContext::new_default(json_person)))
}
#[debug_handler]
async fn http_get_all_articles(
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<FederationJson<WithContext<ArticleCollection>>> {
let collection = DbArticleCollection::read_local(&(), &data).await?;
let collection = DbArticleCollection::read_local(&(), &context).await?;
Ok(FederationJson(WithContext::new_default(collection)))
}
#[debug_handler]
async fn http_get_linked_instances(
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<FederationJson<WithContext<InstanceCollection>>> {
let collection = DbInstanceCollection::read_local(&(), &data).await?;
let collection = DbInstanceCollection::read_local(&(), &context).await?;
Ok(FederationJson(WithContext::new_default(collection)))
}
#[debug_handler]
async fn http_get_article(
Path(title): Path<String>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<FederationJson<WithContext<ApubArticle>>> {
let article = DbArticle::read_view_title(&title, None, &data)?;
let json = article.article.into_json(&data).await?;
let article = DbArticle::read_view_title(&title, None, &context)?;
let json = article.article.into_json(&context).await?;
Ok(FederationJson(WithContext::new_default(json)))
}
#[debug_handler]
async fn http_get_article_edits(
Path(title): Path<String>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<FederationJson<WithContext<ApubEditCollection>>> {
let article = DbArticle::read_view_title(&title, None, &data)?;
let json = DbEditCollection::read_local(&article.article, &data).await?;
let article = DbArticle::read_view_title(&title, None, &context)?;
let json = DbEditCollection::read_local(&article.article, &context).await?;
Ok(FederationJson(WithContext::new_default(json)))
}
#[debug_handler]
async fn http_get_comment(
Path(id): Path<i32>,
data: Data<IbisData>,
context: Data<IbisContext>,
) -> MyResult<FederationJson<WithContext<ApubComment>>> {
let comment = DbComment::read(CommentId(id), &data)?;
let json = comment.into_json(&data).await?;
let comment = DbComment::read(CommentId(id), &context)?;
let json = comment.into_json(&context).await?;
Ok(FederationJson(WithContext::new_default(json)))
}
@ -163,10 +163,10 @@ pub enum AnnouncableActivities {
#[debug_handler]
pub async fn http_post_inbox(
data: Data<IbisData>,
context: Data<IbisContext>,
activity_data: ActivityData,
) -> impl IntoResponse {
receive_activity::<WithContext<InboxActivities>, UserOrInstance, IbisData>(activity_data, &data)
receive_activity::<WithContext<InboxActivities>, UserOrInstance, _>(activity_data, &context)
.await
}
@ -191,7 +191,7 @@ pub enum PersonOrInstanceType {
#[async_trait::async_trait]
impl Object for UserOrInstance {
type DataType = IbisData;
type DataType = IbisContext;
type Kind = PersonOrInstance;
type Error = Error;

View file

@ -1,7 +1,7 @@
use crate::{
backend::{
config::IbisConfig,
database::{article::DbArticleForm, instance::DbInstanceForm, IbisData},
database::{article::DbArticleForm, instance::DbInstanceForm, IbisContext},
federation::{activities::submit_article_update, VerifyUrlData},
utils::{
error::{Error, MyResult},
@ -59,11 +59,11 @@ pub async fn start(
.get()?
.run_pending_migrations(MIGRATIONS)
.expect("run migrations");
let ibis_data = IbisData { db_pool, config };
let context = IbisContext { db_pool, config };
let data = FederationConfig::builder()
.domain(ibis_data.config.federation.domain.clone())
.url_verifier(Box::new(VerifyUrlData(ibis_data.config.clone())))
.app_data(ibis_data)
.domain(context.config.federation.domain.clone())
.url_verifier(Box::new(VerifyUrlData(context.config.clone())))
.app_data(context)
.http_fetch_limit(1000)
.debug(cfg!(debug_assertions))
.build()
@ -89,8 +89,8 @@ const MAIN_PAGE_DEFAULT_TEXT: &str = "Welcome to Ibis, the federated Wikipedia a
This main page can only be edited by the admin. Use it as an introduction for new users, \
and to list interesting articles.";
async fn setup(data: &Data<IbisData>) -> Result<(), Error> {
let domain = &data.config.federation.domain;
async fn setup(context: &Data<IbisContext>) -> Result<(), Error> {
let domain = &context.config.federation.domain;
let ap_id = ObjectId::parse(&format!("{}://{domain}", http_protocol_str()))?;
let inbox_url = format!("{}://{domain}/inbox", http_protocol_str());
let keypair = generate_keypair()?;
@ -106,13 +106,13 @@ async fn setup(data: &Data<IbisData>) -> Result<(), Error> {
last_refreshed_at: Utc::now(),
local: true,
};
let instance = DbInstance::create(&form, data)?;
let instance = DbInstance::create(&form, context)?;
let person = DbPerson::create_local(
data.config.setup.admin_username.clone(),
data.config.setup.admin_password.clone(),
context.config.setup.admin_username.clone(),
context.config.setup.admin_password.clone(),
true,
data,
context,
)?;
// Create the main page which is shown by default
@ -128,7 +128,7 @@ async fn setup(data: &Data<IbisData>) -> Result<(), Error> {
protected: true,
approved: true,
};
let article = DbArticle::create(form, data)?;
let article = DbArticle::create(form, context)?;
// also create an article so its included in most recently edited list
submit_article_update(
MAIN_PAGE_DEFAULT_TEXT.to_string(),
@ -136,12 +136,12 @@ async fn setup(data: &Data<IbisData>) -> Result<(), Error> {
EditVersion::default(),
&article,
person.person.id,
data,
context,
)
.await?;
// create ghost user
DbPerson::ghost(data)?;
DbPerson::ghost(context)?;
Ok(())
}

View file

@ -1,5 +1,5 @@
use crate::{
backend::{api::user::validate, database::IbisData},
backend::{api::user::validate, database::IbisContext},
common::{Auth, AUTH_COOKIE},
};
use axum::{body::Body, extract::State, http::Request, middleware::Next, response::Response};
@ -14,7 +14,7 @@ pub(super) const FEDERATION_ROUTES_PREFIX: &str = "/federation_routes";
/// If user is authenticated sets extensions `Auth` and `LocalUserView`.
#[debug_middleware]
pub(super) async fn auth_middleware(
State(data): State<Arc<IbisData>>,
State(context): State<Arc<IbisContext>>,
mut request: Request<Body>,
next: Next,
) -> Response {
@ -34,7 +34,7 @@ pub(super) async fn auth_middleware(
let auth: HashSet<_> = headers.chain(cookies).map(|s| s.to_string()).collect();
for auth in auth {
if let Ok(local_user) = validate(&auth, &data).await {
if let Ok(local_user) = validate(&auth, &context).await {
request.extensions_mut().insert(Auth(Some(auth)));
request.extensions_mut().insert(local_user);
}

View file

@ -1,4 +1,4 @@
use super::{database::IbisData, utils::error::MyResult};
use super::{database::IbisContext, utils::error::MyResult};
use crate::{
backend::{api::api_routes, federation::routes::federation_routes},
common::Auth,
@ -31,7 +31,7 @@ mod middleware;
mod nodeinfo;
pub(super) async fn start_server(
data: FederationConfig<IbisData>,
context: FederationConfig<IbisContext>,
override_hostname: Option<SocketAddr>,
notify_start: Option<oneshot::Sender<()>>,
) -> MyResult<()> {
@ -42,7 +42,7 @@ pub(super) async fn start_server(
}
let routes = generate_route_list(App);
let arc_data = Arc::new(data.deref().clone());
let arc_data = Arc::new(context.deref().clone());
let app = Router::new()
.leptos_routes_with_handler(routes, get(leptos_routes_handler))
.fallback(file_and_error_handler)
@ -50,7 +50,7 @@ pub(super) async fn start_server(
.nest(FEDERATION_ROUTES_PREFIX, federation_routes())
.nest("/api/v1", api_routes())
.nest("", nodeinfo::config())
.layer(FederationMiddleware::new(data))
.layer(FederationMiddleware::new(context))
.layer(CorsLayer::permissive())
.layer(CompressionLayer::new())
.route_layer(from_fn_with_state(arc_data, auth_middleware));

View file

@ -1,6 +1,6 @@
use crate::{
backend::{
database::{instance_stats::InstanceStats, IbisData},
database::{instance_stats::InstanceStats, IbisContext},
utils::error::MyResult,
},
common::utils::http_protocol_str,
@ -16,21 +16,21 @@ pub fn config() -> Router<()> {
.route("/.well-known/nodeinfo", get(node_info_well_known))
}
async fn node_info_well_known(data: Data<IbisData>) -> MyResult<Json<NodeInfoWellKnown>> {
async fn node_info_well_known(context: Data<IbisContext>) -> MyResult<Json<NodeInfoWellKnown>> {
Ok(Json(NodeInfoWellKnown {
links: vec![NodeInfoWellKnownLinks {
rel: Url::parse("http://nodeinfo.diaspora.software/ns/schema/2.1")?,
href: Url::parse(&format!(
"{}://{}/nodeinfo/2.1.json",
http_protocol_str(),
data.domain()
context.domain()
))?,
}],
}))
}
async fn node_info(data: Data<IbisData>) -> MyResult<Json<NodeInfo>> {
let stats = InstanceStats::read(&data)?;
async fn node_info(context: Data<IbisContext>) -> MyResult<Json<NodeInfo>> {
let stats = InstanceStats::read(&context)?;
Ok(Json(NodeInfo {
version: "2.1".to_string(),
software: NodeInfoSoftware {
@ -49,7 +49,7 @@ async fn node_info(data: Data<IbisData>) -> MyResult<Json<NodeInfo>> {
local_posts: stats.articles,
local_comments: stats.comments,
},
open_registrations: data.config.options.registration_open,
open_registrations: context.config.options.registration_open,
services: Default::default(),
metadata: vec![],
}))

View file

@ -1,5 +1,5 @@
use crate::{
backend::{database::IbisData, utils::error::MyResult},
backend::{database::IbisContext, utils::error::MyResult},
common::{
article::{DbEdit, EditVersion},
utils,
@ -19,8 +19,8 @@ pub mod error;
pub(super) mod scheduled_tasks;
pub(super) mod validate;
pub(super) fn generate_activity_id(data: &Data<IbisData>) -> Result<Url, ParseError> {
let domain = &data.config.federation.domain;
pub(super) fn generate_activity_id(context: &Data<IbisContext>) -> Result<Url, ParseError> {
let domain = &context.config.federation.domain;
let id: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(7)