Compare commits
9 commits
federated-
...
main
Author | SHA1 | Date | |
---|---|---|---|
f55ef1d7ef | |||
14bc9f0946 | |||
493598c1ba | |||
05b485b678 | |||
360d4ea8d1 | |||
|
74272ed754 | ||
|
4426c3176d | ||
|
7b0a09e84e | ||
|
ab947f1f08 |
52 changed files with 512 additions and 940 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -4,9 +4,9 @@ version = 3
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitystreams"
|
name = "activitystreams"
|
||||||
version = "0.7.0-alpha.11"
|
version = "0.7.0-alpha.10"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3a5da1d857ec9ca65ef8d0469cdd64e7b93b59d6cad26f1444bf84b62f3eadd4"
|
checksum = "fe7ceed015dfca322d3bcec3653909c77557e7e57df72e98cb8806e2c93cc919"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"mime",
|
"mime",
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.10.0-rc.5
|
0.10.0-rc.7
|
||||||
|
|
|
@ -22,7 +22,6 @@ import {
|
||||||
searchForUser,
|
searchForUser,
|
||||||
banPersonFromSite,
|
banPersonFromSite,
|
||||||
searchPostLocal,
|
searchPostLocal,
|
||||||
followCommunity,
|
|
||||||
banPersonFromCommunity,
|
banPersonFromCommunity,
|
||||||
} from './shared';
|
} from './shared';
|
||||||
import { PostView, CommunityView } from 'lemmy-js-client';
|
import { PostView, CommunityView } from 'lemmy-js-client';
|
||||||
|
@ -170,38 +169,35 @@ test('Sticky a post', async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Lock a post', async () => {
|
test('Lock a post', async () => {
|
||||||
await followCommunity(alpha, true, betaCommunity.community.id);
|
|
||||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||||
|
|
||||||
// Lock the post
|
// Lock the post
|
||||||
let searchBeta = await searchPost(beta, postRes.post_view.post);
|
let lockedPostRes = await lockPost(alpha, true, postRes.post_view.post);
|
||||||
let betaPost1 = searchBeta.posts[0];
|
|
||||||
let lockedPostRes = await lockPost(beta, true, betaPost1.post);
|
|
||||||
expect(lockedPostRes.post_view.post.locked).toBe(true);
|
expect(lockedPostRes.post_view.post.locked).toBe(true);
|
||||||
|
|
||||||
// Make sure that post is locked on alpha
|
// Make sure that post is locked on beta
|
||||||
let searchAlpha = await searchPostLocal(alpha, postRes.post_view.post);
|
let searchBeta = await searchPostLocal(beta, postRes.post_view.post);
|
||||||
let alphaPost1 = searchAlpha.posts[0];
|
let betaPost1 = searchBeta.posts[0];
|
||||||
expect(alphaPost1.post.locked).toBe(true);
|
expect(betaPost1.post.locked).toBe(true);
|
||||||
|
|
||||||
// Try to make a new comment there, on alpha
|
// Try to make a new comment there, on alpha
|
||||||
let comment: any = await createComment(alpha, alphaPost1.post.id);
|
let comment: any = await createComment(alpha, postRes.post_view.post.id);
|
||||||
expect(comment['error']).toBe('locked');
|
expect(comment['error']).toBe('locked');
|
||||||
|
|
||||||
// Unlock a post
|
// Unlock a post
|
||||||
let unlockedPost = await lockPost(beta, false, betaPost1.post);
|
let unlockedPost = await lockPost(alpha, false, postRes.post_view.post);
|
||||||
expect(unlockedPost.post_view.post.locked).toBe(false);
|
expect(unlockedPost.post_view.post.locked).toBe(false);
|
||||||
|
|
||||||
// Make sure that post is unlocked on alpha
|
// Make sure that post is unlocked on beta
|
||||||
let searchAlpha2 = await searchPostLocal(alpha, postRes.post_view.post);
|
let searchBeta2 = await searchPost(beta, postRes.post_view.post);
|
||||||
let alphaPost2 = searchAlpha2.posts[0];
|
let betaPost2 = searchBeta2.posts[0];
|
||||||
expect(alphaPost2.community.local).toBe(false);
|
expect(betaPost2.community.local).toBe(true);
|
||||||
expect(alphaPost2.creator.local).toBe(true);
|
expect(betaPost2.creator.local).toBe(false);
|
||||||
expect(alphaPost2.post.locked).toBe(false);
|
expect(betaPost2.post.locked).toBe(false);
|
||||||
|
|
||||||
// Try to create a new comment, on alpha
|
// Try to create a new comment, on beta
|
||||||
let commentAlpha = await createComment(alpha, alphaPost1.post.id);
|
let commentBeta = await createComment(beta, betaPost2.post.id);
|
||||||
expect(commentAlpha).toBeDefined();
|
expect(commentBeta).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Delete a post', async () => {
|
test('Delete a post', async () => {
|
||||||
|
|
|
@ -15,9 +15,7 @@ use lemmy_apub::{
|
||||||
generate_inbox_url,
|
generate_inbox_url,
|
||||||
generate_shared_inbox_url,
|
generate_shared_inbox_url,
|
||||||
ActorType,
|
ActorType,
|
||||||
CommunityType,
|
|
||||||
EndpointType,
|
EndpointType,
|
||||||
UserType,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_queries::{
|
use lemmy_db_queries::{
|
||||||
diesel_option_overwrite_to_url,
|
diesel_option_overwrite_to_url,
|
||||||
|
@ -36,7 +34,7 @@ use lemmy_db_queries::{
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
naive_now,
|
naive_now,
|
||||||
source::{comment::Comment, community::*, moderator::*, person::Person, post::Post, site::*},
|
source::{comment::Comment, community::*, moderator::*, post::Post, site::*},
|
||||||
PersonId,
|
PersonId,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::comment_view::CommentQueryBuilder;
|
use lemmy_db_views::comment_view::CommentQueryBuilder;
|
||||||
|
@ -709,16 +707,16 @@ impl Perform for AddModToCommunity {
|
||||||
let data: &AddModToCommunity = &self;
|
let data: &AddModToCommunity = &self;
|
||||||
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
|
||||||
|
|
||||||
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
|
community_id: data.community_id,
|
||||||
|
person_id: data.person_id,
|
||||||
|
};
|
||||||
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
|
|
||||||
// Verify that only mods or admins can add mod
|
// Verify that only mods or admins can add mod
|
||||||
is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
|
is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
|
||||||
|
|
||||||
// Update in local database
|
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
|
||||||
community_id: data.community_id,
|
|
||||||
person_id: data.person_id,
|
|
||||||
};
|
|
||||||
if data.added {
|
if data.added {
|
||||||
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
|
let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
|
||||||
if blocking(context.pool(), join).await?.is_err() {
|
if blocking(context.pool(), join).await?.is_err() {
|
||||||
|
@ -743,28 +741,6 @@ impl Perform for AddModToCommunity {
|
||||||
})
|
})
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
// Send to federated instances
|
|
||||||
let updated_mod_id = data.person_id;
|
|
||||||
let updated_mod = blocking(context.pool(), move |conn| {
|
|
||||||
Person::read(conn, updated_mod_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
let community = blocking(context.pool(), move |conn| {
|
|
||||||
Community::read(conn, community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
if data.added {
|
|
||||||
community
|
|
||||||
.send_add_mod(&local_user_view.person, updated_mod, context)
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
community
|
|
||||||
.send_remove_mod(&local_user_view.person, updated_mod, context)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: in case a remote mod is added, this returns the old moderators list, it will only get
|
|
||||||
// updated once we receive an activity from the community (like `Announce/Add/Moderator`)
|
|
||||||
let community_id = data.community_id;
|
let community_id = data.community_id;
|
||||||
let moderators = blocking(context.pool(), move |conn| {
|
let moderators = blocking(context.pool(), move |conn| {
|
||||||
CommunityModeratorView::for_community(conn, community_id)
|
CommunityModeratorView::for_community(conn, community_id)
|
||||||
|
@ -772,18 +748,18 @@ impl Perform for AddModToCommunity {
|
||||||
.await??;
|
.await??;
|
||||||
|
|
||||||
let res = AddModToCommunityResponse { moderators };
|
let res = AddModToCommunityResponse { moderators };
|
||||||
|
|
||||||
context.chat_server().do_send(SendCommunityRoomMessage {
|
context.chat_server().do_send(SendCommunityRoomMessage {
|
||||||
op: UserOperation::AddModToCommunity,
|
op: UserOperation::AddModToCommunity,
|
||||||
response: res.clone(),
|
response: res.clone(),
|
||||||
community_id,
|
community_id,
|
||||||
websocket_id,
|
websocket_id,
|
||||||
});
|
});
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we dont do anything for federation here, it should be updated the next time the community
|
|
||||||
// gets fetched. i hope we can get rid of the community creator role soon.
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[async_trait::async_trait(?Send)]
|
||||||
impl Perform for TransferCommunity {
|
impl Perform for TransferCommunity {
|
||||||
type Response = GetCommunityResponse;
|
type Response = GetCommunityResponse;
|
||||||
|
|
|
@ -41,7 +41,7 @@ use lemmy_utils::{
|
||||||
};
|
};
|
||||||
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
|
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::process::Command;
|
use std::{env, process::Command};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
|
@ -100,16 +100,32 @@ pub(crate) async fn get_local_user_view_from_jwt(
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
Err(_e) => return Err(ApiError::err("not_logged_in").into()),
|
Err(_e) => return Err(ApiError::err("not_logged_in").into()),
|
||||||
};
|
};
|
||||||
let local_user_id = LocalUserId(claims.local_user_id);
|
let local_user_id = LocalUserId(claims.sub);
|
||||||
let local_user_view =
|
let local_user_view =
|
||||||
blocking(pool, move |conn| LocalUserView::read(conn, local_user_id)).await??;
|
blocking(pool, move |conn| LocalUserView::read(conn, local_user_id)).await??;
|
||||||
// Check for a site ban
|
// Check for a site ban
|
||||||
if local_user_view.person.banned {
|
if local_user_view.person.banned {
|
||||||
return Err(ApiError::err("site_ban").into());
|
return Err(ApiError::err("site_ban").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
|
||||||
|
|
||||||
Ok(local_user_view)
|
Ok(local_user_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if user's token was issued before user's password reset.
|
||||||
|
pub(crate) fn check_validator_time(
|
||||||
|
validator_time: &chrono::NaiveDateTime,
|
||||||
|
claims: &Claims,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let user_validation_time = validator_time.timestamp();
|
||||||
|
if user_validation_time > claims.iat {
|
||||||
|
Err(ApiError::err("not_logged_in").into())
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_local_user_view_from_jwt_opt(
|
pub(crate) async fn get_local_user_view_from_jwt_opt(
|
||||||
jwt: &Option<String>,
|
jwt: &Option<String>,
|
||||||
pool: &DbPool,
|
pool: &DbPool,
|
||||||
|
@ -128,7 +144,7 @@ pub(crate) async fn get_local_user_settings_view_from_jwt(
|
||||||
Ok(claims) => claims.claims,
|
Ok(claims) => claims.claims,
|
||||||
Err(_e) => return Err(ApiError::err("not_logged_in").into()),
|
Err(_e) => return Err(ApiError::err("not_logged_in").into()),
|
||||||
};
|
};
|
||||||
let local_user_id = LocalUserId(claims.local_user_id);
|
let local_user_id = LocalUserId(claims.sub);
|
||||||
let local_user_view = blocking(pool, move |conn| {
|
let local_user_view = blocking(pool, move |conn| {
|
||||||
LocalUserSettingsView::read(conn, local_user_id)
|
LocalUserSettingsView::read(conn, local_user_id)
|
||||||
})
|
})
|
||||||
|
@ -137,6 +153,9 @@ pub(crate) async fn get_local_user_settings_view_from_jwt(
|
||||||
if local_user_view.person.banned {
|
if local_user_view.person.banned {
|
||||||
return Err(ApiError::err("site_ban").into());
|
return Err(ApiError::err("site_ban").into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_validator_time(&local_user_view.local_user.validator_time, &claims)?;
|
||||||
|
|
||||||
Ok(local_user_view)
|
Ok(local_user_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -459,7 +478,11 @@ pub(crate) fn captcha_espeak_wav_base64(captcha: &str) -> Result<String, LemmyEr
|
||||||
pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
|
pub(crate) fn espeak_wav_base64(text: &str) -> Result<String, LemmyError> {
|
||||||
// Make a temp file path
|
// Make a temp file path
|
||||||
let uuid = uuid::Uuid::new_v4().to_string();
|
let uuid = uuid::Uuid::new_v4().to_string();
|
||||||
let file_path = format!("/tmp/lemmy_espeak_{}.wav", &uuid);
|
let file_path = format!(
|
||||||
|
"{}/lemmy_espeak_{}.wav",
|
||||||
|
env::temp_dir().to_string_lossy(),
|
||||||
|
&uuid
|
||||||
|
);
|
||||||
|
|
||||||
// Write the wav file
|
// Write the wav file
|
||||||
Command::new("espeak")
|
Command::new("espeak")
|
||||||
|
@ -491,7 +514,70 @@ pub(crate) fn password_length_check(pass: &str) -> Result<(), LemmyError> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::captcha_espeak_wav_base64;
|
use crate::{captcha_espeak_wav_base64, check_validator_time};
|
||||||
|
use lemmy_db_queries::{establish_unpooled_connection, source::local_user::LocalUser_, Crud};
|
||||||
|
use lemmy_db_schema::source::{
|
||||||
|
local_user::{LocalUser, LocalUserForm},
|
||||||
|
person::{Person, PersonForm},
|
||||||
|
};
|
||||||
|
use lemmy_utils::claims::Claims;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_should_not_validate_user_token_after_password_change() {
|
||||||
|
let conn = establish_unpooled_connection();
|
||||||
|
|
||||||
|
let new_person = PersonForm {
|
||||||
|
name: "Gerry9812".into(),
|
||||||
|
preferred_username: None,
|
||||||
|
avatar: None,
|
||||||
|
banner: None,
|
||||||
|
banned: None,
|
||||||
|
deleted: None,
|
||||||
|
published: None,
|
||||||
|
updated: None,
|
||||||
|
actor_id: None,
|
||||||
|
bio: None,
|
||||||
|
local: None,
|
||||||
|
private_key: None,
|
||||||
|
public_key: None,
|
||||||
|
last_refreshed_at: None,
|
||||||
|
inbox_url: None,
|
||||||
|
shared_inbox_url: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let inserted_person = Person::create(&conn, &new_person).unwrap();
|
||||||
|
|
||||||
|
let local_user_form = LocalUserForm {
|
||||||
|
person_id: inserted_person.id,
|
||||||
|
email: None,
|
||||||
|
matrix_user_id: None,
|
||||||
|
password_encrypted: "123456".to_string(),
|
||||||
|
admin: None,
|
||||||
|
show_nsfw: None,
|
||||||
|
theme: None,
|
||||||
|
default_sort_type: None,
|
||||||
|
default_listing_type: None,
|
||||||
|
lang: None,
|
||||||
|
show_avatars: None,
|
||||||
|
send_notifications_to_email: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let inserted_local_user = LocalUser::create(&conn, &local_user_form).unwrap();
|
||||||
|
|
||||||
|
let jwt = Claims::jwt(inserted_local_user.id.0).unwrap();
|
||||||
|
let claims = Claims::decode(&jwt).unwrap().claims;
|
||||||
|
let check = check_validator_time(&inserted_local_user.validator_time, &claims);
|
||||||
|
assert!(check.is_ok());
|
||||||
|
|
||||||
|
// The check should fail, since the validator time is now newer than the jwt issue time
|
||||||
|
let updated_local_user =
|
||||||
|
LocalUser::update_password(&conn, inserted_local_user.id, &"password111").unwrap();
|
||||||
|
let check_after = check_validator_time(&updated_local_user.validator_time, &claims);
|
||||||
|
assert!(check_after.is_err());
|
||||||
|
|
||||||
|
let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
|
||||||
|
assert_eq!(1, num_deleted);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_espeak() {
|
fn test_espeak() {
|
||||||
|
|
|
@ -130,7 +130,7 @@ impl Perform for Login {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(local_user_view.local_user.id.0, Settings::get().hostname())?,
|
jwt: Claims::jwt(local_user_view.local_user.id.0)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -336,7 +336,7 @@ impl Perform for Register {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(inserted_local_user.id.0, Settings::get().hostname())?,
|
jwt: Claims::jwt(inserted_local_user.id.0)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -526,7 +526,7 @@ impl Perform for SaveUserSettings {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(updated_local_user.id.0, Settings::get().hostname())?,
|
jwt: Claims::jwt(updated_local_user.id.0)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1078,7 +1078,7 @@ impl Perform for PasswordChange {
|
||||||
|
|
||||||
// Return the jwt
|
// Return the jwt
|
||||||
Ok(LoginResponse {
|
Ok(LoginResponse {
|
||||||
jwt: Claims::jwt(updated_local_user.id.0, Settings::get().hostname())?,
|
jwt: Claims::jwt(updated_local_user.id.0)?,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,7 @@ lemmy_db_views_actor = { path = "../db_views_actor" }
|
||||||
lemmy_api_structs = { path = "../api_structs" }
|
lemmy_api_structs = { path = "../api_structs" }
|
||||||
lemmy_websocket = { path = "../websocket" }
|
lemmy_websocket = { path = "../websocket" }
|
||||||
diesel = "1.4.5"
|
diesel = "1.4.5"
|
||||||
activitystreams = "0.7.0-alpha.11"
|
activitystreams = "0.7.0-alpha.10"
|
||||||
activitystreams-ext = "0.1.0-alpha.2"
|
activitystreams-ext = "0.1.0-alpha.2"
|
||||||
bcrypt = "0.9.0"
|
bcrypt = "0.9.0"
|
||||||
chrono = { version = "0.4.19", features = ["serde"] }
|
chrono = { version = "0.4.19", features = ["serde"] }
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
pub(crate) mod receive;
|
pub(crate) mod receive;
|
||||||
pub mod send;
|
pub(crate) mod send;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{activities::receive::get_actor_as_person, objects::FromApub, ActorType, NoteExt};
|
use crate::{activities::receive::get_actor_as_person, objects::FromApub, ActorType, NoteExt};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{ActorAndObjectRefExt, Create, Dislike, Like, Update},
|
activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update},
|
||||||
base::ExtendsExt,
|
base::ExtendsExt,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
|
@ -23,8 +23,7 @@ pub(crate) async fn receive_create_comment(
|
||||||
let note = NoteExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
|
let note = NoteExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
|
|
||||||
let comment =
|
let comment = Comment::from_apub(¬e, context, person.actor_id(), request_counter).await?;
|
||||||
Comment::from_apub(¬e, context, person.actor_id(), request_counter, false).await?;
|
|
||||||
|
|
||||||
let post_id = comment.post_id;
|
let post_id = comment.post_id;
|
||||||
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
@ -74,8 +73,7 @@ pub(crate) async fn receive_update_comment(
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
let person = get_actor_as_person(&update, context, request_counter).await?;
|
let person = get_actor_as_person(&update, context, request_counter).await?;
|
||||||
|
|
||||||
let comment =
|
let comment = Comment::from_apub(¬e, context, person.actor_id(), request_counter).await?;
|
||||||
Comment::from_apub(¬e, context, person.actor_id(), request_counter, false).await?;
|
|
||||||
|
|
||||||
let comment_id = comment.id;
|
let comment_id = comment.id;
|
||||||
let post_id = comment.post_id;
|
let post_id = comment.post_id;
|
||||||
|
@ -230,6 +228,7 @@ pub(crate) async fn receive_delete_comment(
|
||||||
|
|
||||||
pub(crate) async fn receive_remove_comment(
|
pub(crate) async fn receive_remove_comment(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
_remove: Remove,
|
||||||
comment: Comment,
|
comment: Comment,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let removed_comment = blocking(context.pool(), move |conn| {
|
let removed_comment = blocking(context.pool(), move |conn| {
|
||||||
|
|
|
@ -1,9 +1,16 @@
|
||||||
|
use crate::{activities::receive::verify_activity_domains_valid, inbox::is_addressed_to_public};
|
||||||
|
use activitystreams::{
|
||||||
|
activity::{ActorAndObjectRefExt, Delete, Remove, Undo},
|
||||||
|
base::{AnyBase, ExtendsExt},
|
||||||
|
};
|
||||||
|
use anyhow::Context;
|
||||||
use lemmy_api_structs::{blocking, community::CommunityResponse};
|
use lemmy_api_structs::{blocking, community::CommunityResponse};
|
||||||
use lemmy_db_queries::source::community::Community_;
|
use lemmy_db_queries::{source::community::Community_, ApubObject};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_views_actor::community_view::CommunityView;
|
use lemmy_db_views_actor::community_view::CommunityView;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendCommunityRoomMessage, LemmyContext, UserOperation};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
pub(crate) async fn receive_delete_community(
|
pub(crate) async fn receive_delete_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -35,8 +42,23 @@ pub(crate) async fn receive_delete_community(
|
||||||
|
|
||||||
pub(crate) async fn receive_remove_community(
|
pub(crate) async fn receive_remove_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
community: Community,
|
activity: AnyBase,
|
||||||
|
expected_domain: &Url,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
let remove = Remove::from_any_base(activity)?.context(location_info!())?;
|
||||||
|
verify_activity_domains_valid(&remove, expected_domain, true)?;
|
||||||
|
is_addressed_to_public(&remove)?;
|
||||||
|
|
||||||
|
let community_uri = remove
|
||||||
|
.object()
|
||||||
|
.to_owned()
|
||||||
|
.single_xsd_any_uri()
|
||||||
|
.context(location_info!())?;
|
||||||
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
Community::read_from_apub_id(conn, &community_uri.into())
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
let removed_community = blocking(context.pool(), move |conn| {
|
let removed_community = blocking(context.pool(), move |conn| {
|
||||||
Community::update_removed(conn, community.id, true)
|
Community::update_removed(conn, community.id, true)
|
||||||
})
|
})
|
||||||
|
@ -63,8 +85,16 @@ pub(crate) async fn receive_remove_community(
|
||||||
|
|
||||||
pub(crate) async fn receive_undo_delete_community(
|
pub(crate) async fn receive_undo_delete_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
undo: Undo,
|
||||||
community: Community,
|
community: Community,
|
||||||
|
expected_domain: &Url,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
is_addressed_to_public(&undo)?;
|
||||||
|
let inner = undo.object().to_owned().one().context(location_info!())?;
|
||||||
|
let delete = Delete::from_any_base(inner)?.context(location_info!())?;
|
||||||
|
verify_activity_domains_valid(&delete, expected_domain, true)?;
|
||||||
|
is_addressed_to_public(&delete)?;
|
||||||
|
|
||||||
let deleted_community = blocking(context.pool(), move |conn| {
|
let deleted_community = blocking(context.pool(), move |conn| {
|
||||||
Community::update_deleted(conn, community.id, false)
|
Community::update_deleted(conn, community.id, false)
|
||||||
})
|
})
|
||||||
|
@ -91,8 +121,26 @@ pub(crate) async fn receive_undo_delete_community(
|
||||||
|
|
||||||
pub(crate) async fn receive_undo_remove_community(
|
pub(crate) async fn receive_undo_remove_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
community: Community,
|
undo: Undo,
|
||||||
|
expected_domain: &Url,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
is_addressed_to_public(&undo)?;
|
||||||
|
|
||||||
|
let inner = undo.object().to_owned().one().context(location_info!())?;
|
||||||
|
let remove = Remove::from_any_base(inner)?.context(location_info!())?;
|
||||||
|
verify_activity_domains_valid(&remove, &expected_domain, true)?;
|
||||||
|
is_addressed_to_public(&remove)?;
|
||||||
|
|
||||||
|
let community_uri = remove
|
||||||
|
.object()
|
||||||
|
.to_owned()
|
||||||
|
.single_xsd_any_uri()
|
||||||
|
.context(location_info!())?;
|
||||||
|
let community = blocking(context.pool(), move |conn| {
|
||||||
|
Community::read_from_apub_id(conn, &community_uri.into())
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
|
||||||
let removed_community = blocking(context.pool(), move |conn| {
|
let removed_community = blocking(context.pool(), move |conn| {
|
||||||
Community::update_removed(conn, community.id, false)
|
Community::update_removed(conn, community.id, false)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,24 +1,12 @@
|
||||||
use crate::{
|
use crate::{activities::receive::get_actor_as_person, objects::FromApub, ActorType, PageExt};
|
||||||
activities::receive::get_actor_as_person,
|
|
||||||
inbox::receive_for_community::verify_mod_activity,
|
|
||||||
objects::FromApub,
|
|
||||||
ActorType,
|
|
||||||
PageExt,
|
|
||||||
};
|
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Announce, Create, Dislike, Like, Update},
|
activity::{Create, Dislike, Like, Remove, Update},
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use lemmy_api_structs::{blocking, post::PostResponse};
|
use lemmy_api_structs::{blocking, post::PostResponse};
|
||||||
use lemmy_db_queries::{source::post::Post_, ApubObject, Crud, Likeable};
|
use lemmy_db_queries::{source::post::Post_, Likeable};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::source::post::{Post, PostLike, PostLikeForm};
|
||||||
source::{
|
|
||||||
community::Community,
|
|
||||||
post::{Post, PostLike, PostLikeForm},
|
|
||||||
},
|
|
||||||
DbUrl,
|
|
||||||
};
|
|
||||||
use lemmy_db_views::post_view::PostView;
|
use lemmy_db_views::post_view::PostView;
|
||||||
use lemmy_utils::{location_info, LemmyError};
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
|
use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation};
|
||||||
|
@ -32,7 +20,7 @@ pub(crate) async fn receive_create_post(
|
||||||
let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
|
let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
|
|
||||||
let post = Post::from_apub(&page, context, person.actor_id(), request_counter, false).await?;
|
let post = Post::from_apub(&page, context, person.actor_id(), request_counter).await?;
|
||||||
|
|
||||||
// Refetch the view
|
// Refetch the view
|
||||||
let post_id = post.id;
|
let post_id = post.id;
|
||||||
|
@ -54,7 +42,6 @@ pub(crate) async fn receive_create_post(
|
||||||
|
|
||||||
pub(crate) async fn receive_update_post(
|
pub(crate) async fn receive_update_post(
|
||||||
update: Update,
|
update: Update,
|
||||||
announce: Option<Announce>,
|
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
@ -62,40 +49,7 @@ pub(crate) async fn receive_update_post(
|
||||||
let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
|
let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
|
|
||||||
let post_id: DbUrl = page
|
let post = Post::from_apub(&page, context, person.actor_id(), request_counter).await?;
|
||||||
.id_unchecked()
|
|
||||||
.context(location_info!())?
|
|
||||||
.to_owned()
|
|
||||||
.into();
|
|
||||||
let old_post = blocking(context.pool(), move |conn| {
|
|
||||||
Post::read_from_apub_id(conn, &post_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
// If sticked or locked state was changed, make sure the actor is a mod
|
|
||||||
let stickied = page.ext_one.stickied.context(location_info!())?;
|
|
||||||
let locked = !page.ext_one.comments_enabled.context(location_info!())?;
|
|
||||||
let mut mod_action_allowed = false;
|
|
||||||
if stickied != old_post.stickied || locked != old_post.locked {
|
|
||||||
let community = blocking(context.pool(), move |conn| {
|
|
||||||
Community::read(conn, old_post.community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
// Only check mod status if the community is local, otherwise we trust that it was sent correctly.
|
|
||||||
if community.local {
|
|
||||||
verify_mod_activity(&update, announce, &community, context).await?;
|
|
||||||
}
|
|
||||||
mod_action_allowed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let post = Post::from_apub(
|
|
||||||
&page,
|
|
||||||
context,
|
|
||||||
person.actor_id(),
|
|
||||||
request_counter,
|
|
||||||
mod_action_allowed,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_id = post.id;
|
let post_id = post.id;
|
||||||
// Refetch the view
|
// Refetch the view
|
||||||
|
@ -219,6 +173,7 @@ pub(crate) async fn receive_delete_post(
|
||||||
|
|
||||||
pub(crate) async fn receive_remove_post(
|
pub(crate) async fn receive_remove_post(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
_remove: Remove,
|
||||||
post: Post,
|
post: Post,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let removed_post = blocking(context.pool(), move |conn| {
|
let removed_post = blocking(context.pool(), move |conn| {
|
||||||
|
|
|
@ -39,7 +39,7 @@ pub(crate) async fn receive_create_private_message(
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
|
|
||||||
let private_message =
|
let private_message =
|
||||||
PrivateMessage::from_apub(¬e, context, expected_domain, request_counter, false).await?;
|
PrivateMessage::from_apub(¬e, context, expected_domain, request_counter).await?;
|
||||||
|
|
||||||
let message = blocking(&context.pool(), move |conn| {
|
let message = blocking(&context.pool(), move |conn| {
|
||||||
PrivateMessageView::read(conn, private_message.id)
|
PrivateMessageView::read(conn, private_message.id)
|
||||||
|
@ -85,7 +85,7 @@ pub(crate) async fn receive_update_private_message(
|
||||||
let note = NoteExt::from_any_base(object)?.context(location_info!())?;
|
let note = NoteExt::from_any_base(object)?.context(location_info!())?;
|
||||||
|
|
||||||
let private_message =
|
let private_message =
|
||||||
PrivateMessage::from_apub(¬e, context, expected_domain, request_counter, false).await?;
|
PrivateMessage::from_apub(¬e, context, expected_domain, request_counter).await?;
|
||||||
|
|
||||||
let private_message_id = private_message.id;
|
let private_message_id = private_message.id;
|
||||||
let message = blocking(&context.pool(), move |conn| {
|
let message = blocking(&context.pool(), move |conn| {
|
||||||
|
|
|
@ -1,24 +1,20 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
activities::send::generate_activity_id,
|
activities::send::generate_activity_id,
|
||||||
activity_queue::{send_activity_single_dest, send_to_community, send_to_community_followers},
|
activity_queue::{send_activity_single_dest, send_to_community_followers},
|
||||||
check_is_apub_id_valid,
|
check_is_apub_id_valid,
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
fetcher::person::get_or_fetch_and_upsert_person,
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
generate_moderators_url,
|
|
||||||
insert_activity,
|
insert_activity,
|
||||||
ActorType,
|
ActorType,
|
||||||
CommunityType,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{
|
activity::{
|
||||||
kind::{AcceptType, AddType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType},
|
kind::{AcceptType, AnnounceType, DeleteType, LikeType, RemoveType, UndoType},
|
||||||
Accept,
|
Accept,
|
||||||
ActorAndObjectRefExt,
|
ActorAndObjectRefExt,
|
||||||
Add,
|
|
||||||
Announce,
|
Announce,
|
||||||
Delete,
|
Delete,
|
||||||
Follow,
|
Follow,
|
||||||
OptTargetRefExt,
|
|
||||||
Remove,
|
Remove,
|
||||||
Undo,
|
Undo,
|
||||||
},
|
},
|
||||||
|
@ -30,7 +26,7 @@ use anyhow::Context;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use lemmy_api_structs::blocking;
|
use lemmy_api_structs::blocking;
|
||||||
use lemmy_db_queries::DbPool;
|
use lemmy_db_queries::DbPool;
|
||||||
use lemmy_db_schema::source::{community::Community, person::Person};
|
use lemmy_db_schema::source::community::Community;
|
||||||
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
||||||
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
|
@ -58,10 +54,23 @@ impl ActorType for Community {
|
||||||
.unwrap_or_else(|| self.inbox_url.to_owned())
|
.unwrap_or_else(|| self.inbox_url.to_owned())
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
async fn send_follow(
|
||||||
impl CommunityType for Community {
|
&self,
|
||||||
|
_follow_actor_id: &Url,
|
||||||
|
_context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_unfollow(
|
||||||
|
&self,
|
||||||
|
_follow_actor_id: &Url,
|
||||||
|
_context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
/// As a local community, accept the follow request from a remote person.
|
/// As a local community, accept the follow request from a remote person.
|
||||||
async fn send_accept_follow(
|
async fn send_accept_follow(
|
||||||
&self,
|
&self,
|
||||||
|
@ -202,46 +211,4 @@ impl CommunityType for Community {
|
||||||
|
|
||||||
Ok(inboxes)
|
Ok(inboxes)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_add_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
added_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let mut add = Add::new(
|
|
||||||
actor.actor_id.clone().into_inner(),
|
|
||||||
added_mod.actor_id.into_inner(),
|
|
||||||
);
|
|
||||||
add
|
|
||||||
.set_many_contexts(lemmy_context()?)
|
|
||||||
.set_id(generate_activity_id(AddType::Add)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()])
|
|
||||||
.set_target(generate_moderators_url(&self.actor_id)?.into_inner());
|
|
||||||
|
|
||||||
send_to_community(add, actor, self, context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_remove_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
removed_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let mut remove = Remove::new(
|
|
||||||
actor.actor_id.clone().into_inner(),
|
|
||||||
removed_mod.actor_id.into_inner(),
|
|
||||||
);
|
|
||||||
remove
|
|
||||||
.set_many_contexts(lemmy_context()?)
|
|
||||||
.set_id(generate_activity_id(RemoveType::Remove)?)
|
|
||||||
.set_to(public())
|
|
||||||
.set_many_ccs(vec![self.actor_id()])
|
|
||||||
.set_target(generate_moderators_url(&self.actor_id)?.into_inner());
|
|
||||||
|
|
||||||
send_to_community(remove, &actor, self, context).await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
||||||
activity_queue::send_activity_single_dest,
|
activity_queue::send_activity_single_dest,
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
ActorType,
|
ActorType,
|
||||||
UserType,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{
|
activity::{
|
||||||
|
@ -11,11 +10,11 @@ use activitystreams::{
|
||||||
Follow,
|
Follow,
|
||||||
Undo,
|
Undo,
|
||||||
},
|
},
|
||||||
base::{BaseExt, ExtendsExt},
|
base::{AnyBase, BaseExt, ExtendsExt},
|
||||||
object::ObjectExt,
|
object::ObjectExt,
|
||||||
};
|
};
|
||||||
use lemmy_api_structs::blocking;
|
use lemmy_api_structs::blocking;
|
||||||
use lemmy_db_queries::{ApubObject, Followable};
|
use lemmy_db_queries::{ApubObject, DbPool, Followable};
|
||||||
use lemmy_db_schema::source::{
|
use lemmy_db_schema::source::{
|
||||||
community::{Community, CommunityFollower, CommunityFollowerForm},
|
community::{Community, CommunityFollower, CommunityFollowerForm},
|
||||||
person::Person,
|
person::Person,
|
||||||
|
@ -48,10 +47,7 @@ impl ActorType for Person {
|
||||||
.unwrap_or_else(|| self.inbox_url.to_owned())
|
.unwrap_or_else(|| self.inbox_url.to_owned())
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
impl UserType for Person {
|
|
||||||
/// As a given local person, send out a follow request to a remote community.
|
/// As a given local person, send out a follow request to a remote community.
|
||||||
async fn send_follow(
|
async fn send_follow(
|
||||||
&self,
|
&self,
|
||||||
|
@ -114,4 +110,40 @@ impl UserType for Person {
|
||||||
send_activity_single_dest(undo, self, community.inbox_url.into(), context).await?;
|
send_activity_single_dest(undo, self, community.inbox_url.into(), context).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn send_accept_follow(
|
||||||
|
&self,
|
||||||
|
_follow: Follow,
|
||||||
|
_context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_undo_delete(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_remove(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_undo_remove(&self, _context: &LemmyContext) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn send_announce(
|
||||||
|
&self,
|
||||||
|
_activity: AnyBase,
|
||||||
|
_context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<Url>, LemmyError> {
|
||||||
|
unimplemented!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
||||||
extensions::signatures::sign_and_send,
|
extensions::signatures::sign_and_send,
|
||||||
insert_activity,
|
insert_activity,
|
||||||
ActorType,
|
ActorType,
|
||||||
CommunityType,
|
|
||||||
APUB_JSON_CONTENT_TYPE,
|
APUB_JSON_CONTENT_TYPE,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
|
|
|
@ -11,8 +11,7 @@ pub(crate) fn lemmy_context() -> Result<Vec<AnyBase>, LemmyError> {
|
||||||
"comments_enabled": {
|
"comments_enabled": {
|
||||||
"kind": "sc:Boolean",
|
"kind": "sc:Boolean",
|
||||||
"id": "pt:commentsEnabled"
|
"id": "pt:commentsEnabled"
|
||||||
},
|
}
|
||||||
"moderators": "as:moderators"
|
|
||||||
}))?;
|
}))?;
|
||||||
Ok(vec![AnyBase::from(context()), context_ext])
|
Ok(vec![AnyBase::from(context()), context_ext])
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ use activitystreams::unparsed::UnparsedMutExt;
|
||||||
use activitystreams_ext::UnparsedExtension;
|
use activitystreams_ext::UnparsedExtension;
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
/// Activitystreams extension to allow (de)serializing additional Community field
|
/// Activitystreams extension to allow (de)serializing additional Community field
|
||||||
/// `sensitive` (called 'nsfw' in Lemmy).
|
/// `sensitive` (called 'nsfw' in Lemmy).
|
||||||
|
@ -10,14 +9,12 @@ use url::Url;
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct GroupExtension {
|
pub struct GroupExtension {
|
||||||
pub sensitive: Option<bool>,
|
pub sensitive: Option<bool>,
|
||||||
pub moderators: Option<Url>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GroupExtension {
|
impl GroupExtension {
|
||||||
pub fn new(sensitive: bool, moderators_url: Url) -> Result<GroupExtension, LemmyError> {
|
pub fn new(sensitive: bool) -> Result<GroupExtension, LemmyError> {
|
||||||
Ok(GroupExtension {
|
Ok(GroupExtension {
|
||||||
sensitive: Some(sensitive),
|
sensitive: Some(sensitive),
|
||||||
moderators: Some(moderators_url),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,13 +28,11 @@ where
|
||||||
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
|
fn try_from_unparsed(unparsed_mut: &mut U) -> Result<Self, Self::Error> {
|
||||||
Ok(GroupExtension {
|
Ok(GroupExtension {
|
||||||
sensitive: unparsed_mut.remove("sensitive")?,
|
sensitive: unparsed_mut.remove("sensitive")?,
|
||||||
moderators: unparsed_mut.remove("moderators")?,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
|
fn try_into_unparsed(self, unparsed_mut: &mut U) -> Result<(), Self::Error> {
|
||||||
unparsed_mut.insert("sensitive", self.sensitive)?;
|
unparsed_mut.insert("sensitive", self.sensitive)?;
|
||||||
unparsed_mut.insert("moderators", self.moderators)?;
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
fetcher::{fetch::fetch_remote_object, is_deleted, should_refetch_actor},
|
fetcher::{
|
||||||
|
fetch::fetch_remote_object,
|
||||||
|
get_or_fetch_and_upsert_person,
|
||||||
|
is_deleted,
|
||||||
|
should_refetch_actor,
|
||||||
|
},
|
||||||
inbox::person_inbox::receive_announce,
|
inbox::person_inbox::receive_announce,
|
||||||
objects::FromApub,
|
objects::FromApub,
|
||||||
GroupExt,
|
GroupExt,
|
||||||
|
@ -7,12 +12,13 @@ use crate::{
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
actor::ApActorExt,
|
actor::ApActorExt,
|
||||||
collection::{CollectionExt, OrderedCollection},
|
collection::{CollectionExt, OrderedCollection},
|
||||||
|
object::ObjectExt,
|
||||||
};
|
};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use diesel::result::Error::NotFound;
|
use diesel::result::Error::NotFound;
|
||||||
use lemmy_api_structs::blocking;
|
use lemmy_api_structs::blocking;
|
||||||
use lemmy_db_queries::{source::community::Community_, ApubObject};
|
use lemmy_db_queries::{source::community::Community_, ApubObject, Joinable};
|
||||||
use lemmy_db_schema::source::community::Community;
|
use lemmy_db_schema::source::community::{Community, CommunityModerator, CommunityModeratorForm};
|
||||||
use lemmy_utils::{location_info, LemmyError};
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
@ -71,14 +77,42 @@ async fn fetch_remote_community(
|
||||||
}
|
}
|
||||||
|
|
||||||
let group = group?;
|
let group = group?;
|
||||||
let community = Community::from_apub(
|
let community =
|
||||||
&group,
|
Community::from_apub(&group, context, apub_id.to_owned(), recursion_counter).await?;
|
||||||
context,
|
|
||||||
apub_id.to_owned(),
|
// Also add the community moderators too
|
||||||
recursion_counter,
|
let attributed_to = group.inner.attributed_to().context(location_info!())?;
|
||||||
false,
|
let creator_and_moderator_uris: Vec<&Url> = attributed_to
|
||||||
)
|
.as_many()
|
||||||
.await?;
|
.context(location_info!())?
|
||||||
|
.iter()
|
||||||
|
.map(|a| a.as_xsd_any_uri().context(""))
|
||||||
|
.collect::<Result<Vec<&Url>, anyhow::Error>>()?;
|
||||||
|
|
||||||
|
let mut creator_and_moderators = Vec::new();
|
||||||
|
|
||||||
|
for uri in creator_and_moderator_uris {
|
||||||
|
let c_or_m = get_or_fetch_and_upsert_person(uri, context, recursion_counter).await?;
|
||||||
|
|
||||||
|
creator_and_moderators.push(c_or_m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: need to make this work to update mods of existing communities
|
||||||
|
if old_community.is_none() {
|
||||||
|
let community_id = community.id;
|
||||||
|
blocking(context.pool(), move |conn| {
|
||||||
|
for mod_ in creator_and_moderators {
|
||||||
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
|
community_id,
|
||||||
|
person_id: mod_.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommunityModerator::join(conn, &community_moderator_form)?;
|
||||||
|
}
|
||||||
|
Ok(()) as Result<(), LemmyError>
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
}
|
||||||
|
|
||||||
// only fetch outbox for new communities, otherwise this can create an infinite loop
|
// only fetch outbox for new communities, otherwise this can create an infinite loop
|
||||||
if old_community.is_none() {
|
if old_community.is_none() {
|
||||||
|
@ -109,27 +143,3 @@ async fn fetch_community_outbox(
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn fetch_community_mods(
|
|
||||||
context: &LemmyContext,
|
|
||||||
group: &GroupExt,
|
|
||||||
recursion_counter: &mut i32,
|
|
||||||
) -> Result<Vec<Url>, LemmyError> {
|
|
||||||
if let Some(mods_url) = &group.ext_one.moderators {
|
|
||||||
let mods =
|
|
||||||
fetch_remote_object::<OrderedCollection>(context.client(), mods_url, recursion_counter)
|
|
||||||
.await?;
|
|
||||||
let mods = mods
|
|
||||||
.items()
|
|
||||||
.map(|i| i.as_many())
|
|
||||||
.flatten()
|
|
||||||
.context(location_info!())?
|
|
||||||
.iter()
|
|
||||||
.filter_map(|i| i.as_xsd_any_uri())
|
|
||||||
.map(|u| u.to_owned())
|
|
||||||
.collect();
|
|
||||||
Ok(mods)
|
|
||||||
} else {
|
|
||||||
Ok(vec![])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -30,14 +30,7 @@ pub(crate) async fn get_or_fetch_and_insert_post(
|
||||||
debug!("Fetching and creating remote post: {}", post_ap_id);
|
debug!("Fetching and creating remote post: {}", post_ap_id);
|
||||||
let page =
|
let page =
|
||||||
fetch_remote_object::<PageExt>(context.client(), post_ap_id, recursion_counter).await?;
|
fetch_remote_object::<PageExt>(context.client(), post_ap_id, recursion_counter).await?;
|
||||||
let post = Post::from_apub(
|
let post = Post::from_apub(&page, context, post_ap_id.to_owned(), recursion_counter).await?;
|
||||||
&page,
|
|
||||||
context,
|
|
||||||
post_ap_id.to_owned(),
|
|
||||||
recursion_counter,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(post)
|
Ok(post)
|
||||||
}
|
}
|
||||||
|
@ -74,7 +67,6 @@ pub(crate) async fn get_or_fetch_and_insert_comment(
|
||||||
context,
|
context,
|
||||||
comment_ap_id.to_owned(),
|
comment_ap_id.to_owned(),
|
||||||
recursion_counter,
|
recursion_counter,
|
||||||
false,
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
|
|
@ -46,14 +46,8 @@ pub(crate) async fn get_or_fetch_and_upsert_person(
|
||||||
return Ok(u);
|
return Ok(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
let person = Person::from_apub(
|
let person =
|
||||||
&person?,
|
Person::from_apub(&person?, context, apub_id.to_owned(), recursion_counter).await?;
|
||||||
context,
|
|
||||||
apub_id.to_owned(),
|
|
||||||
recursion_counter,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let person_id = person.id;
|
let person_id = person.id;
|
||||||
blocking(context.pool(), move |conn| {
|
blocking(context.pool(), move |conn| {
|
||||||
|
@ -69,14 +63,8 @@ pub(crate) async fn get_or_fetch_and_upsert_person(
|
||||||
let person =
|
let person =
|
||||||
fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
|
fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
|
||||||
|
|
||||||
let person = Person::from_apub(
|
let person =
|
||||||
&person,
|
Person::from_apub(&person, context, apub_id.to_owned(), recursion_counter).await?;
|
||||||
context,
|
|
||||||
apub_id.to_owned(),
|
|
||||||
recursion_counter,
|
|
||||||
false,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
Ok(person)
|
Ok(person)
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,13 +147,13 @@ async fn build_response(
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
SearchAcceptedObjects::Page(p) => {
|
SearchAcceptedObjects::Page(p) => {
|
||||||
let p = Post::from_apub(&p, context, query_url, recursion_counter, false).await?;
|
let p = Post::from_apub(&p, context, query_url, recursion_counter).await?;
|
||||||
|
|
||||||
response.posts =
|
response.posts =
|
||||||
vec![blocking(context.pool(), move |conn| PostView::read(conn, p.id, None)).await??];
|
vec![blocking(context.pool(), move |conn| PostView::read(conn, p.id, None)).await??];
|
||||||
}
|
}
|
||||||
SearchAcceptedObjects::Comment(c) => {
|
SearchAcceptedObjects::Comment(c) => {
|
||||||
let c = Comment::from_apub(&c, context, query_url, recursion_counter, false).await?;
|
let c = Comment::from_apub(&c, context, query_url, recursion_counter).await?;
|
||||||
|
|
||||||
response.comments = vec![
|
response.comments = vec![
|
||||||
blocking(context.pool(), move |conn| {
|
blocking(context.pool(), move |conn| {
|
||||||
|
|
|
@ -12,12 +12,12 @@ use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(crate) struct CommentQuery {
|
pub struct CommentQuery {
|
||||||
comment_id: String,
|
comment_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the ActivityPub json representation of a local comment over HTTP.
|
/// Return the ActivityPub json representation of a local comment over HTTP.
|
||||||
pub(crate) async fn get_apub_comment(
|
pub async fn get_apub_comment(
|
||||||
info: Path<CommentQuery>,
|
info: Path<CommentQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
extensions::context::lemmy_context,
|
extensions::context::lemmy_context,
|
||||||
generate_moderators_url,
|
|
||||||
http::{create_apub_response, create_apub_tombstone_response},
|
http::{create_apub_response, create_apub_tombstone_response},
|
||||||
objects::ToApub,
|
objects::ToApub,
|
||||||
ActorType,
|
ActorType,
|
||||||
|
@ -8,27 +7,23 @@ use crate::{
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
base::{AnyBase, BaseExt},
|
base::{AnyBase, BaseExt},
|
||||||
collection::{CollectionExt, OrderedCollection, UnorderedCollection},
|
collection::{CollectionExt, OrderedCollection, UnorderedCollection},
|
||||||
url::Url,
|
|
||||||
};
|
};
|
||||||
use actix_web::{body::Body, web, HttpResponse};
|
use actix_web::{body::Body, web, HttpResponse};
|
||||||
use lemmy_api_structs::blocking;
|
use lemmy_api_structs::blocking;
|
||||||
use lemmy_db_queries::source::{activity::Activity_, community::Community_};
|
use lemmy_db_queries::source::{activity::Activity_, community::Community_};
|
||||||
use lemmy_db_schema::source::{activity::Activity, community::Community};
|
use lemmy_db_schema::source::{activity::Activity, community::Community};
|
||||||
use lemmy_db_views_actor::{
|
use lemmy_db_views_actor::community_follower_view::CommunityFollowerView;
|
||||||
community_follower_view::CommunityFollowerView,
|
|
||||||
community_moderator_view::CommunityModeratorView,
|
|
||||||
};
|
|
||||||
use lemmy_utils::LemmyError;
|
use lemmy_utils::LemmyError;
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(crate) struct CommunityQuery {
|
pub struct CommunityQuery {
|
||||||
community_name: String,
|
community_name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the ActivityPub json representation of a local community over HTTP.
|
/// Return the ActivityPub json representation of a local community over HTTP.
|
||||||
pub(crate) async fn get_apub_community_http(
|
pub async fn get_apub_community_http(
|
||||||
info: web::Path<CommunityQuery>,
|
info: web::Path<CommunityQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
@ -47,7 +42,7 @@ pub(crate) async fn get_apub_community_http(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an empty followers collection, only populating the size (for privacy).
|
/// Returns an empty followers collection, only populating the size (for privacy).
|
||||||
pub(crate) async fn get_apub_community_followers(
|
pub async fn get_apub_community_followers(
|
||||||
info: web::Path<CommunityQuery>,
|
info: web::Path<CommunityQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
@ -72,7 +67,7 @@ pub(crate) async fn get_apub_community_followers(
|
||||||
|
|
||||||
/// Returns the community outbox, which is populated by a maximum of 20 posts (but no other
|
/// Returns the community outbox, which is populated by a maximum of 20 posts (but no other
|
||||||
/// activites like votes or comments).
|
/// activites like votes or comments).
|
||||||
pub(crate) async fn get_apub_community_outbox(
|
pub async fn get_apub_community_outbox(
|
||||||
info: web::Path<CommunityQuery>,
|
info: web::Path<CommunityQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
@ -101,7 +96,7 @@ pub(crate) async fn get_apub_community_outbox(
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_apub_community_inbox(
|
pub async fn get_apub_community_inbox(
|
||||||
info: web::Path<CommunityQuery>,
|
info: web::Path<CommunityQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
@ -112,39 +107,7 @@ pub(crate) async fn get_apub_community_inbox(
|
||||||
|
|
||||||
let mut collection = OrderedCollection::new();
|
let mut collection = OrderedCollection::new();
|
||||||
collection
|
collection
|
||||||
.set_id(community.inbox_url.into())
|
.set_id(format!("{}/inbox", community.actor_id).parse()?)
|
||||||
.set_many_contexts(lemmy_context()?);
|
|
||||||
Ok(create_apub_response(&collection))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn get_apub_community_moderators(
|
|
||||||
info: web::Path<CommunityQuery>,
|
|
||||||
context: web::Data<LemmyContext>,
|
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
|
||||||
let community = blocking(context.pool(), move |conn| {
|
|
||||||
Community::read_from_name(&conn, &info.community_name)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
// The attributed to, is an ordered vector with the creator actor_ids first,
|
|
||||||
// then the rest of the moderators
|
|
||||||
// TODO Technically the instance admins can mod the community, but lets
|
|
||||||
// ignore that for now
|
|
||||||
let cid = community.id;
|
|
||||||
let moderators = blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModeratorView::for_community(&conn, cid)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
let moderators: Vec<Url> = moderators
|
|
||||||
.into_iter()
|
|
||||||
.map(|m| m.moderator.actor_id.into_inner())
|
|
||||||
.collect();
|
|
||||||
let mut collection = OrderedCollection::new();
|
|
||||||
collection
|
|
||||||
.set_id(generate_moderators_url(&community.actor_id)?.into())
|
|
||||||
.set_total_items(moderators.len() as u64)
|
|
||||||
.set_many_items(moderators)
|
|
||||||
.set_many_contexts(lemmy_context()?);
|
.set_many_contexts(lemmy_context()?);
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ pub struct CommunityQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the ActivityPub json representation of a local community over HTTP.
|
/// Return the ActivityPub json representation of a local community over HTTP.
|
||||||
pub(crate) async fn get_activity(
|
pub async fn get_activity(
|
||||||
info: web::Path<CommunityQuery>,
|
info: web::Path<CommunityQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub struct PersonQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the ActivityPub json representation of a local person over HTTP.
|
/// Return the ActivityPub json representation of a local person over HTTP.
|
||||||
pub(crate) async fn get_apub_person_http(
|
pub async fn get_apub_person_http(
|
||||||
info: web::Path<PersonQuery>,
|
info: web::Path<PersonQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
@ -43,7 +43,7 @@ pub(crate) async fn get_apub_person_http(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_apub_person_outbox(
|
pub async fn get_apub_person_outbox(
|
||||||
info: web::Path<PersonQuery>,
|
info: web::Path<PersonQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
@ -61,7 +61,7 @@ pub(crate) async fn get_apub_person_outbox(
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn get_apub_person_inbox(
|
pub async fn get_apub_person_inbox(
|
||||||
info: web::Path<PersonQuery>,
|
info: web::Path<PersonQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
@ -72,7 +72,7 @@ pub(crate) async fn get_apub_person_inbox(
|
||||||
|
|
||||||
let mut collection = OrderedCollection::new();
|
let mut collection = OrderedCollection::new();
|
||||||
collection
|
collection
|
||||||
.set_id(person.inbox_url.into())
|
.set_id(format!("{}/inbox", person.actor_id.into_inner()).parse()?)
|
||||||
.set_many_contexts(lemmy_context()?);
|
.set_many_contexts(lemmy_context()?);
|
||||||
Ok(create_apub_response(&collection))
|
Ok(create_apub_response(&collection))
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,12 +12,12 @@ use lemmy_websocket::LemmyContext;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub(crate) struct PostQuery {
|
pub struct PostQuery {
|
||||||
post_id: String,
|
post_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the ActivityPub json representation of a local post over HTTP.
|
/// Return the ActivityPub json representation of a local post over HTTP.
|
||||||
pub(crate) async fn get_apub_post(
|
pub async fn get_apub_post(
|
||||||
info: web::Path<PostQuery>,
|
info: web::Path<PostQuery>,
|
||||||
context: web::Data<LemmyContext>,
|
context: web::Data<LemmyContext>,
|
||||||
) -> Result<HttpResponse<Body>, LemmyError> {
|
) -> Result<HttpResponse<Body>, LemmyError> {
|
||||||
|
|
|
@ -6,21 +6,18 @@ use crate::{
|
||||||
get_activity_to_and_cc,
|
get_activity_to_and_cc,
|
||||||
inbox_verify_http_signature,
|
inbox_verify_http_signature,
|
||||||
is_activity_already_known,
|
is_activity_already_known,
|
||||||
|
is_addressed_to_public,
|
||||||
receive_for_community::{
|
receive_for_community::{
|
||||||
receive_add_for_community,
|
|
||||||
receive_create_for_community,
|
receive_create_for_community,
|
||||||
receive_delete_for_community,
|
receive_delete_for_community,
|
||||||
receive_dislike_for_community,
|
receive_dislike_for_community,
|
||||||
receive_like_for_community,
|
receive_like_for_community,
|
||||||
receive_remove_for_community,
|
|
||||||
receive_undo_for_community,
|
receive_undo_for_community,
|
||||||
receive_update_for_community,
|
receive_update_for_community,
|
||||||
},
|
},
|
||||||
verify_is_addressed_to_public,
|
|
||||||
},
|
},
|
||||||
insert_activity,
|
insert_activity,
|
||||||
ActorType,
|
ActorType,
|
||||||
CommunityType,
|
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{kind::FollowType, ActorAndObject, Follow, Undo},
|
activity::{kind::FollowType, ActorAndObject, Follow, Undo},
|
||||||
|
@ -57,8 +54,7 @@ pub enum CommunityValidTypes {
|
||||||
Like, // upvote post or comment
|
Like, // upvote post or comment
|
||||||
Dislike, // downvote post or comment
|
Dislike, // downvote post or comment
|
||||||
Delete, // post or comment deleted by creator
|
Delete, // post or comment deleted by creator
|
||||||
Remove, // post or comment removed by mod or admin, or mod removed from community
|
Remove, // post or comment removed by mod or admin
|
||||||
Add, // mod added to community
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type CommunityAcceptedActivities = ActorAndObject<CommunityValidTypes>;
|
pub type CommunityAcceptedActivities = ActorAndObject<CommunityValidTypes>;
|
||||||
|
@ -152,8 +148,7 @@ pub(crate) async fn community_receive_message(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
CommunityValidTypes::Update => {
|
CommunityValidTypes::Update => {
|
||||||
receive_update_for_community(context, any_base.clone(), None, &actor_url, request_counter)
|
receive_update_for_community(context, any_base.clone(), &actor_url, request_counter).await?;
|
||||||
.await?;
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
CommunityValidTypes::Like => {
|
CommunityValidTypes::Like => {
|
||||||
|
@ -165,22 +160,19 @@ pub(crate) async fn community_receive_message(
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
CommunityValidTypes::Delete => {
|
CommunityValidTypes::Delete => {
|
||||||
receive_delete_for_community(context, any_base.clone(), None, &actor_url).await?;
|
receive_delete_for_community(context, any_base.clone(), &actor_url).await?;
|
||||||
true
|
|
||||||
}
|
|
||||||
CommunityValidTypes::Add => {
|
|
||||||
receive_add_for_community(context, any_base.clone(), None, request_counter).await?;
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
CommunityValidTypes::Remove => {
|
CommunityValidTypes::Remove => {
|
||||||
receive_remove_for_community(context, any_base.clone(), None, request_counter).await?;
|
// TODO: we dont support remote mods, so this is ignored for now
|
||||||
true
|
//receive_remove_for_community(context, any_base.clone(), &person_url).await?
|
||||||
|
false
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if do_announce {
|
if do_announce {
|
||||||
// Check again that the activity is public, just to be sure
|
// Check again that the activity is public, just to be sure
|
||||||
verify_is_addressed_to_public(&activity)?;
|
is_addressed_to_public(&activity)?;
|
||||||
to_community
|
to_community
|
||||||
.send_announce(activity.into_any_base()?, context)
|
.send_announce(activity.into_any_base()?, context)
|
||||||
.await?;
|
.await?;
|
||||||
|
@ -232,7 +224,7 @@ async fn handle_undo(
|
||||||
handle_undo_follow(any_base, actor_url, to_community, &context).await?;
|
handle_undo_follow(any_base, actor_url, to_community, &context).await?;
|
||||||
Ok(false)
|
Ok(false)
|
||||||
} else {
|
} else {
|
||||||
receive_undo_for_community(context, any_base, None, &actor_url, request_counter).await?;
|
receive_undo_for_community(context, any_base, &actor_url, request_counter).await?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,7 @@ use url::Url;
|
||||||
|
|
||||||
pub mod community_inbox;
|
pub mod community_inbox;
|
||||||
pub mod person_inbox;
|
pub mod person_inbox;
|
||||||
pub(crate) mod receive_for_community;
|
mod receive_for_community;
|
||||||
pub mod shared_inbox;
|
pub mod shared_inbox;
|
||||||
|
|
||||||
pub(crate) fn get_activity_id<T, Kind>(activity: &T, creator_uri: &Url) -> Result<Url, LemmyError>
|
pub(crate) fn get_activity_id<T, Kind>(activity: &T, creator_uri: &Url) -> Result<Url, LemmyError>
|
||||||
|
@ -84,7 +84,7 @@ where
|
||||||
to_and_cc
|
to_and_cc
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn verify_is_addressed_to_public<T, Kind>(activity: &T) -> Result<(), LemmyError>
|
pub(crate) fn is_addressed_to_public<T, Kind>(activity: &T) -> Result<(), LemmyError>
|
||||||
where
|
where
|
||||||
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
T: AsBase<Kind> + AsObject<Kind> + ActorAndObjectRefExt,
|
||||||
{
|
{
|
||||||
|
|
|
@ -26,8 +26,8 @@ use crate::{
|
||||||
is_activity_already_known,
|
is_activity_already_known,
|
||||||
is_addressed_to_community_followers,
|
is_addressed_to_community_followers,
|
||||||
is_addressed_to_local_person,
|
is_addressed_to_local_person,
|
||||||
|
is_addressed_to_public,
|
||||||
receive_for_community::{
|
receive_for_community::{
|
||||||
receive_add_for_community,
|
|
||||||
receive_create_for_community,
|
receive_create_for_community,
|
||||||
receive_delete_for_community,
|
receive_delete_for_community,
|
||||||
receive_dislike_for_community,
|
receive_dislike_for_community,
|
||||||
|
@ -36,13 +36,12 @@ use crate::{
|
||||||
receive_undo_for_community,
|
receive_undo_for_community,
|
||||||
receive_update_for_community,
|
receive_update_for_community,
|
||||||
},
|
},
|
||||||
verify_is_addressed_to_public,
|
|
||||||
},
|
},
|
||||||
insert_activity,
|
insert_activity,
|
||||||
ActorType,
|
ActorType,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Remove, Undo, Update},
|
activity::{Accept, ActorAndObject, Announce, Create, Delete, Follow, Undo, Update},
|
||||||
base::AnyBase,
|
base::AnyBase,
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
|
@ -166,7 +165,7 @@ pub(crate) async fn person_receive_message(
|
||||||
receive_delete(context, any_base, &actor_url, request_counter).await?
|
receive_delete(context, any_base, &actor_url, request_counter).await?
|
||||||
}
|
}
|
||||||
PersonValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
|
PersonValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
|
||||||
PersonValidTypes::Remove => receive_remove(context, any_base, &actor_url).await?,
|
PersonValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?,
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: would be logical to move websocket notification code here
|
// TODO: would be logical to move websocket notification code here
|
||||||
|
@ -253,7 +252,6 @@ enum AnnouncableActivities {
|
||||||
Delete,
|
Delete,
|
||||||
Remove,
|
Remove,
|
||||||
Undo,
|
Undo,
|
||||||
Add,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes an announce and passes the inner activity to the appropriate handler.
|
/// Takes an announce and passes the inner activity to the appropriate handler.
|
||||||
|
@ -265,7 +263,7 @@ pub async fn receive_announce(
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let announce = Announce::from_any_base(activity)?.context(location_info!())?;
|
let announce = Announce::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&announce, &actor.actor_id(), false)?;
|
verify_activity_domains_valid(&announce, &actor.actor_id(), false)?;
|
||||||
verify_is_addressed_to_public(&announce)?;
|
is_addressed_to_public(&announce)?;
|
||||||
|
|
||||||
let kind = announce
|
let kind = announce
|
||||||
.object()
|
.object()
|
||||||
|
@ -289,14 +287,7 @@ pub async fn receive_announce(
|
||||||
receive_create_for_community(context, inner_activity, &inner_id, request_counter).await
|
receive_create_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||||
}
|
}
|
||||||
Some(Update) => {
|
Some(Update) => {
|
||||||
receive_update_for_community(
|
receive_update_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||||
context,
|
|
||||||
inner_activity,
|
|
||||||
Some(announce),
|
|
||||||
&inner_id,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
Some(Like) => {
|
Some(Like) => {
|
||||||
receive_like_for_community(context, inner_activity, &inner_id, request_counter).await
|
receive_like_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||||
|
@ -304,31 +295,15 @@ pub async fn receive_announce(
|
||||||
Some(Dislike) => {
|
Some(Dislike) => {
|
||||||
receive_dislike_for_community(context, inner_activity, &inner_id, request_counter).await
|
receive_dislike_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||||
}
|
}
|
||||||
Some(Delete) => {
|
Some(Delete) => receive_delete_for_community(context, inner_activity, &inner_id).await,
|
||||||
receive_delete_for_community(context, inner_activity, Some(announce), &inner_id).await
|
Some(Remove) => receive_remove_for_community(context, inner_activity, &inner_id).await,
|
||||||
}
|
|
||||||
Some(Remove) => {
|
|
||||||
receive_remove_for_community(context, inner_activity, Some(announce), request_counter).await
|
|
||||||
}
|
|
||||||
Some(Undo) => {
|
Some(Undo) => {
|
||||||
receive_undo_for_community(
|
receive_undo_for_community(context, inner_activity, &inner_id, request_counter).await
|
||||||
context,
|
|
||||||
inner_activity,
|
|
||||||
Some(announce),
|
|
||||||
&inner_id,
|
|
||||||
request_counter,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
Some(Add) => {
|
|
||||||
receive_add_for_community(context, inner_activity, Some(announce), request_counter).await
|
|
||||||
}
|
}
|
||||||
_ => receive_unhandled_activity(inner_activity),
|
_ => receive_unhandled_activity(inner_activity),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive either a new private message, or a new comment mention. We distinguish them by checking
|
|
||||||
/// whether the activity is public.
|
|
||||||
async fn receive_create(
|
async fn receive_create(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
activity: AnyBase,
|
activity: AnyBase,
|
||||||
|
@ -337,15 +312,13 @@ async fn receive_create(
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let create = Create::from_any_base(activity)?.context(location_info!())?;
|
let create = Create::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&create, &expected_domain, true)?;
|
verify_activity_domains_valid(&create, &expected_domain, true)?;
|
||||||
if verify_is_addressed_to_public(&create).is_ok() {
|
if is_addressed_to_public(&create).is_ok() {
|
||||||
receive_create_comment(create, context, request_counter).await
|
receive_create_comment(create, context, request_counter).await
|
||||||
} else {
|
} else {
|
||||||
receive_create_private_message(&context, create, expected_domain, request_counter).await
|
receive_create_private_message(&context, create, expected_domain, request_counter).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Receive either an updated private message, or an updated comment mention. We distinguish
|
|
||||||
/// them by checking whether the activity is public.
|
|
||||||
async fn receive_update(
|
async fn receive_update(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
activity: AnyBase,
|
activity: AnyBase,
|
||||||
|
@ -354,7 +327,7 @@ async fn receive_update(
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let update = Update::from_any_base(activity)?.context(location_info!())?;
|
let update = Update::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&update, &expected_domain, true)?;
|
verify_activity_domains_valid(&update, &expected_domain, true)?;
|
||||||
if verify_is_addressed_to_public(&update).is_ok() {
|
if is_addressed_to_public(&update).is_ok() {
|
||||||
receive_update_comment(update, context, request_counter).await
|
receive_update_comment(update, context, request_counter).await
|
||||||
} else {
|
} else {
|
||||||
receive_update_private_message(&context, update, expected_domain, request_counter).await
|
receive_update_private_message(&context, update, expected_domain, request_counter).await
|
||||||
|
@ -383,31 +356,13 @@ async fn receive_delete(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn receive_remove(
|
|
||||||
context: &LemmyContext,
|
|
||||||
any_base: AnyBase,
|
|
||||||
expected_domain: &Url,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let remove = Remove::from_any_base(any_base.clone())?.context(location_info!())?;
|
|
||||||
verify_activity_domains_valid(&remove, expected_domain, true)?;
|
|
||||||
let object_uri = remove
|
|
||||||
.object()
|
|
||||||
.to_owned()
|
|
||||||
.single_xsd_any_uri()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let community = blocking(context.pool(), move |conn| {
|
|
||||||
Community::read_from_apub_id(conn, &object_uri.into())
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
receive_remove_community(&context, community).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn receive_undo(
|
async fn receive_undo(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
any_base: AnyBase,
|
any_base: AnyBase,
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
|
use CommunityOrPrivateMessage::*;
|
||||||
let undo = Undo::from_any_base(any_base)?.context(location_info!())?;
|
let undo = Undo::from_any_base(any_base)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&undo, expected_domain, true)?;
|
verify_activity_domains_valid(&undo, expected_domain, true)?;
|
||||||
|
|
||||||
|
@ -422,28 +377,15 @@ async fn receive_undo(
|
||||||
.to_owned()
|
.to_owned()
|
||||||
.single_xsd_any_uri()
|
.single_xsd_any_uri()
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
use CommunityOrPrivateMessage::*;
|
|
||||||
match find_community_or_private_message_by_id(context, object_uri).await? {
|
match find_community_or_private_message_by_id(context, object_uri).await? {
|
||||||
Community(c) => receive_undo_delete_community(context, c).await,
|
Community(c) => receive_undo_delete_community(context, undo, c, expected_domain).await,
|
||||||
PrivateMessage(p) => {
|
PrivateMessage(p) => {
|
||||||
receive_undo_delete_private_message(context, undo, expected_domain, p, request_counter)
|
receive_undo_delete_private_message(context, undo, expected_domain, p, request_counter)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Some("Remove") => {
|
Some("Remove") => receive_undo_remove_community(context, undo, expected_domain).await,
|
||||||
let remove = Remove::from_any_base(inner_activity)?.context(location_info!())?;
|
|
||||||
let object_uri = remove
|
|
||||||
.object()
|
|
||||||
.to_owned()
|
|
||||||
.single_xsd_any_uri()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let community = blocking(context.pool(), move |conn| {
|
|
||||||
Community::read_from_apub_id(conn, &object_uri.into())
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
receive_undo_remove_community(context, community).await
|
|
||||||
}
|
|
||||||
_ => receive_unhandled_activity(undo),
|
_ => receive_unhandled_activity(undo),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,50 +31,21 @@ use crate::{
|
||||||
receive_unhandled_activity,
|
receive_unhandled_activity,
|
||||||
verify_activity_domains_valid,
|
verify_activity_domains_valid,
|
||||||
},
|
},
|
||||||
fetcher::{
|
fetcher::objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
||||||
objects::{get_or_fetch_and_insert_comment, get_or_fetch_and_insert_post},
|
|
||||||
person::get_or_fetch_and_upsert_person,
|
|
||||||
},
|
|
||||||
find_object_by_id,
|
|
||||||
find_post_or_comment_by_id,
|
find_post_or_comment_by_id,
|
||||||
generate_moderators_url,
|
inbox::is_addressed_to_public,
|
||||||
inbox::verify_is_addressed_to_public,
|
|
||||||
ActorType,
|
|
||||||
CommunityType,
|
|
||||||
Object,
|
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::{
|
activity::{Create, Delete, Dislike, Like, Remove, Undo, Update},
|
||||||
ActorAndObjectRef,
|
|
||||||
Add,
|
|
||||||
Announce,
|
|
||||||
Create,
|
|
||||||
Delete,
|
|
||||||
Dislike,
|
|
||||||
Like,
|
|
||||||
OptTargetRef,
|
|
||||||
Remove,
|
|
||||||
Undo,
|
|
||||||
Update,
|
|
||||||
},
|
|
||||||
base::AnyBase,
|
base::AnyBase,
|
||||||
object::AsObject,
|
|
||||||
prelude::*,
|
prelude::*,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::Context;
|
||||||
use diesel::result::Error::NotFound;
|
use diesel::result::Error::NotFound;
|
||||||
use lemmy_api_structs::blocking;
|
use lemmy_api_structs::blocking;
|
||||||
use lemmy_db_queries::{source::community::CommunityModerator_, ApubObject, Crud, Joinable};
|
use lemmy_db_queries::Crud;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::source::site::Site;
|
||||||
source::{
|
|
||||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
|
||||||
person::Person,
|
|
||||||
site::Site,
|
|
||||||
},
|
|
||||||
DbUrl,
|
|
||||||
};
|
|
||||||
use lemmy_db_views_actor::community_view::CommunityView;
|
|
||||||
use lemmy_utils::{location_info, LemmyError};
|
use lemmy_utils::{location_info, LemmyError};
|
||||||
use lemmy_websocket::LemmyContext;
|
use lemmy_websocket::LemmyContext;
|
||||||
use strum_macros::EnumString;
|
use strum_macros::EnumString;
|
||||||
|
@ -98,7 +69,7 @@ pub(in crate::inbox) async fn receive_create_for_community(
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let create = Create::from_any_base(activity)?.context(location_info!())?;
|
let create = Create::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&create, &expected_domain, true)?;
|
verify_activity_domains_valid(&create, &expected_domain, true)?;
|
||||||
verify_is_addressed_to_public(&create)?;
|
is_addressed_to_public(&create)?;
|
||||||
|
|
||||||
let kind = create
|
let kind = create
|
||||||
.object()
|
.object()
|
||||||
|
@ -115,21 +86,19 @@ pub(in crate::inbox) async fn receive_create_for_community(
|
||||||
pub(in crate::inbox) async fn receive_update_for_community(
|
pub(in crate::inbox) async fn receive_update_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
activity: AnyBase,
|
activity: AnyBase,
|
||||||
announce: Option<Announce>,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let update = Update::from_any_base(activity)?.context(location_info!())?;
|
let update = Update::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&update, &expected_domain, false)?;
|
verify_activity_domains_valid(&update, &expected_domain, true)?;
|
||||||
verify_is_addressed_to_public(&update)?;
|
is_addressed_to_public(&update)?;
|
||||||
verify_modification_actor_instance(&update, &announce, context).await?;
|
|
||||||
|
|
||||||
let kind = update
|
let kind = update
|
||||||
.object()
|
.object()
|
||||||
.as_single_kind_str()
|
.as_single_kind_str()
|
||||||
.and_then(|s| s.parse().ok());
|
.and_then(|s| s.parse().ok());
|
||||||
match kind {
|
match kind {
|
||||||
Some(PageOrNote::Page) => receive_update_post(update, announce, context, request_counter).await,
|
Some(PageOrNote::Page) => receive_update_post(update, context, request_counter).await,
|
||||||
Some(PageOrNote::Note) => receive_update_comment(update, context, request_counter).await,
|
Some(PageOrNote::Note) => receive_update_comment(update, context, request_counter).await,
|
||||||
_ => receive_unhandled_activity(update),
|
_ => receive_unhandled_activity(update),
|
||||||
}
|
}
|
||||||
|
@ -144,7 +113,7 @@ pub(in crate::inbox) async fn receive_like_for_community(
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let like = Like::from_any_base(activity)?.context(location_info!())?;
|
let like = Like::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&like, &expected_domain, false)?;
|
verify_activity_domains_valid(&like, &expected_domain, false)?;
|
||||||
verify_is_addressed_to_public(&like)?;
|
is_addressed_to_public(&like)?;
|
||||||
|
|
||||||
let object_id = like
|
let object_id = like
|
||||||
.object()
|
.object()
|
||||||
|
@ -175,7 +144,7 @@ pub(in crate::inbox) async fn receive_dislike_for_community(
|
||||||
|
|
||||||
let dislike = Dislike::from_any_base(activity)?.context(location_info!())?;
|
let dislike = Dislike::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
|
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
|
||||||
verify_is_addressed_to_public(&dislike)?;
|
is_addressed_to_public(&dislike)?;
|
||||||
|
|
||||||
let object_id = dislike
|
let object_id = dislike
|
||||||
.object()
|
.object()
|
||||||
|
@ -195,13 +164,11 @@ pub(in crate::inbox) async fn receive_dislike_for_community(
|
||||||
pub(in crate::inbox) async fn receive_delete_for_community(
|
pub(in crate::inbox) async fn receive_delete_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
activity: AnyBase,
|
activity: AnyBase,
|
||||||
announce: Option<Announce>,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let delete = Delete::from_any_base(activity)?.context(location_info!())?;
|
let delete = Delete::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
||||||
verify_is_addressed_to_public(&delete)?;
|
is_addressed_to_public(&delete)?;
|
||||||
verify_modification_actor_instance(&delete, &announce, context).await?;
|
|
||||||
|
|
||||||
let object = delete
|
let object = delete
|
||||||
.object()
|
.object()
|
||||||
|
@ -220,48 +187,38 @@ pub(in crate::inbox) async fn receive_delete_for_community(
|
||||||
/// A post or comment being removed by a mod/admin
|
/// A post or comment being removed by a mod/admin
|
||||||
pub(in crate::inbox) async fn receive_remove_for_community(
|
pub(in crate::inbox) async fn receive_remove_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
remove_any_base: AnyBase,
|
activity: AnyBase,
|
||||||
announce: Option<Announce>,
|
expected_domain: &Url,
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let remove = Remove::from_any_base(remove_any_base.to_owned())?.context(location_info!())?;
|
let remove = Remove::from_any_base(activity)?.context(location_info!())?;
|
||||||
let community = extract_community_from_cc(&remove, context).await?;
|
verify_activity_domains_valid(&remove, &expected_domain, false)?;
|
||||||
|
is_addressed_to_public(&remove)?;
|
||||||
|
|
||||||
verify_mod_activity(&remove, announce, &community, context).await?;
|
let cc = remove
|
||||||
verify_is_addressed_to_public(&remove)?;
|
.cc()
|
||||||
|
.map(|c| c.as_many())
|
||||||
|
.flatten()
|
||||||
|
.context(location_info!())?;
|
||||||
|
let community_id = cc
|
||||||
|
.first()
|
||||||
|
.map(|c| c.as_xsd_any_uri())
|
||||||
|
.flatten()
|
||||||
|
.context(location_info!())?;
|
||||||
|
|
||||||
if remove.target().is_some() {
|
let object = remove
|
||||||
let remove_mod = remove
|
.object()
|
||||||
.object()
|
.to_owned()
|
||||||
.as_single_xsd_any_uri()
|
.single_xsd_any_uri()
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
let remove_mod = get_or_fetch_and_upsert_person(&remove_mod, context, request_counter).await?;
|
|
||||||
let form = CommunityModeratorForm {
|
|
||||||
community_id: community.id,
|
|
||||||
person_id: remove_mod.id,
|
|
||||||
};
|
|
||||||
blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModerator::leave(conn, &form)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
community.send_announce(remove_any_base, context).await?;
|
|
||||||
// TODO: send websocket notification about removed mod
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
// Remove a post or comment
|
|
||||||
else {
|
|
||||||
let object = remove
|
|
||||||
.object()
|
|
||||||
.to_owned()
|
|
||||||
.single_xsd_any_uri()
|
|
||||||
.context(location_info!())?;
|
|
||||||
|
|
||||||
match find_post_or_comment_by_id(context, object).await {
|
// Ensure that remove activity comes from the same domain as the community
|
||||||
Ok(PostOrComment::Post(p)) => receive_remove_post(context, *p).await,
|
remove.id(community_id.domain().context(location_info!())?)?;
|
||||||
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, *c).await,
|
|
||||||
// if we dont have the object, no need to do anything
|
match find_post_or_comment_by_id(context, object).await {
|
||||||
Err(_) => Ok(()),
|
Ok(PostOrComment::Post(p)) => receive_remove_post(context, remove, *p).await,
|
||||||
}
|
Ok(PostOrComment::Comment(c)) => receive_remove_comment(context, remove, *c).await,
|
||||||
|
// if we dont have the object, no need to do anything
|
||||||
|
Err(_) => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,13 +234,12 @@ enum UndoableActivities {
|
||||||
pub(in crate::inbox) async fn receive_undo_for_community(
|
pub(in crate::inbox) async fn receive_undo_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
activity: AnyBase,
|
activity: AnyBase,
|
||||||
announce: Option<Announce>,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let undo = Undo::from_any_base(activity)?.context(location_info!())?;
|
let undo = Undo::from_any_base(activity)?.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&undo, &expected_domain.to_owned(), true)?;
|
verify_activity_domains_valid(&undo, &expected_domain.to_owned(), true)?;
|
||||||
verify_is_addressed_to_public(&undo)?;
|
is_addressed_to_public(&undo)?;
|
||||||
|
|
||||||
use UndoableActivities::*;
|
use UndoableActivities::*;
|
||||||
match undo
|
match undo
|
||||||
|
@ -292,9 +248,7 @@ pub(in crate::inbox) async fn receive_undo_for_community(
|
||||||
.and_then(|s| s.parse().ok())
|
.and_then(|s| s.parse().ok())
|
||||||
{
|
{
|
||||||
Some(Delete) => receive_undo_delete_for_community(context, undo, expected_domain).await,
|
Some(Delete) => receive_undo_delete_for_community(context, undo, expected_domain).await,
|
||||||
Some(Remove) => {
|
Some(Remove) => receive_undo_remove_for_community(context, undo, expected_domain).await,
|
||||||
receive_undo_remove_for_community(context, undo, announce, expected_domain).await
|
|
||||||
}
|
|
||||||
Some(Like) => {
|
Some(Like) => {
|
||||||
receive_undo_like_for_community(context, undo, expected_domain, request_counter).await
|
receive_undo_like_for_community(context, undo, expected_domain, request_counter).await
|
||||||
}
|
}
|
||||||
|
@ -314,7 +268,7 @@ pub(in crate::inbox) async fn receive_undo_delete_for_community(
|
||||||
let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
let delete = Delete::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
verify_activity_domains_valid(&delete, &expected_domain, true)?;
|
||||||
verify_is_addressed_to_public(&delete)?;
|
is_addressed_to_public(&delete)?;
|
||||||
|
|
||||||
let object = delete
|
let object = delete
|
||||||
.object()
|
.object()
|
||||||
|
@ -333,14 +287,12 @@ pub(in crate::inbox) async fn receive_undo_delete_for_community(
|
||||||
pub(in crate::inbox) async fn receive_undo_remove_for_community(
|
pub(in crate::inbox) async fn receive_undo_remove_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
undo: Undo,
|
undo: Undo,
|
||||||
announce: Option<Announce>,
|
|
||||||
expected_domain: &Url,
|
expected_domain: &Url,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
let remove = Remove::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&remove, &expected_domain, false)?;
|
verify_activity_domains_valid(&remove, &expected_domain, false)?;
|
||||||
verify_is_addressed_to_public(&remove)?;
|
is_addressed_to_public(&remove)?;
|
||||||
verify_undo_remove_actor_instance(&undo, &remove, &announce, context).await?;
|
|
||||||
|
|
||||||
let object = remove
|
let object = remove
|
||||||
.object()
|
.object()
|
||||||
|
@ -365,7 +317,7 @@ pub(in crate::inbox) async fn receive_undo_like_for_community(
|
||||||
let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
let like = Like::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&like, &expected_domain, false)?;
|
verify_activity_domains_valid(&like, &expected_domain, false)?;
|
||||||
verify_is_addressed_to_public(&like)?;
|
is_addressed_to_public(&like)?;
|
||||||
|
|
||||||
let object_id = like
|
let object_id = like
|
||||||
.object()
|
.object()
|
||||||
|
@ -381,50 +333,6 @@ pub(in crate::inbox) async fn receive_undo_like_for_community(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Add a new mod to the community (can only be done by an existing mod).
|
|
||||||
pub(in crate::inbox) async fn receive_add_for_community(
|
|
||||||
context: &LemmyContext,
|
|
||||||
add_any_base: AnyBase,
|
|
||||||
announce: Option<Announce>,
|
|
||||||
request_counter: &mut i32,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let add = Add::from_any_base(add_any_base.to_owned())?.context(location_info!())?;
|
|
||||||
let community = extract_community_from_cc(&add, context).await?;
|
|
||||||
|
|
||||||
verify_mod_activity(&add, announce, &community, context).await?;
|
|
||||||
verify_is_addressed_to_public(&add)?;
|
|
||||||
verify_add_remove_moderator_target(&add, &community)?;
|
|
||||||
|
|
||||||
let new_mod = add
|
|
||||||
.object()
|
|
||||||
.as_single_xsd_any_uri()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let new_mod = get_or_fetch_and_upsert_person(&new_mod, context, request_counter).await?;
|
|
||||||
|
|
||||||
// If we had to refetch the community while parsing the activity, then the new mod has already
|
|
||||||
// been added. Skip it here as it would result in a duplicate key error.
|
|
||||||
let new_mod_id = new_mod.id;
|
|
||||||
let moderated_communities = blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModerator::get_person_moderated_communities(conn, new_mod_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
if !moderated_communities.contains(&community.id) {
|
|
||||||
let form = CommunityModeratorForm {
|
|
||||||
community_id: community.id,
|
|
||||||
person_id: new_mod.id,
|
|
||||||
};
|
|
||||||
blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModerator::join(conn, &form)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
}
|
|
||||||
if community.local {
|
|
||||||
community.send_announce(add_any_base, context).await?;
|
|
||||||
}
|
|
||||||
// TODO: send websocket notification about added mod
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A post or comment downvote being reverted
|
/// A post or comment downvote being reverted
|
||||||
pub(in crate::inbox) async fn receive_undo_dislike_for_community(
|
pub(in crate::inbox) async fn receive_undo_dislike_for_community(
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
|
@ -435,7 +343,7 @@ pub(in crate::inbox) async fn receive_undo_dislike_for_community(
|
||||||
let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
let dislike = Dislike::from_any_base(undo.object().to_owned().one().context(location_info!())?)?
|
||||||
.context(location_info!())?;
|
.context(location_info!())?;
|
||||||
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
|
verify_activity_domains_valid(&dislike, &expected_domain, false)?;
|
||||||
verify_is_addressed_to_public(&dislike)?;
|
is_addressed_to_public(&dislike)?;
|
||||||
|
|
||||||
let object_id = dislike
|
let object_id = dislike
|
||||||
.object()
|
.object()
|
||||||
|
@ -466,172 +374,3 @@ async fn fetch_post_or_comment_by_id(
|
||||||
|
|
||||||
Err(NotFound.into())
|
Err(NotFound.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Searches the activity's cc field for a Community ID, and returns the community.
|
|
||||||
async fn extract_community_from_cc<T, Kind>(
|
|
||||||
activity: &T,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<Community, LemmyError>
|
|
||||||
where
|
|
||||||
T: AsObject<Kind>,
|
|
||||||
{
|
|
||||||
let cc = activity
|
|
||||||
.cc()
|
|
||||||
.map(|c| c.as_many())
|
|
||||||
.flatten()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let community_id = cc
|
|
||||||
.first()
|
|
||||||
.map(|c| c.as_xsd_any_uri())
|
|
||||||
.flatten()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let community_id: DbUrl = community_id.to_owned().into();
|
|
||||||
let community = blocking(&context.pool(), move |conn| {
|
|
||||||
Community::read_from_apub_id(&conn, &community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
Ok(community)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks that a moderation activity was sent by a user who is listed as mod for the community.
|
|
||||||
/// This is only used in the case of remote mods, as local mod actions don't go through the
|
|
||||||
/// community inbox.
|
|
||||||
///
|
|
||||||
/// This method should only be used for activities received by the community, not for activities
|
|
||||||
/// used by community followers.
|
|
||||||
async fn verify_actor_is_community_mod<T, Kind>(
|
|
||||||
activity: &T,
|
|
||||||
community: &Community,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: ActorAndObjectRef + BaseExt<Kind>,
|
|
||||||
{
|
|
||||||
let actor = activity
|
|
||||||
.actor()?
|
|
||||||
.as_single_xsd_any_uri()
|
|
||||||
.context(location_info!())?
|
|
||||||
.to_owned();
|
|
||||||
let actor = blocking(&context.pool(), move |conn| {
|
|
||||||
Person::read_from_apub_id(&conn, &actor.into())
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
|
|
||||||
// Note: this will also return true for admins in addition to mods, but as we dont know about
|
|
||||||
// remote admins, it doesnt make any difference.
|
|
||||||
let community_id = community.id;
|
|
||||||
let actor_id = actor.id;
|
|
||||||
let is_mod_or_admin = blocking(context.pool(), move |conn| {
|
|
||||||
CommunityView::is_mod_or_admin(conn, actor_id, community_id)
|
|
||||||
})
|
|
||||||
.await?;
|
|
||||||
if !is_mod_or_admin {
|
|
||||||
return Err(anyhow!("Not a mod").into());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This method behaves differently, depending if it is called via community inbox (activity
|
|
||||||
/// received by community from a remote user), or via user inbox (activity received by user from
|
|
||||||
/// community). We distinguish the cases by checking if the activity is wrapper in an announce
|
|
||||||
/// (only true when sent from user to community).
|
|
||||||
///
|
|
||||||
/// In the first case, we check that the actor is listed as community mod. In the second case, we
|
|
||||||
/// only check that the announce comes from the same domain as the activity. We trust the
|
|
||||||
/// community's instance to have validated the inner activity correctly. We can't do this validation
|
|
||||||
/// here, because we don't know who the instance admins are. Plus this allows for compatibility with
|
|
||||||
/// software that uses different rules for mod actions.
|
|
||||||
pub(crate) async fn verify_mod_activity<T, Kind>(
|
|
||||||
mod_action: &T,
|
|
||||||
announce: Option<Announce>,
|
|
||||||
community: &Community,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: ActorAndObjectRef + BaseExt<Kind>,
|
|
||||||
{
|
|
||||||
match announce {
|
|
||||||
None => verify_actor_is_community_mod(mod_action, community, context).await?,
|
|
||||||
Some(a) => verify_activity_domains_valid(&a, &community.actor_id.to_owned().into(), false)?,
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For Add/Remove community moderator activities, check that the target field actually contains
|
|
||||||
/// /c/community/moderators. Any different values are unsupported.
|
|
||||||
fn verify_add_remove_moderator_target<T, Kind>(
|
|
||||||
activity: &T,
|
|
||||||
community: &Community,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: ActorAndObjectRef + BaseExt<Kind> + OptTargetRef,
|
|
||||||
{
|
|
||||||
let target = activity
|
|
||||||
.target()
|
|
||||||
.map(|t| t.as_single_xsd_any_uri())
|
|
||||||
.flatten()
|
|
||||||
.context(location_info!())?;
|
|
||||||
if target != &generate_moderators_url(&community.actor_id)?.into_inner() {
|
|
||||||
return Err(anyhow!("Unkown target url").into());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// For activities like Update, Delete or Remove, check that the actor is from the same instance
|
|
||||||
/// as the original object itself (or is a remote mod).
|
|
||||||
///
|
|
||||||
/// Note: This is only needed for mod actions. Normal user actions (edit post, undo vote etc) are
|
|
||||||
/// already verified with `expected_domain`, so this serves as an additional check.
|
|
||||||
async fn verify_modification_actor_instance<T, Kind>(
|
|
||||||
activity: &T,
|
|
||||||
announce: &Option<Announce>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: ActorAndObjectRef + BaseExt<Kind> + AsObject<Kind>,
|
|
||||||
{
|
|
||||||
let actor_id = activity
|
|
||||||
.actor()?
|
|
||||||
.to_owned()
|
|
||||||
.single_xsd_any_uri()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let object_id = activity
|
|
||||||
.object()
|
|
||||||
.as_one()
|
|
||||||
.map(|o| o.id())
|
|
||||||
.flatten()
|
|
||||||
.context(location_info!())?;
|
|
||||||
let original_id = match find_object_by_id(context, object_id.to_owned()).await? {
|
|
||||||
Object::Post(p) => p.ap_id.into_inner(),
|
|
||||||
Object::Comment(c) => c.ap_id.into_inner(),
|
|
||||||
Object::Community(c) => c.actor_id(),
|
|
||||||
Object::Person(p) => p.actor_id(),
|
|
||||||
Object::PrivateMessage(p) => p.ap_id.into_inner(),
|
|
||||||
};
|
|
||||||
if actor_id.domain() != original_id.domain() {
|
|
||||||
let community = extract_community_from_cc(activity, context).await?;
|
|
||||||
verify_mod_activity(activity, announce.to_owned(), &community, context).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn verify_undo_remove_actor_instance<T, Kind>(
|
|
||||||
undo: &Undo,
|
|
||||||
inner: &T,
|
|
||||||
announce: &Option<Announce>,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>
|
|
||||||
where
|
|
||||||
T: ActorAndObjectRef + BaseExt<Kind> + AsObject<Kind>,
|
|
||||||
{
|
|
||||||
if announce.is_none() {
|
|
||||||
let community = extract_community_from_cc(undo, context).await?;
|
|
||||||
verify_mod_activity(undo, announce.to_owned(), &community, context).await?;
|
|
||||||
verify_mod_activity(inner, announce.to_owned(), &community, context).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
|
@ -36,7 +36,6 @@ pub enum ValidTypes {
|
||||||
Undo,
|
Undo,
|
||||||
Remove,
|
Remove,
|
||||||
Announce,
|
Announce,
|
||||||
Add,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this isnt entirely correct, cause some of these receive are not ActorAndObject,
|
// TODO: this isnt entirely correct, cause some of these receive are not ActorAndObject,
|
||||||
|
|
|
@ -17,7 +17,7 @@ use crate::extensions::{
|
||||||
};
|
};
|
||||||
use activitystreams::{
|
use activitystreams::{
|
||||||
activity::Follow,
|
activity::Follow,
|
||||||
actor,
|
actor::{ApActor, Group, Person},
|
||||||
base::AnyBase,
|
base::AnyBase,
|
||||||
object::{ApObject, Note, Page},
|
object::{ApObject, Note, Page},
|
||||||
};
|
};
|
||||||
|
@ -31,7 +31,7 @@ use lemmy_db_schema::{
|
||||||
activity::Activity,
|
activity::Activity,
|
||||||
comment::Comment,
|
comment::Comment,
|
||||||
community::Community,
|
community::Community,
|
||||||
person::{Person as DbPerson, Person},
|
person::Person as DbPerson,
|
||||||
post::Post,
|
post::Post,
|
||||||
private_message::PrivateMessage,
|
private_message::PrivateMessage,
|
||||||
},
|
},
|
||||||
|
@ -44,9 +44,9 @@ use std::net::IpAddr;
|
||||||
use url::{ParseError, Url};
|
use url::{ParseError, Url};
|
||||||
|
|
||||||
/// Activitystreams type for community
|
/// Activitystreams type for community
|
||||||
type GroupExt = Ext2<actor::ApActor<ApObject<actor::Group>>, GroupExtension, PublicKeyExtension>;
|
type GroupExt = Ext2<ApActor<ApObject<Group>>, GroupExtension, PublicKeyExtension>;
|
||||||
/// Activitystreams type for person
|
/// Activitystreams type for person
|
||||||
type PersonExt = Ext1<actor::ApActor<ApObject<actor::Person>>, PublicKeyExtension>;
|
type PersonExt = Ext1<ApActor<ApObject<Person>>, PublicKeyExtension>;
|
||||||
/// Activitystreams type for post
|
/// Activitystreams type for post
|
||||||
type PageExt = Ext1<ApObject<Page>, PageExtension>;
|
type PageExt = Ext1<ApObject<Page>, PageExtension>;
|
||||||
type NoteExt = ApObject<Note>;
|
type NoteExt = ApObject<Note>;
|
||||||
|
@ -166,6 +166,38 @@ pub trait ActorType {
|
||||||
fn public_key(&self) -> Option<String>;
|
fn public_key(&self) -> Option<String>;
|
||||||
fn private_key(&self) -> Option<String>;
|
fn private_key(&self) -> Option<String>;
|
||||||
|
|
||||||
|
async fn send_follow(
|
||||||
|
&self,
|
||||||
|
follow_actor_id: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError>;
|
||||||
|
async fn send_unfollow(
|
||||||
|
&self,
|
||||||
|
follow_actor_id: &Url,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
|
async fn send_accept_follow(
|
||||||
|
&self,
|
||||||
|
follow: Follow,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
|
async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
|
async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
|
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
|
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
|
async fn send_announce(
|
||||||
|
&self,
|
||||||
|
activity: AnyBase,
|
||||||
|
context: &LemmyContext,
|
||||||
|
) -> Result<(), LemmyError>;
|
||||||
|
|
||||||
|
/// For a given community, returns the inboxes of all followers.
|
||||||
|
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
|
||||||
|
|
||||||
fn get_shared_inbox_or_inbox_url(&self) -> Url;
|
fn get_shared_inbox_or_inbox_url(&self) -> Url;
|
||||||
|
|
||||||
/// Outbox URL is not generally used by Lemmy, so it can be generated on the fly (but only for
|
/// Outbox URL is not generally used by Lemmy, so it can be generated on the fly (but only for
|
||||||
|
@ -189,55 +221,6 @@ pub trait ActorType {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
pub trait CommunityType {
|
|
||||||
async fn get_follower_inboxes(&self, pool: &DbPool) -> Result<Vec<Url>, LemmyError>;
|
|
||||||
async fn send_accept_follow(
|
|
||||||
&self,
|
|
||||||
follow: Follow,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
|
|
||||||
async fn send_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
|
||||||
async fn send_undo_delete(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
|
||||||
|
|
||||||
async fn send_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
|
||||||
async fn send_undo_remove(&self, context: &LemmyContext) -> Result<(), LemmyError>;
|
|
||||||
|
|
||||||
async fn send_announce(
|
|
||||||
&self,
|
|
||||||
activity: AnyBase,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
|
|
||||||
async fn send_add_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
added_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
async fn send_remove_mod(
|
|
||||||
&self,
|
|
||||||
actor: &Person,
|
|
||||||
removed_mod: Person,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
pub trait UserType {
|
|
||||||
async fn send_follow(
|
|
||||||
&self,
|
|
||||||
follow_actor_id: &Url,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
async fn send_unfollow(
|
|
||||||
&self,
|
|
||||||
follow_actor_id: &Url,
|
|
||||||
context: &LemmyContext,
|
|
||||||
) -> Result<(), LemmyError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub enum EndpointType {
|
pub enum EndpointType {
|
||||||
Community,
|
Community,
|
||||||
Person,
|
Person,
|
||||||
|
@ -293,10 +276,6 @@ pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError>
|
||||||
Ok(Url::parse(&url)?.into())
|
Ok(Url::parse(&url)?.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn generate_moderators_url(community_id: &DbUrl) -> Result<DbUrl, LemmyError> {
|
|
||||||
Ok(Url::parse(&format!("{}/moderators", community_id))?.into())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Store a sent or received activity in the database, for logging purposes. These records are not
|
/// Store a sent or received activity in the database, for logging purposes. These records are not
|
||||||
/// persistent.
|
/// persistent.
|
||||||
pub(crate) async fn insert_activity<T>(
|
pub(crate) async fn insert_activity<T>(
|
||||||
|
@ -350,7 +329,6 @@ pub(crate) async fn find_post_or_comment_by_id(
|
||||||
Err(NotFound.into())
|
Err(NotFound.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) enum Object {
|
pub(crate) enum Object {
|
||||||
Comment(Box<Comment>),
|
Comment(Box<Comment>),
|
||||||
Post(Box<Post>),
|
Post(Box<Post>),
|
||||||
|
|
|
@ -102,16 +102,9 @@ impl FromApub for Comment {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<Comment, LemmyError> {
|
) -> Result<Comment, LemmyError> {
|
||||||
let comment: Comment = get_object_from_apub(
|
let comment: Comment =
|
||||||
note,
|
get_object_from_apub(note, context, expected_domain, request_counter).await?;
|
||||||
context,
|
|
||||||
expected_domain,
|
|
||||||
request_counter,
|
|
||||||
mod_action_allowed,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_id = comment.post_id;
|
let post_id = comment.post_id;
|
||||||
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
|
||||||
|
@ -138,7 +131,6 @@ impl FromApubToForm<NoteExt> for CommentForm {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
_mod_action_allowed: bool,
|
|
||||||
) -> Result<CommentForm, LemmyError> {
|
) -> Result<CommentForm, LemmyError> {
|
||||||
let creator_actor_id = ¬e
|
let creator_actor_id = ¬e
|
||||||
.attributed_to()
|
.attributed_to()
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
extensions::{context::lemmy_context, group_extensions::GroupExtension},
|
extensions::{context::lemmy_context, group_extensions::GroupExtension},
|
||||||
fetcher::{community::fetch_community_mods, person::get_or_fetch_and_upsert_person},
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
generate_moderators_url,
|
|
||||||
objects::{
|
objects::{
|
||||||
check_object_domain,
|
check_object_domain,
|
||||||
create_tombstone,
|
create_tombstone,
|
||||||
|
@ -24,11 +23,10 @@ use activitystreams::{
|
||||||
use activitystreams_ext::Ext2;
|
use activitystreams_ext::Ext2;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use lemmy_api_structs::blocking;
|
use lemmy_api_structs::blocking;
|
||||||
use lemmy_db_queries::{DbPool, Joinable};
|
use lemmy_db_queries::DbPool;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
naive_now,
|
naive_now,
|
||||||
source::community::{Community, CommunityForm, CommunityModerator, CommunityModeratorForm},
|
source::community::{Community, CommunityForm},
|
||||||
DbUrl,
|
|
||||||
};
|
};
|
||||||
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
|
use lemmy_db_views_actor::community_moderator_view::CommunityModeratorView;
|
||||||
use lemmy_utils::{
|
use lemmy_utils::{
|
||||||
|
@ -43,13 +41,28 @@ use url::Url;
|
||||||
impl ToApub for Community {
|
impl ToApub for Community {
|
||||||
type ApubType = GroupExt;
|
type ApubType = GroupExt;
|
||||||
|
|
||||||
async fn to_apub(&self, _pool: &DbPool) -> Result<GroupExt, LemmyError> {
|
async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
|
||||||
|
// The attributed to, is an ordered vector with the creator actor_ids first,
|
||||||
|
// then the rest of the moderators
|
||||||
|
// TODO Technically the instance admins can mod the community, but lets
|
||||||
|
// ignore that for now
|
||||||
|
let id = self.id;
|
||||||
|
let moderators = blocking(pool, move |conn| {
|
||||||
|
CommunityModeratorView::for_community(&conn, id)
|
||||||
|
})
|
||||||
|
.await??;
|
||||||
|
let moderators: Vec<Url> = moderators
|
||||||
|
.into_iter()
|
||||||
|
.map(|m| m.moderator.actor_id.into_inner())
|
||||||
|
.collect();
|
||||||
|
|
||||||
let mut group = ApObject::new(Group::new());
|
let mut group = ApObject::new(Group::new());
|
||||||
group
|
group
|
||||||
.set_many_contexts(lemmy_context()?)
|
.set_many_contexts(lemmy_context()?)
|
||||||
.set_id(self.actor_id.to_owned().into())
|
.set_id(self.actor_id.to_owned().into())
|
||||||
.set_name(self.title.to_owned())
|
.set_name(self.title.to_owned())
|
||||||
.set_published(convert_datetime(self.published));
|
.set_published(convert_datetime(self.published))
|
||||||
|
.set_many_attributed_tos(moderators);
|
||||||
|
|
||||||
if let Some(u) = self.updated.to_owned() {
|
if let Some(u) = self.updated.to_owned() {
|
||||||
group.set_updated(convert_datetime(u));
|
group.set_updated(convert_datetime(u));
|
||||||
|
@ -82,7 +95,7 @@ impl ToApub for Community {
|
||||||
|
|
||||||
Ok(Ext2::new(
|
Ok(Ext2::new(
|
||||||
ap_actor,
|
ap_actor,
|
||||||
GroupExtension::new(self.nsfw, generate_moderators_url(&self.actor_id)?.into())?,
|
GroupExtension::new(self.nsfw)?,
|
||||||
self.get_public_key_ext()?,
|
self.get_public_key_ext()?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
@ -101,64 +114,14 @@ impl ToApub for Community {
|
||||||
impl FromApub for Community {
|
impl FromApub for Community {
|
||||||
type ApubType = GroupExt;
|
type ApubType = GroupExt;
|
||||||
|
|
||||||
/// Converts a `Group` to `Community`, inserts it into the database and updates moderators.
|
/// Converts a `Group` to `Community`.
|
||||||
async fn from_apub(
|
async fn from_apub(
|
||||||
group: &GroupExt,
|
group: &GroupExt,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<Community, LemmyError> {
|
) -> Result<Community, LemmyError> {
|
||||||
let community: Community = get_object_from_apub(
|
get_object_from_apub(group, context, expected_domain, request_counter).await
|
||||||
group,
|
|
||||||
context,
|
|
||||||
expected_domain,
|
|
||||||
request_counter,
|
|
||||||
mod_action_allowed,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let new_moderators = fetch_community_mods(context, group, request_counter).await?;
|
|
||||||
let community_id = community.id;
|
|
||||||
let current_moderators = blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModeratorView::for_community(&conn, community_id)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
// Remove old mods from database which arent in the moderators collection anymore
|
|
||||||
for mod_user in ¤t_moderators {
|
|
||||||
if !new_moderators.contains(&&mod_user.moderator.actor_id.clone().into()) {
|
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
|
||||||
community_id: mod_user.community.id,
|
|
||||||
person_id: mod_user.moderator.id,
|
|
||||||
};
|
|
||||||
blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModerator::leave(conn, &community_moderator_form)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add new mods to database which have been added to moderators collection
|
|
||||||
for mod_uri in new_moderators {
|
|
||||||
let mod_user = get_or_fetch_and_upsert_person(&mod_uri, context, request_counter).await?;
|
|
||||||
let current_mod_uris: Vec<DbUrl> = current_moderators
|
|
||||||
.clone()
|
|
||||||
.iter()
|
|
||||||
.map(|c| c.moderator.actor_id.clone())
|
|
||||||
.collect();
|
|
||||||
if !current_mod_uris.contains(&mod_user.actor_id) {
|
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
|
||||||
community_id: community.id,
|
|
||||||
person_id: mod_user.id,
|
|
||||||
};
|
|
||||||
blocking(context.pool(), move |conn| {
|
|
||||||
CommunityModerator::join(conn, &community_moderator_form)
|
|
||||||
})
|
|
||||||
.await??;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(community)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,10 +132,16 @@ impl FromApubToForm<GroupExt> for CommunityForm {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
_mod_action_allowed: bool,
|
|
||||||
) -> Result<Self, LemmyError> {
|
) -> Result<Self, LemmyError> {
|
||||||
let moderator_uris = fetch_community_mods(context, group, request_counter).await?;
|
let creator_and_moderator_uris = group.inner.attributed_to().context(location_info!())?;
|
||||||
let creator_uri = moderator_uris.first().context(location_info!())?;
|
let creator_uri = creator_and_moderator_uris
|
||||||
|
.as_many()
|
||||||
|
.context(location_info!())?
|
||||||
|
.iter()
|
||||||
|
.next()
|
||||||
|
.context(location_info!())?
|
||||||
|
.as_xsd_any_uri()
|
||||||
|
.context(location_info!())?;
|
||||||
|
|
||||||
let creator = get_or_fetch_and_upsert_person(creator_uri, context, request_counter).await?;
|
let creator = get_or_fetch_and_upsert_person(creator_uri, context, request_counter).await?;
|
||||||
let name = group
|
let name = group
|
||||||
|
|
|
@ -45,14 +45,12 @@ pub(crate) trait FromApub {
|
||||||
///
|
///
|
||||||
/// * `apub` The object to read from
|
/// * `apub` The object to read from
|
||||||
/// * `context` LemmyContext which holds DB pool, HTTP client etc
|
/// * `context` LemmyContext which holds DB pool, HTTP client etc
|
||||||
/// * `expected_domain` Domain where the object was received from. None in case of mod action.
|
/// * `expected_domain` Domain where the object was received from
|
||||||
/// * `mod_action_allowed` True if the object can be a mod activity, ignore `expected_domain` in this case
|
|
||||||
async fn from_apub(
|
async fn from_apub(
|
||||||
apub: &Self::ApubType,
|
apub: &Self::ApubType,
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<Self, LemmyError>
|
) -> Result<Self, LemmyError>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
@ -65,7 +63,6 @@ pub(in crate::objects) trait FromApubToForm<ApubType> {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<Self, LemmyError>
|
) -> Result<Self, LemmyError>
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
@ -135,17 +132,19 @@ where
|
||||||
{
|
{
|
||||||
let content = object
|
let content = object
|
||||||
.content()
|
.content()
|
||||||
.map(|s| s.as_single_xsd_string().map(|s2| s2.to_string()))
|
.map(|s| s.as_single_xsd_string())
|
||||||
.flatten();
|
.flatten()
|
||||||
|
.map(|s| s.to_string());
|
||||||
if content.is_some() {
|
if content.is_some() {
|
||||||
let source = object.source().context(location_info!())?;
|
let source = object.source().context(location_info!())?;
|
||||||
let source = Object::<()>::from_any_base(source.to_owned())?.context(location_info!())?;
|
let source = Object::<()>::from_any_base(source.to_owned())?.context(location_info!())?;
|
||||||
check_is_markdown(source.media_type())?;
|
check_is_markdown(source.media_type())?;
|
||||||
let source_content = source
|
let source_content = source
|
||||||
.content()
|
.content()
|
||||||
.map(|s| s.as_single_xsd_string().map(|s2| s2.to_string()))
|
.map(|s| s.as_single_xsd_string())
|
||||||
.flatten()
|
.flatten()
|
||||||
.context(location_info!())?;
|
.context(location_info!())?
|
||||||
|
.to_string();
|
||||||
return Ok(Some(source_content));
|
return Ok(Some(source_content));
|
||||||
}
|
}
|
||||||
Ok(None)
|
Ok(None)
|
||||||
|
@ -178,7 +177,6 @@ pub(in crate::objects) async fn get_object_from_apub<From, Kind, To, ToForm, IdT
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
is_mod_action: bool,
|
|
||||||
) -> Result<To, LemmyError>
|
) -> Result<To, LemmyError>
|
||||||
where
|
where
|
||||||
From: BaseExt<Kind>,
|
From: BaseExt<Kind>,
|
||||||
|
@ -198,14 +196,7 @@ where
|
||||||
}
|
}
|
||||||
// otherwise parse and insert, assuring that it comes from the right domain
|
// otherwise parse and insert, assuring that it comes from the right domain
|
||||||
else {
|
else {
|
||||||
let to_form = ToForm::from_apub(
|
let to_form = ToForm::from_apub(&from, context, expected_domain, request_counter).await?;
|
||||||
&from,
|
|
||||||
context,
|
|
||||||
expected_domain,
|
|
||||||
request_counter,
|
|
||||||
is_mod_action,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let to = blocking(context.pool(), move |conn| To::upsert(conn, &to_form)).await??;
|
let to = blocking(context.pool(), move |conn| To::upsert(conn, &to_form)).await??;
|
||||||
Ok(to)
|
Ok(to)
|
||||||
|
|
|
@ -93,7 +93,6 @@ impl FromApub for DbPerson {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<DbPerson, LemmyError> {
|
) -> Result<DbPerson, LemmyError> {
|
||||||
let person_id = person.id_unchecked().context(location_info!())?.to_owned();
|
let person_id = person.id_unchecked().context(location_info!())?.to_owned();
|
||||||
let domain = person_id.domain().context(location_info!())?;
|
let domain = person_id.domain().context(location_info!())?;
|
||||||
|
@ -104,14 +103,8 @@ impl FromApub for DbPerson {
|
||||||
.await??;
|
.await??;
|
||||||
Ok(person)
|
Ok(person)
|
||||||
} else {
|
} else {
|
||||||
let person_form = PersonForm::from_apub(
|
let person_form =
|
||||||
person,
|
PersonForm::from_apub(person, context, expected_domain, request_counter).await?;
|
||||||
context,
|
|
||||||
expected_domain,
|
|
||||||
request_counter,
|
|
||||||
mod_action_allowed,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
let person = blocking(context.pool(), move |conn| {
|
let person = blocking(context.pool(), move |conn| {
|
||||||
DbPerson::upsert(conn, &person_form)
|
DbPerson::upsert(conn, &person_form)
|
||||||
})
|
})
|
||||||
|
@ -128,7 +121,6 @@ impl FromApubToForm<PersonExt> for PersonForm {
|
||||||
_context: &LemmyContext,
|
_context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
_request_counter: &mut i32,
|
_request_counter: &mut i32,
|
||||||
_mod_action_allowed: bool,
|
|
||||||
) -> Result<Self, LemmyError> {
|
) -> Result<Self, LemmyError> {
|
||||||
let avatar = match person.icon() {
|
let avatar = match person.icon() {
|
||||||
Some(any_image) => Some(
|
Some(any_image) => Some(
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
check_is_apub_id_valid,
|
|
||||||
extensions::{context::lemmy_context, page_extension::PageExtension},
|
extensions::{context::lemmy_context, page_extension::PageExtension},
|
||||||
fetcher::person::get_or_fetch_and_upsert_person,
|
fetcher::person::get_or_fetch_and_upsert_person,
|
||||||
objects::{
|
objects::{
|
||||||
|
@ -118,16 +117,8 @@ impl FromApub for Post {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<Post, LemmyError> {
|
) -> Result<Post, LemmyError> {
|
||||||
let post: Post = get_object_from_apub(
|
let post: Post = get_object_from_apub(page, context, expected_domain, request_counter).await?;
|
||||||
page,
|
|
||||||
context,
|
|
||||||
expected_domain,
|
|
||||||
request_counter,
|
|
||||||
mod_action_allowed,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
check_object_for_community_or_site_ban(page, post.community_id, context, request_counter)
|
check_object_for_community_or_site_ban(page, post.community_id, context, request_counter)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(post)
|
Ok(post)
|
||||||
|
@ -141,15 +132,7 @@ impl FromApubToForm<PageExt> for PostForm {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<PostForm, LemmyError> {
|
) -> Result<PostForm, LemmyError> {
|
||||||
let ap_id = if mod_action_allowed {
|
|
||||||
let id = page.id_unchecked().context(location_info!())?;
|
|
||||||
check_is_apub_id_valid(id)?;
|
|
||||||
id.to_owned().into()
|
|
||||||
} else {
|
|
||||||
check_object_domain(page, expected_domain)?
|
|
||||||
};
|
|
||||||
let ext = &page.ext_one;
|
let ext = &page.ext_one;
|
||||||
let creator_actor_id = page
|
let creator_actor_id = page
|
||||||
.inner
|
.inner
|
||||||
|
@ -196,20 +179,16 @@ impl FromApubToForm<PageExt> for PostForm {
|
||||||
let name = page
|
let name = page
|
||||||
.inner
|
.inner
|
||||||
.name()
|
.name()
|
||||||
|
.map(|s| s.map(|s2| s2.to_owned()))
|
||||||
// The following is for compatibility with lemmy v0.9.9 and older
|
// The following is for compatibility with lemmy v0.9.9 and older
|
||||||
// TODO: remove it after some time (along with the map above)
|
// TODO: remove it after some time (along with the map above)
|
||||||
.or_else(|| page.inner.summary())
|
.or_else(|| page.inner.summary().map(|s| s.to_owned()))
|
||||||
.context(location_info!())?
|
.context(location_info!())?
|
||||||
.as_single_xsd_string()
|
.as_single_xsd_string()
|
||||||
.context(location_info!())?
|
.context(location_info!())?
|
||||||
.to_string();
|
.to_string();
|
||||||
let body = get_source_markdown_value(page)?;
|
let body = get_source_markdown_value(page)?;
|
||||||
|
|
||||||
// TODO: expected_domain is wrong in this case, because it simply takes the domain of the actor
|
|
||||||
// maybe we need to take id_unchecked() if the activity is from community to user?
|
|
||||||
// why did this work before? -> i dont think it did?
|
|
||||||
// -> try to make expected_domain optional and set it null if it is a mod action
|
|
||||||
|
|
||||||
check_slurs(&name)?;
|
check_slurs(&name)?;
|
||||||
let body_slurs_removed = body.map(|b| remove_slurs(&b));
|
let body_slurs_removed = body.map(|b| remove_slurs(&b));
|
||||||
Ok(PostForm {
|
Ok(PostForm {
|
||||||
|
@ -237,7 +216,7 @@ impl FromApubToForm<PageExt> for PostForm {
|
||||||
embed_description: iframely_description,
|
embed_description: iframely_description,
|
||||||
embed_html: iframely_html,
|
embed_html: iframely_html,
|
||||||
thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
|
thumbnail_url: pictrs_thumbnail.map(|u| u.into()),
|
||||||
ap_id: Some(ap_id),
|
ap_id: Some(check_object_domain(page, expected_domain)?),
|
||||||
local: false,
|
local: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -77,16 +77,8 @@ impl FromApub for PrivateMessage {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
mod_action_allowed: bool,
|
|
||||||
) -> Result<PrivateMessage, LemmyError> {
|
) -> Result<PrivateMessage, LemmyError> {
|
||||||
get_object_from_apub(
|
get_object_from_apub(note, context, expected_domain, request_counter).await
|
||||||
note,
|
|
||||||
context,
|
|
||||||
expected_domain,
|
|
||||||
request_counter,
|
|
||||||
mod_action_allowed,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -97,7 +89,6 @@ impl FromApubToForm<NoteExt> for PrivateMessageForm {
|
||||||
context: &LemmyContext,
|
context: &LemmyContext,
|
||||||
expected_domain: Url,
|
expected_domain: Url,
|
||||||
request_counter: &mut i32,
|
request_counter: &mut i32,
|
||||||
_mod_action_allowed: bool,
|
|
||||||
) -> Result<PrivateMessageForm, LemmyError> {
|
) -> Result<PrivateMessageForm, LemmyError> {
|
||||||
let creator_actor_id = note
|
let creator_actor_id = note
|
||||||
.attributed_to()
|
.attributed_to()
|
||||||
|
|
|
@ -5,7 +5,6 @@ use crate::{
|
||||||
get_apub_community_followers,
|
get_apub_community_followers,
|
||||||
get_apub_community_http,
|
get_apub_community_http,
|
||||||
get_apub_community_inbox,
|
get_apub_community_inbox,
|
||||||
get_apub_community_moderators,
|
|
||||||
get_apub_community_outbox,
|
get_apub_community_outbox,
|
||||||
},
|
},
|
||||||
get_activity,
|
get_activity,
|
||||||
|
@ -58,10 +57,6 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||||
"/c/{community_name}/inbox",
|
"/c/{community_name}/inbox",
|
||||||
web::get().to(get_apub_community_inbox),
|
web::get().to(get_apub_community_inbox),
|
||||||
)
|
)
|
||||||
.route(
|
|
||||||
"/c/{community_name}/moderators",
|
|
||||||
web::get().to(get_apub_community_moderators),
|
|
||||||
)
|
|
||||||
.route("/u/{user_name}", web::get().to(get_apub_person_http))
|
.route("/u/{user_name}", web::get().to(get_apub_person_http))
|
||||||
.route(
|
.route(
|
||||||
"/u/{user_name}/outbox",
|
"/u/{user_name}/outbox",
|
||||||
|
|
|
@ -2,26 +2,13 @@ use crate::Crud;
|
||||||
use bcrypt::{hash, DEFAULT_COST};
|
use bcrypt::{hash, DEFAULT_COST};
|
||||||
use diesel::{dsl::*, result::Error, *};
|
use diesel::{dsl::*, result::Error, *};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
naive_now,
|
||||||
schema::local_user::dsl::*,
|
schema::local_user::dsl::*,
|
||||||
source::local_user::{LocalUser, LocalUserForm},
|
source::local_user::{LocalUser, LocalUserForm},
|
||||||
LocalUserId,
|
LocalUserId,
|
||||||
PersonId,
|
PersonId,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod safe_type {
|
|
||||||
use crate::ToSafe;
|
|
||||||
use lemmy_db_schema::{schema::local_user::columns::*, source::local_user::LocalUser};
|
|
||||||
|
|
||||||
type Columns = (id, person_id, admin, matrix_user_id);
|
|
||||||
|
|
||||||
impl ToSafe for LocalUser {
|
|
||||||
type SafeColumns = Columns;
|
|
||||||
fn safe_columns_tuple() -> Self::SafeColumns {
|
|
||||||
(id, person_id, admin, matrix_user_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mod safe_settings_type {
|
mod safe_settings_type {
|
||||||
use crate::ToSafeSettings;
|
use crate::ToSafeSettings;
|
||||||
use lemmy_db_schema::{schema::local_user::columns::*, source::local_user::LocalUser};
|
use lemmy_db_schema::{schema::local_user::columns::*, source::local_user::LocalUser};
|
||||||
|
@ -39,6 +26,7 @@ mod safe_settings_type {
|
||||||
show_avatars,
|
show_avatars,
|
||||||
send_notifications_to_email,
|
send_notifications_to_email,
|
||||||
matrix_user_id,
|
matrix_user_id,
|
||||||
|
validator_time,
|
||||||
);
|
);
|
||||||
|
|
||||||
impl ToSafeSettings for LocalUser {
|
impl ToSafeSettings for LocalUser {
|
||||||
|
@ -59,6 +47,7 @@ mod safe_settings_type {
|
||||||
show_avatars,
|
show_avatars,
|
||||||
send_notifications_to_email,
|
send_notifications_to_email,
|
||||||
matrix_user_id,
|
matrix_user_id,
|
||||||
|
validator_time,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,7 +81,10 @@ impl LocalUser_ for LocalUser {
|
||||||
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
|
let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
|
||||||
|
|
||||||
diesel::update(local_user.find(local_user_id))
|
diesel::update(local_user.find(local_user_id))
|
||||||
.set((password_encrypted.eq(password_hash),))
|
.set((
|
||||||
|
password_encrypted.eq(password_hash),
|
||||||
|
validator_time.eq(naive_now()),
|
||||||
|
))
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@ table! {
|
||||||
show_avatars -> Bool,
|
show_avatars -> Bool,
|
||||||
send_notifications_to_email -> Bool,
|
send_notifications_to_email -> Bool,
|
||||||
matrix_user_id -> Nullable<Text>,
|
matrix_user_id -> Nullable<Text>,
|
||||||
|
validator_time -> Timestamp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ pub struct LocalUser {
|
||||||
pub show_avatars: bool,
|
pub show_avatars: bool,
|
||||||
pub send_notifications_to_email: bool,
|
pub send_notifications_to_email: bool,
|
||||||
pub matrix_user_id: Option<String>,
|
pub matrix_user_id: Option<String>,
|
||||||
|
pub validator_time: chrono::NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO redo these, check table defaults
|
// TODO redo these, check table defaults
|
||||||
|
@ -53,4 +54,5 @@ pub struct LocalUserSettings {
|
||||||
pub show_avatars: bool,
|
pub show_avatars: bool,
|
||||||
pub send_notifications_to_email: bool,
|
pub send_notifications_to_email: bool,
|
||||||
pub matrix_user_id: Option<String>,
|
pub matrix_user_id: Option<String>,
|
||||||
|
pub validator_time: chrono::NaiveDateTime,
|
||||||
}
|
}
|
||||||
|
|
|
@ -227,7 +227,7 @@ fn get_feed_front(
|
||||||
jwt: String,
|
jwt: String,
|
||||||
) -> Result<ChannelBuilder, LemmyError> {
|
) -> Result<ChannelBuilder, LemmyError> {
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
let local_user_id = LocalUserId(Claims::decode(&jwt)?.claims.local_user_id);
|
let local_user_id = LocalUserId(Claims::decode(&jwt)?.claims.sub);
|
||||||
let person_id = LocalUser::read(&conn, local_user_id)?.person_id;
|
let person_id = LocalUser::read(&conn, local_user_id)?.person_id;
|
||||||
|
|
||||||
let posts = PostQueryBuilder::create(&conn)
|
let posts = PostQueryBuilder::create(&conn)
|
||||||
|
@ -254,7 +254,7 @@ fn get_feed_front(
|
||||||
|
|
||||||
fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, LemmyError> {
|
fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, LemmyError> {
|
||||||
let site_view = SiteView::read(&conn)?;
|
let site_view = SiteView::read(&conn)?;
|
||||||
let local_user_id = LocalUserId(Claims::decode(&jwt)?.claims.local_user_id);
|
let local_user_id = LocalUserId(Claims::decode(&jwt)?.claims.sub);
|
||||||
let person_id = LocalUser::read(&conn, local_user_id)?.person_id;
|
let person_id = LocalUser::read(&conn, local_user_id)?.person_id;
|
||||||
|
|
||||||
let sort = SortType::New;
|
let sort = SortType::New;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::settings::structs::Settings;
|
use crate::settings::structs::Settings;
|
||||||
|
use chrono::Utc;
|
||||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
|
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, TokenData, Validation};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -6,8 +7,11 @@ type Jwt = String;
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct Claims {
|
pub struct Claims {
|
||||||
pub local_user_id: i32,
|
/// local_user_id, standard claim by RFC 7519.
|
||||||
|
pub sub: i32,
|
||||||
pub iss: String,
|
pub iss: String,
|
||||||
|
/// Time when this token was issued as UNIX-timestamp in seconds
|
||||||
|
pub iat: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Claims {
|
impl Claims {
|
||||||
|
@ -23,10 +27,11 @@ impl Claims {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn jwt(local_user_id: i32, hostname: String) -> Result<Jwt, jsonwebtoken::errors::Error> {
|
pub fn jwt(local_user_id: i32) -> Result<Jwt, jsonwebtoken::errors::Error> {
|
||||||
let my_claims = Claims {
|
let my_claims = Claims {
|
||||||
local_user_id,
|
sub: local_user_id,
|
||||||
iss: hostname,
|
iss: Settings::get().hostname(),
|
||||||
|
iat: Utc::now().timestamp(),
|
||||||
};
|
};
|
||||||
encode(
|
encode(
|
||||||
&Header::default(),
|
&Header::default(),
|
||||||
|
|
|
@ -13,6 +13,7 @@ use crate::{
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context};
|
use anyhow::{anyhow, Context};
|
||||||
use deser_hjson::from_str;
|
use deser_hjson::from_str;
|
||||||
|
use log::warn;
|
||||||
use merge::Merge;
|
use merge::Merge;
|
||||||
use std::{env, fs, io::Error, net::IpAddr, sync::RwLock};
|
use std::{env, fs, io::Error, net::IpAddr, sync::RwLock};
|
||||||
|
|
||||||
|
@ -24,7 +25,13 @@ static CONFIG_FILE: &str = "config/config.hjson";
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref SETTINGS: RwLock<Settings> = RwLock::new(match Settings::init() {
|
static ref SETTINGS: RwLock<Settings> = RwLock::new(match Settings::init() {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => panic!("{}", e),
|
Err(e) => {
|
||||||
|
warn!(
|
||||||
|
"Couldn't load settings file, using default settings.\n{}",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
Settings::default()
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
pub const VERSION: &str = "0.10.0-rc.5";
|
pub const VERSION: &str = "0.10.0-rc.7";
|
||||||
|
|
|
@ -17,7 +17,7 @@ services:
|
||||||
- iframely
|
- iframely
|
||||||
|
|
||||||
lemmy-ui:
|
lemmy-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.5
|
image: dessalines/lemmy-ui:0.10.0-rc.7
|
||||||
ports:
|
ports:
|
||||||
- "1235:1234"
|
- "1235:1234"
|
||||||
restart: always
|
restart: always
|
||||||
|
|
|
@ -29,7 +29,7 @@ services:
|
||||||
- ./volumes/pictrs_alpha:/mnt
|
- ./volumes/pictrs_alpha:/mnt
|
||||||
|
|
||||||
lemmy-alpha-ui:
|
lemmy-alpha-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.5
|
image: dessalines/lemmy-ui:0.10.0-rc.7
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-alpha:8541
|
- LEMMY_INTERNAL_HOST=lemmy-alpha:8541
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8541
|
- LEMMY_EXTERNAL_HOST=localhost:8541
|
||||||
|
@ -58,7 +58,7 @@ services:
|
||||||
- ./volumes/postgres_alpha:/var/lib/postgresql/data
|
- ./volumes/postgres_alpha:/var/lib/postgresql/data
|
||||||
|
|
||||||
lemmy-beta-ui:
|
lemmy-beta-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.5
|
image: dessalines/lemmy-ui:0.10.0-rc.7
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-beta:8551
|
- LEMMY_INTERNAL_HOST=lemmy-beta:8551
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8551
|
- LEMMY_EXTERNAL_HOST=localhost:8551
|
||||||
|
@ -87,7 +87,7 @@ services:
|
||||||
- ./volumes/postgres_beta:/var/lib/postgresql/data
|
- ./volumes/postgres_beta:/var/lib/postgresql/data
|
||||||
|
|
||||||
lemmy-gamma-ui:
|
lemmy-gamma-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.5
|
image: dessalines/lemmy-ui:0.10.0-rc.7
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-gamma:8561
|
- LEMMY_INTERNAL_HOST=lemmy-gamma:8561
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8561
|
- LEMMY_EXTERNAL_HOST=localhost:8561
|
||||||
|
@ -117,7 +117,7 @@ services:
|
||||||
|
|
||||||
# An instance with only an allowlist for beta
|
# An instance with only an allowlist for beta
|
||||||
lemmy-delta-ui:
|
lemmy-delta-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.5
|
image: dessalines/lemmy-ui:0.10.0-rc.7
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-delta:8571
|
- LEMMY_INTERNAL_HOST=lemmy-delta:8571
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8571
|
- LEMMY_EXTERNAL_HOST=localhost:8571
|
||||||
|
@ -147,7 +147,7 @@ services:
|
||||||
|
|
||||||
# An instance who has a blocklist, with lemmy-alpha blocked
|
# An instance who has a blocklist, with lemmy-alpha blocked
|
||||||
lemmy-epsilon-ui:
|
lemmy-epsilon-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.5
|
image: dessalines/lemmy-ui:0.10.0-rc.7
|
||||||
environment:
|
environment:
|
||||||
- LEMMY_INTERNAL_HOST=lemmy-epsilon:8581
|
- LEMMY_INTERNAL_HOST=lemmy-epsilon:8581
|
||||||
- LEMMY_EXTERNAL_HOST=localhost:8581
|
- LEMMY_EXTERNAL_HOST=localhost:8581
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
{
|
{
|
||||||
hostname: lemmy-alpha:8541
|
|
||||||
port: 8541
|
port: 8541
|
||||||
tls_enabled: false
|
tls_enabled: false
|
||||||
jwt_secret: changeme
|
jwt_secret: changeme
|
||||||
|
|
|
@ -12,7 +12,7 @@ services:
|
||||||
restart: always
|
restart: always
|
||||||
|
|
||||||
lemmy:
|
lemmy:
|
||||||
image: dessalines/lemmy:0.10.0-rc.5
|
image: dessalines/lemmy:0.10.0-rc.7
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:8536:8536"
|
- "127.0.0.1:8536:8536"
|
||||||
restart: always
|
restart: always
|
||||||
|
@ -26,7 +26,7 @@ services:
|
||||||
- iframely
|
- iframely
|
||||||
|
|
||||||
lemmy-ui:
|
lemmy-ui:
|
||||||
image: dessalines/lemmy-ui:0.10.0-rc.5
|
image: dessalines/lemmy-ui:0.10.0-rc.7
|
||||||
ports:
|
ports:
|
||||||
- "127.0.0.1:1235:1234"
|
- "127.0.0.1:1235:1234"
|
||||||
restart: always
|
restart: always
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
alter table local_user drop column validator_time;
|
|
@ -0,0 +1 @@
|
||||||
|
alter table local_user add column validator_time timestamp not null default now();
|
Loading…
Reference in a new issue