mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-11-22 20:31:19 +00:00
Merge branch 'main' into report-user-home-test
This commit is contained in:
commit
a865aca26a
23 changed files with 517 additions and 191 deletions
30
Cargo.lock
generated
30
Cargo.lock
generated
|
@ -16,9 +16,9 @@ checksum = "8f27d075294830fcab6f66e320dab524bc6d048f4a151698e153205559113772"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "activitypub_federation"
|
name = "activitypub_federation"
|
||||||
version = "0.5.5"
|
version = "0.5.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e54fe65c4a4b57712d8e436f1fb86ff37e10b56f011f4233fbbfa8c669163e9d"
|
checksum = "ac8ff2d0151ce9ac02eb29e4a58b41d28693f141f7963d4bfabd2f9d402ecec7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitystreams-kinds",
|
"activitystreams-kinds",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2663,7 +2663,7 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_api"
|
name = "lemmy_api"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2692,7 +2692,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_api_common"
|
name = "lemmy_api_common"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2730,7 +2730,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_api_crud"
|
name = "lemmy_api_crud"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"accept-language",
|
"accept-language",
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
|
@ -2753,7 +2753,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_apub"
|
name = "lemmy_apub"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2791,7 +2791,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_perf"
|
name = "lemmy_db_perf"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
|
@ -2806,7 +2806,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_schema"
|
name = "lemmy_db_schema"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -2846,7 +2846,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_views"
|
name = "lemmy_db_views"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"chrono",
|
"chrono",
|
||||||
|
@ -2868,7 +2868,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_views_actor"
|
name = "lemmy_db_views_actor"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"chrono",
|
"chrono",
|
||||||
"diesel",
|
"diesel",
|
||||||
|
@ -2888,7 +2888,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_db_views_moderator"
|
name = "lemmy_db_views_moderator"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"diesel",
|
"diesel",
|
||||||
"diesel-async",
|
"diesel-async",
|
||||||
|
@ -2900,7 +2900,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_federate"
|
name = "lemmy_federate"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
@ -2923,7 +2923,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_routes"
|
name = "lemmy_routes"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
@ -2948,7 +2948,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_server"
|
name = "lemmy_server"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"actix-cors",
|
"actix-cors",
|
||||||
|
@ -2991,7 +2991,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lemmy_utils"
|
name = "lemmy_utils"
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|
26
Cargo.toml
26
Cargo.toml
|
@ -1,5 +1,5 @@
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.19.4-beta.6"
|
version = "0.19.4-beta.7"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A link aggregator for the fediverse"
|
description = "A link aggregator for the fediverse"
|
||||||
license = "AGPL-3.0"
|
license = "AGPL-3.0"
|
||||||
|
@ -88,18 +88,18 @@ unused_self = "deny"
|
||||||
unwrap_used = "deny"
|
unwrap_used = "deny"
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
lemmy_api = { version = "=0.19.4-beta.6", path = "./crates/api" }
|
lemmy_api = { version = "=0.19.4-beta.7", path = "./crates/api" }
|
||||||
lemmy_api_crud = { version = "=0.19.4-beta.6", path = "./crates/api_crud" }
|
lemmy_api_crud = { version = "=0.19.4-beta.7", path = "./crates/api_crud" }
|
||||||
lemmy_apub = { version = "=0.19.4-beta.6", path = "./crates/apub" }
|
lemmy_apub = { version = "=0.19.4-beta.7", path = "./crates/apub" }
|
||||||
lemmy_utils = { version = "=0.19.4-beta.6", path = "./crates/utils", default-features = false }
|
lemmy_utils = { version = "=0.19.4-beta.7", path = "./crates/utils", default-features = false }
|
||||||
lemmy_db_schema = { version = "=0.19.4-beta.6", path = "./crates/db_schema" }
|
lemmy_db_schema = { version = "=0.19.4-beta.7", path = "./crates/db_schema" }
|
||||||
lemmy_api_common = { version = "=0.19.4-beta.6", path = "./crates/api_common" }
|
lemmy_api_common = { version = "=0.19.4-beta.7", path = "./crates/api_common" }
|
||||||
lemmy_routes = { version = "=0.19.4-beta.6", path = "./crates/routes" }
|
lemmy_routes = { version = "=0.19.4-beta.7", path = "./crates/routes" }
|
||||||
lemmy_db_views = { version = "=0.19.4-beta.6", path = "./crates/db_views" }
|
lemmy_db_views = { version = "=0.19.4-beta.7", path = "./crates/db_views" }
|
||||||
lemmy_db_views_actor = { version = "=0.19.4-beta.6", path = "./crates/db_views_actor" }
|
lemmy_db_views_actor = { version = "=0.19.4-beta.7", path = "./crates/db_views_actor" }
|
||||||
lemmy_db_views_moderator = { version = "=0.19.4-beta.6", path = "./crates/db_views_moderator" }
|
lemmy_db_views_moderator = { version = "=0.19.4-beta.7", path = "./crates/db_views_moderator" }
|
||||||
lemmy_federate = { version = "=0.19.4-beta.6", path = "./crates/federate" }
|
lemmy_federate = { version = "=0.19.4-beta.7", path = "./crates/federate" }
|
||||||
activitypub_federation = { version = "0.5.5", default-features = false, features = [
|
activitypub_federation = { version = "0.5.6", default-features = false, features = [
|
||||||
"actix-web",
|
"actix-web",
|
||||||
] }
|
] }
|
||||||
diesel = "2.1.6"
|
diesel = "2.1.6"
|
||||||
|
|
|
@ -40,7 +40,7 @@ pub struct Register {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
pub password: Sensitive<String>,
|
pub password: Sensitive<String>,
|
||||||
pub password_verify: Sensitive<String>,
|
pub password_verify: Sensitive<String>,
|
||||||
pub show_nsfw: bool,
|
pub show_nsfw: Option<bool>,
|
||||||
/// email is mandatory if email verification is enabled on the server
|
/// email is mandatory if email verification is enabled on the server
|
||||||
pub email: Option<Sensitive<String>>,
|
pub email: Option<Sensitive<String>>,
|
||||||
/// The UUID of the captcha item.
|
/// The UUID of the captcha item.
|
||||||
|
|
|
@ -142,12 +142,17 @@ pub async fn register(
|
||||||
.map(|lang_str| lang_str.split('-').next().unwrap_or_default().to_string())
|
.map(|lang_str| lang_str.split('-').next().unwrap_or_default().to_string())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
// Show nsfw content if param is true, or if content_warning exists
|
||||||
|
let show_nsfw = data
|
||||||
|
.show_nsfw
|
||||||
|
.unwrap_or(site_view.site.content_warning.is_some());
|
||||||
|
|
||||||
// Create the local user
|
// Create the local user
|
||||||
let local_user_form = LocalUserInsertForm::builder()
|
let local_user_form = LocalUserInsertForm::builder()
|
||||||
.person_id(inserted_person.id)
|
.person_id(inserted_person.id)
|
||||||
.email(data.email.as_deref().map(str::to_lowercase))
|
.email(data.email.as_deref().map(str::to_lowercase))
|
||||||
.password_encrypted(data.password.to_string())
|
.password_encrypted(data.password.to_string())
|
||||||
.show_nsfw(Some(data.show_nsfw))
|
.show_nsfw(Some(show_nsfw))
|
||||||
.accepted_application(accepted_application)
|
.accepted_application(accepted_application)
|
||||||
.default_listing_type(Some(local_site.default_post_listing_type))
|
.default_listing_type(Some(local_site.default_post_listing_type))
|
||||||
.post_listing_mode(Some(local_site.default_post_listing_mode))
|
.post_listing_mode(Some(local_site.default_post_listing_mode))
|
||||||
|
|
49
crates/apub/assets/wordpress/activities/announce.json
Normal file
49
crates/apub/assets/wordpress/activities/announce.json
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
{
|
||||||
|
"@context": ["https://www.w3.org/ns/activitystreams"],
|
||||||
|
"id": "https://pfefferle.org/lemmy-part-4/#activity#activity",
|
||||||
|
"type": "Announce",
|
||||||
|
"audience": "https://pfefferle.org/@pfefferle.org",
|
||||||
|
"published": "2024-05-03T12:32:29Z",
|
||||||
|
"updated": "2024-05-06T08:20:33Z",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://pfefferle.org/wp-json/activitypub/1.0/actors/1/followers"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"object": {
|
||||||
|
"id": "https://pfefferle.org/lemmy-part-4/#activity",
|
||||||
|
"type": "Update",
|
||||||
|
"audience": "https://pfefferle.org/@pfefferle.org",
|
||||||
|
"published": "2024-05-03T12:32:29Z",
|
||||||
|
"updated": "2024-05-06T08:20:33Z",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://pfefferle.org/wp-json/activitypub/1.0/actors/1/followers"
|
||||||
|
],
|
||||||
|
"cc": [],
|
||||||
|
"object": {
|
||||||
|
"id": "https://pfefferle.org/lemmy-part-4/",
|
||||||
|
"type": "Article",
|
||||||
|
"attachment": [],
|
||||||
|
"attributedTo": "https://pfefferle.org/author/pfefferle/",
|
||||||
|
"audience": "https://pfefferle.org/@pfefferle.org",
|
||||||
|
"content": "\u003Cp\u003EIdentifies one or more entities that represent the total population of entities for which the object can considered to be relevant. Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant. \u003C/p\u003E",
|
||||||
|
"contentMap": {
|
||||||
|
"en": "\u003Cp\u003EIdentifies one or more entities that represent the total population of entities for which the object can considered to be relevant. Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant. \u003C/p\u003E"
|
||||||
|
},
|
||||||
|
"name": "Lemmy (Part 4)",
|
||||||
|
"published": "2024-05-03T12:32:29Z",
|
||||||
|
"summary": "Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant. Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object can considered to be relevant.Identifies one or more entities that represent the total population of entities for which the object [...]",
|
||||||
|
"tag": [],
|
||||||
|
"updated": "2024-05-06T08:20:33Z",
|
||||||
|
"url": "https://pfefferle.org/lemmy-part-4/",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://pfefferle.org/wp-json/activitypub/1.0/actors/1/followers"
|
||||||
|
],
|
||||||
|
"cc": []
|
||||||
|
},
|
||||||
|
"actor": "https://pfefferle.org/author/pfefferle/"
|
||||||
|
},
|
||||||
|
"actor": "https://pfefferle.org/@pfefferle.org"
|
||||||
|
}
|
66
crates/apub/assets/wordpress/objects/group.json
Normal file
66
crates/apub/assets/wordpress/objects/group.json
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
"https://purl.archive.org/socialweb/webfinger",
|
||||||
|
{
|
||||||
|
"schema": "http://schema.org#",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"webfinger": "https://webfinger.net/#",
|
||||||
|
"lemmy": "https://join-lemmy.org/ns#",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"PropertyValue": "schema:PropertyValue",
|
||||||
|
"value": "schema:value",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"featured": {
|
||||||
|
"@id": "toot:featured",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"featuredTags": {
|
||||||
|
"@id": "toot:featuredTags",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"moderators": {
|
||||||
|
"@id": "lemmy:moderators",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"postingRestrictedToMods": "lemmy:postingRestrictedToMods",
|
||||||
|
"discoverable": "toot:discoverable",
|
||||||
|
"indexable": "toot:indexable",
|
||||||
|
"resource": "webfinger:resource"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://pfefferle.org/@pfefferle.org",
|
||||||
|
"type": "Group",
|
||||||
|
"attachment": [],
|
||||||
|
"attributedTo": "https://pfefferle.org/wp-json/activitypub/1.0/collections/moderators",
|
||||||
|
"name": "Matthias Pfefferle",
|
||||||
|
"icon": {
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://pfefferle.org/wp-content/uploads/2023/06/cropped-BeLItBV-_400x400.jpg"
|
||||||
|
},
|
||||||
|
"published": "2024-04-03T16:58:22Z",
|
||||||
|
"summary": "<p>Webworker, blogger und podcaster</p>\n",
|
||||||
|
"tag": [],
|
||||||
|
"url": "https://pfefferle.org/@pfefferle.org",
|
||||||
|
"inbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/inbox",
|
||||||
|
"outbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/outbox",
|
||||||
|
"following": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/following",
|
||||||
|
"followers": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/followers",
|
||||||
|
"preferredUsername": "pfefferle.org",
|
||||||
|
"endpoints": {
|
||||||
|
"sharedInbox": "https://pfefferle.org/wp-json/activitypub/1.0/inbox"
|
||||||
|
},
|
||||||
|
"publicKey": {
|
||||||
|
"id": "https://pfefferle.org/@pfefferle.org#main-key",
|
||||||
|
"owner": "https://pfefferle.org/@pfefferle.org",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuq8xeLMFcaCwPFBhgMRE\n/dDh2XKoNXFXnixctmK8BXSuuLMxucm3I/8NyhIvb3LqU+uP1fO8F0ecUbk2sN+x\nKag5vIV6yKXzJ8ILMWQ9AaELpXDmMZqL0zal0LUJRAOkDgPDovDAoq6tx++yDoV0\njdVbf9CoZKit1cz2ZrEuE5dswq3J/z9+c6POkhCkWEX5TPJzkOrmnjkvrXxGHUJ2\nA3+P+VaZhd5cmvqYosSpYNJshxCdev12pIF78OnYLiYiyXlgGHU+7uQR0M4tTcij\n6cUdLkms9m+b6H3ctXntPn410e5YLFPldjAYzQB5wHVdFZsWtyrbqfYdCa+KkKpA\nvwIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"manuallyApprovesFollowers": false,
|
||||||
|
"featured": "https://pfefferle.org/wp-json/activitypub/1.0/users/0/collections/featured",
|
||||||
|
"moderators": "https://pfefferle.org/wp-json/activitypub/1.0/collections/moderators",
|
||||||
|
"discoverable": true,
|
||||||
|
"indexable": true,
|
||||||
|
"webfinger": "pfefferle.org@pfefferle.org",
|
||||||
|
"postingRestrictedToMods": true
|
||||||
|
}
|
24
crates/apub/assets/wordpress/objects/note.json
Normal file
24
crates/apub/assets/wordpress/objects/note.json
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"Hashtag": "as:Hashtag"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://pfefferle.org?c=148",
|
||||||
|
"type": "Note",
|
||||||
|
"attributedTo": "https://pfefferle.org/author/pfefferle/",
|
||||||
|
"content": "<p>Nice! Hello from WordPress!</p>",
|
||||||
|
"contentMap": {
|
||||||
|
"en": "<p>Nice! Hello from WordPress!</p>"
|
||||||
|
},
|
||||||
|
"inReplyTo": "https://socialhub.activitypub.rocks/ap/object/ce040f1ead95964f6dbbf1084b81432d",
|
||||||
|
"published": "2024-04-30T15:21:13Z",
|
||||||
|
"tag": [],
|
||||||
|
"url": "https://pfefferle.org?c=148",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://pfefferle.org/wp-json/activitypub/1.0/users/0/followers"
|
||||||
|
],
|
||||||
|
"cc": []
|
||||||
|
}
|
26
crates/apub/assets/wordpress/objects/page.json
Normal file
26
crates/apub/assets/wordpress/objects/page.json
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
{
|
||||||
|
"Hashtag": "as:Hashtag"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://pfefferle.org/this-is-a-test-federation/",
|
||||||
|
"type": "Article",
|
||||||
|
"attachment": [],
|
||||||
|
"attributedTo": "https://pfefferle.org/author/pfefferle/",
|
||||||
|
"content": "<p>with Discource!</p>",
|
||||||
|
"contentMap": {
|
||||||
|
"en": "<p>with Discource!</p>"
|
||||||
|
},
|
||||||
|
"name": "This is a test-federation",
|
||||||
|
"published": "2024-04-30T15:16:41Z",
|
||||||
|
"summary": "with Discource! [...]",
|
||||||
|
"tag": [],
|
||||||
|
"url": "https://pfefferle.org/this-is-a-test-federation/",
|
||||||
|
"to": [
|
||||||
|
"https://www.w3.org/ns/activitystreams#Public",
|
||||||
|
"https://pfefferle.org/wp-json/activitypub/1.0/users/1/followers"
|
||||||
|
],
|
||||||
|
"cc": []
|
||||||
|
}
|
74
crates/apub/assets/wordpress/objects/person.json
Normal file
74
crates/apub/assets/wordpress/objects/person.json
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
{
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/activitystreams",
|
||||||
|
"https://w3id.org/security/v1",
|
||||||
|
"https://purl.archive.org/socialweb/webfinger",
|
||||||
|
{
|
||||||
|
"schema": "http://schema.org#",
|
||||||
|
"toot": "http://joinmastodon.org/ns#",
|
||||||
|
"webfinger": "https://webfinger.net/#",
|
||||||
|
"lemmy": "https://join-lemmy.org/ns#",
|
||||||
|
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
|
||||||
|
"PropertyValue": "schema:PropertyValue",
|
||||||
|
"value": "schema:value",
|
||||||
|
"Hashtag": "as:Hashtag",
|
||||||
|
"featured": {
|
||||||
|
"@id": "toot:featured",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"featuredTags": {
|
||||||
|
"@id": "toot:featuredTags",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"moderators": {
|
||||||
|
"@id": "lemmy:moderators",
|
||||||
|
"@type": "@id"
|
||||||
|
},
|
||||||
|
"postingRestrictedToMods": "lemmy:postingRestrictedToMods",
|
||||||
|
"discoverable": "toot:discoverable",
|
||||||
|
"indexable": "toot:indexable",
|
||||||
|
"resource": "webfinger:resource"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"id": "https://pfefferle.org/author/pfefferle/",
|
||||||
|
"type": "Person",
|
||||||
|
"attachment": [
|
||||||
|
{
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"name": "Blog",
|
||||||
|
"value": "<a rel=\"me\" title=\"https://pfefferle.org/\" target=\"_blank\" href=\"https://pfefferle.org/\">pfefferle.org</a>"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "PropertyValue",
|
||||||
|
"name": "Profile",
|
||||||
|
"value": "<a rel=\"me\" title=\"https://pfefferle.org/author/pfefferle/\" target=\"_blank\" href=\"https://pfefferle.org/author/pfefferle/\">pfefferle.org</a>"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "Matthias Pfefferle",
|
||||||
|
"icon": {
|
||||||
|
"type": "Image",
|
||||||
|
"url": "https://secure.gravatar.com/avatar/a2bdca7870e859658cece96c044b3be5?s=120&d=mm&r=g"
|
||||||
|
},
|
||||||
|
"published": "2014-02-10T15:23:08Z",
|
||||||
|
"summary": "<p>Ich arbeite als Open Web Lead für Automattic.</p>\n",
|
||||||
|
"tag": [],
|
||||||
|
"url": "https://pfefferle.org/author/pfefferle/",
|
||||||
|
"inbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/inbox",
|
||||||
|
"outbox": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/outbox",
|
||||||
|
"following": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/following",
|
||||||
|
"followers": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/followers",
|
||||||
|
"preferredUsername": "matthias",
|
||||||
|
"endpoints": {
|
||||||
|
"sharedInbox": "https://pfefferle.org/wp-json/activitypub/1.0/inbox"
|
||||||
|
},
|
||||||
|
"publicKey": {
|
||||||
|
"id": "https://pfefferle.org/author/pfefferle/#main-key",
|
||||||
|
"owner": "https://pfefferle.org/author/pfefferle/",
|
||||||
|
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvTA5RA40nOsso04RSwyX\nHXTojRPUMlIlArDcSy3M5GUJp9/xbxSUOdBjqd31KKB1GIi3vrLmD1Qi/ZqS95Qy\nw2Zd3xOsCg+o9bsyOG+O6Y8Lu+HEB5JKLUbNHdiSviakJ8wGadH9Wm4WIiN20y+q\n/u6lgxgiWfZ2CFCN6SOc28fUKi9NmKvXK+M12BhFfy1tC5KWXKDm0UbfI1+dmqhR\n3Ffe6vEsCI/YIVVdWxQ9kouOd0XSHOGdslktkepRO7IP9i9TdwyeCa0WWRoeO5Wa\ntVpc1Y0WuNbTM2ksIXTg0G+rO1/6KO/hrHnGu3RCfb/ZIHK5L/aWYb9B3PG3LyKV\n+wIDAQAB\n-----END PUBLIC KEY-----\n"
|
||||||
|
},
|
||||||
|
"manuallyApprovesFollowers": false,
|
||||||
|
"featured": "https://pfefferle.org/wp-json/activitypub/1.0/users/1/collections/featured",
|
||||||
|
"discoverable": true,
|
||||||
|
"indexable": true,
|
||||||
|
"webfinger": "matthias@pfefferle.org"
|
||||||
|
}
|
|
@ -20,7 +20,8 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::ops::Deref;
|
use std::{ops::Deref, time::Duration};
|
||||||
|
use tokio::time::timeout;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
mod comment;
|
mod comment;
|
||||||
|
@ -30,13 +31,22 @@ mod post;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
|
|
||||||
|
const INCOMING_ACTIVITY_TIMEOUT: Duration = Duration::from_secs(9);
|
||||||
|
|
||||||
pub async fn shared_inbox(
|
pub async fn shared_inbox(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
body: Bytes,
|
body: Bytes,
|
||||||
data: Data<LemmyContext>,
|
data: Data<LemmyContext>,
|
||||||
) -> LemmyResult<HttpResponse> {
|
) -> LemmyResult<HttpResponse> {
|
||||||
receive_activity::<SharedInboxActivities, UserOrCommunity, LemmyContext>(request, body, &data)
|
let receive_fut =
|
||||||
|
receive_activity::<SharedInboxActivities, UserOrCommunity, LemmyContext>(request, body, &data);
|
||||||
|
// Set a timeout shorter than `REQWEST_TIMEOUT` for processing incoming activities. This is to
|
||||||
|
// avoid taking a long time to process an incoming activity when a required data fetch times out.
|
||||||
|
// In this case our own instance would timeout and be marked as dead by the sender. Better to
|
||||||
|
// consider the activity broken and move on.
|
||||||
|
timeout(INCOMING_ACTIVITY_TIMEOUT, receive_fut)
|
||||||
.await
|
.await
|
||||||
|
.map_err(|_| LemmyErrorType::InboxTimeout)?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
|
/// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl Object for ApubSite {
|
||||||
kind: ApplicationType::Application,
|
kind: ApplicationType::Application,
|
||||||
id: self.id().into(),
|
id: self.id().into(),
|
||||||
name: self.name.clone(),
|
name: self.name.clone(),
|
||||||
preferred_username: data.domain().to_string(),
|
preferred_username: Some(data.domain().to_string()),
|
||||||
content: self.sidebar.as_ref().map(|d| markdown_to_html(d)),
|
content: self.sidebar.as_ref().map(|d| markdown_to_html(d)),
|
||||||
source: self.sidebar.clone().map(Source::new),
|
source: self.sidebar.clone().map(Source::new),
|
||||||
summary: self.description.clone(),
|
summary: self.description.clone(),
|
||||||
|
|
|
@ -96,4 +96,10 @@ mod tests {
|
||||||
test_json::<Report>("assets/mbin/activities/flag.json")?;
|
test_json::<Report>("assets/mbin/activities/flag.json")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_wordpress_activities() -> LemmyResult<()> {
|
||||||
|
test_json::<AnnounceActivity>("assets/wordpress/activities/announce.json")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ pub struct Instance {
|
||||||
/// site name
|
/// site name
|
||||||
pub(crate) name: String,
|
pub(crate) name: String,
|
||||||
/// instance domain, necessary for mastodon authorized fetch
|
/// instance domain, necessary for mastodon authorized fetch
|
||||||
pub(crate) preferred_username: String,
|
pub(crate) preferred_username: Option<String>,
|
||||||
pub(crate) inbox: Url,
|
pub(crate) inbox: Url,
|
||||||
/// mandatory field in activitypub, lemmy currently serves an empty outbox
|
/// mandatory field in activitypub, lemmy currently serves an empty outbox
|
||||||
pub(crate) outbox: Url,
|
pub(crate) outbox: Url,
|
||||||
|
|
|
@ -206,4 +206,13 @@ mod tests {
|
||||||
test_json::<Person>("assets/nodebb/objects/person.json")?;
|
test_json::<Person>("assets/nodebb/objects/person.json")?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_object_wordpress() -> LemmyResult<()> {
|
||||||
|
test_json::<Group>("assets/wordpress/objects/group.json")?;
|
||||||
|
test_json::<Page>("assets/wordpress/objects/page.json")?;
|
||||||
|
test_json::<Person>("assets/wordpress/objects/person.json")?;
|
||||||
|
test_json::<Note>("assets/wordpress/objects/note.json")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,6 +233,10 @@ impl ActivityHandler for Page {
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl InCommunity for Page {
|
impl InCommunity for Page {
|
||||||
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
async fn community(&self, context: &Data<LemmyContext>) -> LemmyResult<ApubCommunity> {
|
||||||
|
if let Some(audience) = &self.audience {
|
||||||
|
return audience.dereference(context).await;
|
||||||
|
}
|
||||||
|
|
||||||
let community = match &self.attributed_to {
|
let community = match &self.attributed_to {
|
||||||
AttributedTo::Lemmy(_) => {
|
AttributedTo::Lemmy(_) => {
|
||||||
let mut iter = self.to.iter().merge(self.cc.iter());
|
let mut iter = self.to.iter().merge(self.cc.iter());
|
||||||
|
@ -243,7 +247,7 @@ impl InCommunity for Page {
|
||||||
break c;
|
break c;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(LemmyErrorType::NoCommunityFoundInCc)?
|
Err(LemmyErrorType::CouldntFindCommunity)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -251,11 +255,12 @@ impl InCommunity for Page {
|
||||||
p.iter()
|
p.iter()
|
||||||
.find(|a| a.kind == PersonOrGroupType::Group)
|
.find(|a| a.kind == PersonOrGroupType::Group)
|
||||||
.map(|a| ObjectId::<ApubCommunity>::from(a.id.clone().into_inner()))
|
.map(|a| ObjectId::<ApubCommunity>::from(a.id.clone().into_inner()))
|
||||||
.ok_or(LemmyErrorType::PageDoesNotSpecifyGroup)?
|
.ok_or(LemmyErrorType::CouldntFindCommunity)?
|
||||||
.dereference(context)
|
.dereference(context)
|
||||||
.await?
|
.await?
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(audience) = &self.audience {
|
if let Some(audience) = &self.audience {
|
||||||
verify_community_matches(audience, community.actor_id.clone())?;
|
verify_community_matches(audience, community.actor_id.clone())?;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,12 @@
|
||||||
-- (even if only other columns are updated) because triggers can run after the deletion of referenced rows and
|
-- (even if only other columns are updated) because triggers can run after the deletion of referenced rows and
|
||||||
-- before the automatic deletion of the row that references it. This is not a problem for insert or delete.
|
-- before the automatic deletion of the row that references it. This is not a problem for insert or delete.
|
||||||
--
|
--
|
||||||
|
-- After a row update begins, a concurrent update on the same row can't begin until the whole
|
||||||
|
-- transaction that contains the first update is finished. To reduce this locking, statements in
|
||||||
|
-- triggers should be ordered based on the likelihood of concurrent writers. For example, updating
|
||||||
|
-- site_aggregates should be done last because the same row is updated for all local stuff. If
|
||||||
|
-- it were not last, then the locking period for concurrent writers would extend to include the
|
||||||
|
-- time consumed by statements that come after.
|
||||||
--
|
--
|
||||||
--
|
--
|
||||||
-- Create triggers for both post and comments
|
-- Create triggers for both post and comments
|
||||||
|
@ -38,16 +44,18 @@ BEGIN
|
||||||
(thing_like).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_like).score = 1), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE (thing_like).score != 1), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (thing_like).thing_id) AS diff
|
(thing_like).thing_id, coalesce(sum(count_diff) FILTER (WHERE (thing_like).score = 1), 0) AS upvotes, coalesce(sum(count_diff) FILTER (WHERE (thing_like).score != 1), 0) AS downvotes FROM select_old_and_new_rows AS old_and_new_rows GROUP BY (thing_like).thing_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.thing_id = diff.thing_id
|
a.thing_id = diff.thing_id
|
||||||
RETURNING
|
AND (diff.upvotes, diff.downvotes) != (0, 0)
|
||||||
r.creator_id_from_thing_aggregates (a.*) AS creator_id, diff.upvotes - diff.downvotes AS score)
|
RETURNING
|
||||||
UPDATE
|
r.creator_id_from_thing_aggregates (a.*) AS creator_id, diff.upvotes - diff.downvotes AS score)
|
||||||
person_aggregates AS a
|
UPDATE
|
||||||
SET
|
person_aggregates AS a
|
||||||
thing_score = a.thing_score + diff.score FROM (
|
SET
|
||||||
SELECT
|
thing_score = a.thing_score + diff.score FROM (
|
||||||
creator_id, sum(score) AS score FROM thing_diff GROUP BY creator_id) AS diff
|
SELECT
|
||||||
WHERE
|
creator_id, sum(score) AS score FROM thing_diff GROUP BY creator_id) AS diff
|
||||||
a.person_id = diff.creator_id;
|
WHERE
|
||||||
|
a.person_id = diff.creator_id
|
||||||
|
AND diff.score != 0;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END;
|
END;
|
||||||
$$);
|
$$);
|
||||||
|
@ -62,6 +70,21 @@ CALL r.post_or_comment ('post');
|
||||||
CALL r.post_or_comment ('comment');
|
CALL r.post_or_comment ('comment');
|
||||||
|
|
||||||
-- Create triggers that update counts in parent aggregates
|
-- Create triggers that update counts in parent aggregates
|
||||||
|
CREATE FUNCTION r.parent_comment_ids (path ltree)
|
||||||
|
RETURNS SETOF int
|
||||||
|
LANGUAGE sql
|
||||||
|
IMMUTABLE parallel safe
|
||||||
|
BEGIN
|
||||||
|
ATOMIC
|
||||||
|
SELECT
|
||||||
|
comment_id::int
|
||||||
|
FROM
|
||||||
|
string_to_table (ltree2text (path), '.') AS comment_id
|
||||||
|
-- Skip first and last
|
||||||
|
LIMIT (nlevel (path) - 2) OFFSET 1;
|
||||||
|
|
||||||
|
END;
|
||||||
|
|
||||||
CALL r.create_triggers ('comment', $$
|
CALL r.create_triggers ('comment', $$
|
||||||
BEGIN
|
BEGIN
|
||||||
UPDATE
|
UPDATE
|
||||||
|
@ -76,60 +99,84 @@ BEGIN
|
||||||
r.is_counted (comment)
|
r.is_counted (comment)
|
||||||
GROUP BY (comment).creator_id) AS diff
|
GROUP BY (comment).creator_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.person_id = diff.creator_id;
|
a.person_id = diff.creator_id
|
||||||
|
AND diff.comment_count != 0;
|
||||||
|
|
||||||
UPDATE
|
UPDATE
|
||||||
site_aggregates AS a
|
comment_aggregates AS a
|
||||||
SET
|
SET
|
||||||
comments = a.comments + diff.comments
|
child_count = a.child_count + diff.child_count
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
coalesce(sum(count_diff), 0) AS comments
|
parent_id,
|
||||||
FROM
|
coalesce(sum(count_diff), 0) AS child_count
|
||||||
select_old_and_new_rows AS old_and_new_rows
|
FROM (
|
||||||
WHERE
|
-- For each inserted or deleted comment, this outputs 1 row for each parent comment.
|
||||||
r.is_counted (comment)
|
-- For example, this:
|
||||||
AND (comment).local) AS diff;
|
--
|
||||||
|
-- count_diff | (comment).path
|
||||||
|
-- ------------+----------------
|
||||||
|
-- 1 | 0.5.6.7
|
||||||
|
-- 1 | 0.5.6.7.8
|
||||||
|
--
|
||||||
|
-- becomes this:
|
||||||
|
--
|
||||||
|
-- count_diff | parent_id
|
||||||
|
-- ------------+-----------
|
||||||
|
-- 1 | 5
|
||||||
|
-- 1 | 6
|
||||||
|
-- 1 | 5
|
||||||
|
-- 1 | 6
|
||||||
|
-- 1 | 7
|
||||||
|
SELECT
|
||||||
|
count_diff,
|
||||||
|
parent_id
|
||||||
|
FROM
|
||||||
|
select_old_and_new_rows AS old_and_new_rows,
|
||||||
|
LATERAL r.parent_comment_ids ((comment).path) AS parent_id) AS expanded_old_and_new_rows
|
||||||
|
GROUP BY
|
||||||
|
parent_id) AS diff
|
||||||
|
WHERE
|
||||||
|
a.comment_id = diff.parent_id
|
||||||
|
AND diff.child_count != 0;
|
||||||
|
|
||||||
WITH post_diff AS (
|
WITH post_diff AS (
|
||||||
UPDATE
|
UPDATE
|
||||||
post_aggregates AS a
|
post_aggregates AS a
|
||||||
SET
|
SET
|
||||||
comments = a.comments + diff.comments,
|
comments = a.comments + diff.comments,
|
||||||
newest_comment_time = GREATEST (a.newest_comment_time, (
|
newest_comment_time = GREATEST (a.newest_comment_time, diff.newest_comment_time),
|
||||||
SELECT
|
newest_comment_time_necro = GREATEST (a.newest_comment_time_necro, diff.newest_comment_time_necro)
|
||||||
published
|
|
||||||
FROM select_new_rows AS new_comment
|
|
||||||
WHERE
|
|
||||||
a.post_id = new_comment.post_id ORDER BY published DESC LIMIT 1)),
|
|
||||||
newest_comment_time_necro = GREATEST (a.newest_comment_time_necro, (
|
|
||||||
SELECT
|
|
||||||
published
|
|
||||||
FROM select_new_rows AS new_comment
|
|
||||||
WHERE
|
|
||||||
a.post_id = new_comment.post_id
|
|
||||||
-- Ignore comments from the post's creator
|
|
||||||
AND a.creator_id != new_comment.creator_id
|
|
||||||
-- Ignore comments on old posts
|
|
||||||
AND a.published > (new_comment.published - '2 days'::interval)
|
|
||||||
ORDER BY published DESC LIMIT 1))
|
|
||||||
FROM (
|
FROM (
|
||||||
SELECT
|
SELECT
|
||||||
(comment).post_id,
|
post.id AS post_id,
|
||||||
coalesce(sum(count_diff), 0) AS comments
|
coalesce(sum(count_diff), 0) AS comments,
|
||||||
|
-- Old rows are excluded using `count_diff = 1`
|
||||||
|
max((comment).published) FILTER (WHERE count_diff = 1) AS newest_comment_time,
|
||||||
|
max((comment).published) FILTER (WHERE count_diff = 1
|
||||||
|
-- Ignore comments from the post's creator
|
||||||
|
AND post.creator_id != (comment).creator_id
|
||||||
|
-- Ignore comments on old posts
|
||||||
|
AND post.published > ((comment).published - '2 days'::interval)) AS newest_comment_time_necro,
|
||||||
|
r.is_counted (post.*) AS include_in_community_aggregates
|
||||||
FROM
|
FROM
|
||||||
select_old_and_new_rows AS old_and_new_rows
|
select_old_and_new_rows AS old_and_new_rows
|
||||||
|
LEFT JOIN post ON post.id = (comment).post_id
|
||||||
WHERE
|
WHERE
|
||||||
r.is_counted (comment)
|
r.is_counted (comment)
|
||||||
GROUP BY
|
GROUP BY
|
||||||
(comment).post_id) AS diff
|
post.id) AS diff
|
||||||
LEFT JOIN post ON post.id = diff.post_id
|
|
||||||
WHERE
|
WHERE
|
||||||
a.post_id = diff.post_id
|
a.post_id = diff.post_id
|
||||||
|
AND (diff.comments,
|
||||||
|
GREATEST (a.newest_comment_time, diff.newest_comment_time),
|
||||||
|
GREATEST (a.newest_comment_time_necro, diff.newest_comment_time_necro)) != (0,
|
||||||
|
a.newest_comment_time,
|
||||||
|
a.newest_comment_time_necro)
|
||||||
RETURNING
|
RETURNING
|
||||||
a.community_id,
|
a.community_id,
|
||||||
diff.comments,
|
diff.comments,
|
||||||
r.is_counted (post.*) AS include_in_community_aggregates)
|
diff.include_in_community_aggregates)
|
||||||
UPDATE
|
UPDATE
|
||||||
community_aggregates AS a
|
community_aggregates AS a
|
||||||
SET
|
SET
|
||||||
|
@ -145,7 +192,23 @@ FROM (
|
||||||
GROUP BY
|
GROUP BY
|
||||||
community_id) AS diff
|
community_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.community_id = diff.community_id;
|
a.community_id = diff.community_id
|
||||||
|
AND diff.comments != 0;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
site_aggregates AS a
|
||||||
|
SET
|
||||||
|
comments = a.comments + diff.comments
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
coalesce(sum(count_diff), 0) AS comments
|
||||||
|
FROM
|
||||||
|
select_old_and_new_rows AS old_and_new_rows
|
||||||
|
WHERE
|
||||||
|
r.is_counted (comment)
|
||||||
|
AND (comment).local) AS diff
|
||||||
|
WHERE
|
||||||
|
diff.comments != 0;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -167,20 +230,8 @@ BEGIN
|
||||||
r.is_counted (post)
|
r.is_counted (post)
|
||||||
GROUP BY (post).creator_id) AS diff
|
GROUP BY (post).creator_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.person_id = diff.creator_id;
|
a.person_id = diff.creator_id
|
||||||
|
AND diff.post_count != 0;
|
||||||
UPDATE
|
|
||||||
site_aggregates AS a
|
|
||||||
SET
|
|
||||||
posts = a.posts + diff.posts
|
|
||||||
FROM (
|
|
||||||
SELECT
|
|
||||||
coalesce(sum(count_diff), 0) AS posts
|
|
||||||
FROM
|
|
||||||
select_old_and_new_rows AS old_and_new_rows
|
|
||||||
WHERE
|
|
||||||
r.is_counted (post)
|
|
||||||
AND (post).local) AS diff;
|
|
||||||
|
|
||||||
UPDATE
|
UPDATE
|
||||||
community_aggregates AS a
|
community_aggregates AS a
|
||||||
|
@ -197,7 +248,23 @@ FROM (
|
||||||
GROUP BY
|
GROUP BY
|
||||||
(post).community_id) AS diff
|
(post).community_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.community_id = diff.community_id;
|
a.community_id = diff.community_id
|
||||||
|
AND diff.posts != 0;
|
||||||
|
|
||||||
|
UPDATE
|
||||||
|
site_aggregates AS a
|
||||||
|
SET
|
||||||
|
posts = a.posts + diff.posts
|
||||||
|
FROM (
|
||||||
|
SELECT
|
||||||
|
coalesce(sum(count_diff), 0) AS posts
|
||||||
|
FROM
|
||||||
|
select_old_and_new_rows AS old_and_new_rows
|
||||||
|
WHERE
|
||||||
|
r.is_counted (post)
|
||||||
|
AND (post).local) AS diff
|
||||||
|
WHERE
|
||||||
|
diff.posts != 0;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -217,7 +284,9 @@ BEGIN
|
||||||
FROM select_old_and_new_rows AS old_and_new_rows
|
FROM select_old_and_new_rows AS old_and_new_rows
|
||||||
WHERE
|
WHERE
|
||||||
r.is_counted (community)
|
r.is_counted (community)
|
||||||
AND (community).local) AS diff;
|
AND (community).local) AS diff
|
||||||
|
WHERE
|
||||||
|
diff.communities != 0;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -235,7 +304,9 @@ BEGIN
|
||||||
SELECT
|
SELECT
|
||||||
coalesce(sum(count_diff), 0) AS users
|
coalesce(sum(count_diff), 0) AS users
|
||||||
FROM select_old_and_new_rows AS old_and_new_rows
|
FROM select_old_and_new_rows AS old_and_new_rows
|
||||||
WHERE (person).local) AS diff;
|
WHERE (person).local) AS diff
|
||||||
|
WHERE
|
||||||
|
diff.users != 0;
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -270,7 +341,8 @@ BEGIN
|
||||||
GROUP BY
|
GROUP BY
|
||||||
old_post.community_id) AS diff
|
old_post.community_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.community_id = diff.community_id;
|
a.community_id = diff.community_id
|
||||||
|
AND diff.comments != 0;
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
END;
|
END;
|
||||||
$$;
|
$$;
|
||||||
|
@ -296,7 +368,8 @@ BEGIN
|
||||||
LEFT JOIN community ON community.id = (community_follower).community_id
|
LEFT JOIN community ON community.id = (community_follower).community_id
|
||||||
LEFT JOIN person ON person.id = (community_follower).person_id GROUP BY (community_follower).community_id) AS diff
|
LEFT JOIN person ON person.id = (community_follower).person_id GROUP BY (community_follower).community_id) AS diff
|
||||||
WHERE
|
WHERE
|
||||||
a.community_id = diff.community_id;
|
a.community_id = diff.community_id
|
||||||
|
AND (diff.subscribers, diff.subscribers_local) != (0, 0);
|
||||||
|
|
||||||
RETURN NULL;
|
RETURN NULL;
|
||||||
|
|
||||||
|
@ -474,3 +547,24 @@ CREATE TRIGGER delete_follow
|
||||||
FOR EACH ROW
|
FOR EACH ROW
|
||||||
EXECUTE FUNCTION r.delete_follow_before_person ();
|
EXECUTE FUNCTION r.delete_follow_before_person ();
|
||||||
|
|
||||||
|
-- Triggers that change values before insert or update
|
||||||
|
CREATE FUNCTION r.comment_change_values ()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE plpgsql
|
||||||
|
AS $$
|
||||||
|
DECLARE
|
||||||
|
id text = NEW.id::text;
|
||||||
|
BEGIN
|
||||||
|
-- Make `path` end with `id` if it doesn't already
|
||||||
|
IF NOT (NEW.path ~ ('*.' || id)::lquery) THEN
|
||||||
|
NEW.path = NEW.path || id;
|
||||||
|
END IF;
|
||||||
|
RETURN NEW;
|
||||||
|
END
|
||||||
|
$$;
|
||||||
|
|
||||||
|
CREATE TRIGGER change_values
|
||||||
|
BEFORE INSERT OR UPDATE ON comment
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION r.comment_change_values ();
|
||||||
|
|
||||||
|
|
|
@ -15,12 +15,7 @@ use crate::{
|
||||||
utils::{functions::coalesce, get_conn, naive_now, DbPool, DELETED_REPLACEMENT_TEXT},
|
utils::{functions::coalesce, get_conn, naive_now, DbPool, DELETED_REPLACEMENT_TEXT},
|
||||||
};
|
};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use diesel::{
|
use diesel::{dsl::insert_into, result::Error, ExpressionMethods, QueryDsl};
|
||||||
dsl::{insert_into, sql_query},
|
|
||||||
result::Error,
|
|
||||||
ExpressionMethods,
|
|
||||||
QueryDsl,
|
|
||||||
};
|
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use diesel_ltree::Ltree;
|
use diesel_ltree::Ltree;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -72,81 +67,23 @@ impl Comment {
|
||||||
parent_path: Option<&Ltree>,
|
parent_path: Option<&Ltree>,
|
||||||
) -> Result<Comment, Error> {
|
) -> Result<Comment, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
let comment_form = (comment_form, parent_path.map(|p| comment::path.eq(p)));
|
||||||
|
|
||||||
conn
|
if let Some(timestamp) = timestamp {
|
||||||
.build_transaction()
|
insert_into(comment::table)
|
||||||
.run(|conn| {
|
.values(comment_form)
|
||||||
Box::pin(async move {
|
.on_conflict(comment::ap_id)
|
||||||
// Insert, to get the id
|
.filter_target(coalesce(comment::updated, comment::published).lt(timestamp))
|
||||||
let inserted_comment = if let Some(timestamp) = timestamp {
|
.do_update()
|
||||||
insert_into(comment::table)
|
.set(comment_form)
|
||||||
.values(comment_form)
|
.get_result::<Self>(conn)
|
||||||
.on_conflict(comment::ap_id)
|
.await
|
||||||
.filter_target(coalesce(comment::updated, comment::published).lt(timestamp))
|
} else {
|
||||||
.do_update()
|
insert_into(comment::table)
|
||||||
.set(comment_form)
|
.values(comment_form)
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await?
|
.await
|
||||||
} else {
|
}
|
||||||
insert_into(comment::table)
|
|
||||||
.values(comment_form)
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
.await?
|
|
||||||
};
|
|
||||||
|
|
||||||
let comment_id = inserted_comment.id;
|
|
||||||
|
|
||||||
// You need to update the ltree column
|
|
||||||
let ltree = Ltree(if let Some(parent_path) = parent_path {
|
|
||||||
// The previous parent will already have 0 in it
|
|
||||||
// Append this comment id
|
|
||||||
format!("{}.{}", parent_path.0, comment_id)
|
|
||||||
} else {
|
|
||||||
// '0' is always the first path, append to that
|
|
||||||
format!("{}.{}", 0, comment_id)
|
|
||||||
});
|
|
||||||
|
|
||||||
let updated_comment = diesel::update(comment::table.find(comment_id))
|
|
||||||
.set(comment::path.eq(ltree))
|
|
||||||
.get_result::<Self>(conn)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Update the child count for the parent comment_aggregates
|
|
||||||
// You could do this with a trigger, but since you have to do this manually anyway,
|
|
||||||
// you can just have it here
|
|
||||||
if let Some(parent_path) = parent_path {
|
|
||||||
// You have to update counts for all parents, not just the immediate one
|
|
||||||
// TODO if the performance of this is terrible, it might be better to do this as part of a
|
|
||||||
// scheduled query... although the counts would often be wrong.
|
|
||||||
//
|
|
||||||
// The child_count query for reference:
|
|
||||||
// select c.id, c.path, count(c2.id) as child_count from comment c
|
|
||||||
// left join comment c2 on c2.path <@ c.path and c2.path != c.path
|
|
||||||
// group by c.id
|
|
||||||
|
|
||||||
let parent_id = parent_path.0.split('.').nth(1);
|
|
||||||
|
|
||||||
if let Some(parent_id) = parent_id {
|
|
||||||
let top_parent = format!("0.{}", parent_id);
|
|
||||||
let update_child_count_stmt = format!(
|
|
||||||
"
|
|
||||||
update comment_aggregates ca set child_count = c.child_count
|
|
||||||
from (
|
|
||||||
select c.id, c.path, count(c2.id) as child_count from comment c
|
|
||||||
join comment c2 on c2.path <@ c.path and c2.path != c.path
|
|
||||||
and c.path <@ '{top_parent}'
|
|
||||||
group by c.id
|
|
||||||
) as c
|
|
||||||
where ca.comment_id = c.id"
|
|
||||||
);
|
|
||||||
|
|
||||||
sql_query(update_child_count_stmt).execute(conn).await?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(updated_comment)
|
|
||||||
}) as _
|
|
||||||
})
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn read_from_apub_id(
|
pub async fn read_from_apub_id(
|
||||||
|
|
|
@ -43,21 +43,18 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
|
||||||
.map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?
|
.map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?
|
||||||
.ok_or(ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
|
.ok_or(ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
|
||||||
|
|
||||||
let protocols = if site_view.local_site.federation_enabled {
|
|
||||||
Some(vec!["activitypub".to_string()])
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
// Since there are 3 registration options,
|
// Since there are 3 registration options,
|
||||||
// we need to set open_registrations as true if RegistrationMode is not Closed.
|
// we need to set open_registrations as true if RegistrationMode is not Closed.
|
||||||
let open_registrations = Some(site_view.local_site.registration_mode != RegistrationMode::Closed);
|
let open_registrations = Some(site_view.local_site.registration_mode != RegistrationMode::Closed);
|
||||||
let json = NodeInfo {
|
let json = NodeInfo {
|
||||||
version: Some("2.0".to_string()),
|
version: Some("2.1".to_string()),
|
||||||
software: Some(NodeInfoSoftware {
|
software: Some(NodeInfoSoftware {
|
||||||
name: Some("lemmy".to_string()),
|
name: Some("lemmy".to_string()),
|
||||||
version: Some(VERSION.to_string()),
|
version: Some(VERSION.to_string()),
|
||||||
|
repository: Some("https://github.com/LemmyNet/lemmy".to_string()),
|
||||||
|
homepage: Some("https://join-lemmy.org/".to_string()),
|
||||||
}),
|
}),
|
||||||
protocols,
|
protocols: Some(vec!["activitypub".to_string()]),
|
||||||
usage: Some(NodeInfoUsage {
|
usage: Some(NodeInfoUsage {
|
||||||
users: Some(NodeInfoUsers {
|
users: Some(NodeInfoUsers {
|
||||||
total: Some(site_view.counts.users),
|
total: Some(site_view.counts.users),
|
||||||
|
@ -68,6 +65,11 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
|
||||||
local_comments: Some(site_view.counts.comments),
|
local_comments: Some(site_view.counts.comments),
|
||||||
}),
|
}),
|
||||||
open_registrations,
|
open_registrations,
|
||||||
|
services: Some(NodeInfoServices {
|
||||||
|
inbound: Some(vec![]),
|
||||||
|
outbound: Some(vec![]),
|
||||||
|
}),
|
||||||
|
metadata: Some(vec![]),
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(HttpResponse::Ok().json(json))
|
Ok(HttpResponse::Ok().json(json))
|
||||||
|
@ -84,6 +86,7 @@ struct NodeInfoWellKnownLinks {
|
||||||
pub href: Url,
|
pub href: Url,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Nodeinfo spec: http://nodeinfo.diaspora.software/docson/index.html#/ns/schema/2.1
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
#[serde(rename_all = "camelCase", default)]
|
#[serde(rename_all = "camelCase", default)]
|
||||||
pub struct NodeInfo {
|
pub struct NodeInfo {
|
||||||
|
@ -92,6 +95,9 @@ pub struct NodeInfo {
|
||||||
pub protocols: Option<Vec<String>>,
|
pub protocols: Option<Vec<String>>,
|
||||||
pub usage: Option<NodeInfoUsage>,
|
pub usage: Option<NodeInfoUsage>,
|
||||||
pub open_registrations: Option<bool>,
|
pub open_registrations: Option<bool>,
|
||||||
|
/// These fields are required by the spec for no reason
|
||||||
|
pub services: Option<NodeInfoServices>,
|
||||||
|
pub metadata: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
@ -99,6 +105,8 @@ pub struct NodeInfo {
|
||||||
pub struct NodeInfoSoftware {
|
pub struct NodeInfoSoftware {
|
||||||
pub name: Option<String>,
|
pub name: Option<String>,
|
||||||
pub version: Option<String>,
|
pub version: Option<String>,
|
||||||
|
pub repository: Option<String>,
|
||||||
|
pub homepage: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
@ -116,3 +124,10 @@ pub struct NodeInfoUsers {
|
||||||
pub active_halfyear: Option<i64>,
|
pub active_halfyear: Option<i64>,
|
||||||
pub active_month: Option<i64>,
|
pub active_month: Option<i64>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
#[serde(rename_all = "camelCase", default)]
|
||||||
|
pub struct NodeInfoServices {
|
||||||
|
pub inbound: Option<Vec<String>>,
|
||||||
|
pub outbound: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
|
@ -99,8 +99,6 @@ pub enum LemmyErrorType {
|
||||||
PersonIsBannedFromSite(String),
|
PersonIsBannedFromSite(String),
|
||||||
InvalidVoteValue,
|
InvalidVoteValue,
|
||||||
PageDoesNotSpecifyCreator,
|
PageDoesNotSpecifyCreator,
|
||||||
PageDoesNotSpecifyGroup,
|
|
||||||
NoCommunityFoundInCc,
|
|
||||||
NoEmailSetup,
|
NoEmailSetup,
|
||||||
LocalSiteNotSetup,
|
LocalSiteNotSetup,
|
||||||
EmailSmtpServerNeedsAPort,
|
EmailSmtpServerNeedsAPort,
|
||||||
|
@ -177,6 +175,7 @@ pub enum LemmyErrorType {
|
||||||
InvalidBotAction,
|
InvalidBotAction,
|
||||||
CantBlockLocalInstance,
|
CantBlockLocalInstance,
|
||||||
UrlWithoutDomain,
|
UrlWithoutDomain,
|
||||||
|
InboxTimeout,
|
||||||
Unknown(String),
|
Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit a4681f70a4ddf077951ed2dcc8cf90bb243d4828
|
Subproject commit f0ab81deea347c433277a90ae752b10f68473719
|
3
migrations/2024-05-04-140749_separate_triggers/down.sql
Normal file
3
migrations/2024-05-04-140749_separate_triggers/down.sql
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
SELECT
|
||||||
|
1;
|
||||||
|
|
4
migrations/2024-05-04-140749_separate_triggers/up.sql
Normal file
4
migrations/2024-05-04-140749_separate_triggers/up.sql
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
-- This migration exists to trigger re-execution of replaceable_schema
|
||||||
|
SELECT
|
||||||
|
1;
|
||||||
|
|
|
@ -160,10 +160,10 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||||
rate_limit_cell.clone(),
|
rate_limit_cell.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if !args.disable_scheduled_tasks {
|
let scheduled_tasks = (!args.disable_scheduled_tasks).then(|| {
|
||||||
// Schedules various cleanup tasks for the DB
|
// Schedules various cleanup tasks for the DB
|
||||||
let _scheduled_tasks = tokio::task::spawn(scheduled_tasks::setup(context.clone()));
|
tokio::task::spawn(scheduled_tasks::setup(context.clone()))
|
||||||
}
|
});
|
||||||
|
|
||||||
if let Some(prometheus) = SETTINGS.prometheus.clone() {
|
if let Some(prometheus) = SETTINGS.prometheus.clone() {
|
||||||
serve_prometheus(prometheus, context.clone())?;
|
serve_prometheus(prometheus, context.clone())?;
|
||||||
|
@ -218,7 +218,7 @@ pub async fn start_lemmy_server(args: CmdArgs) -> LemmyResult<()> {
|
||||||
let mut interrupt = tokio::signal::unix::signal(SignalKind::interrupt())?;
|
let mut interrupt = tokio::signal::unix::signal(SignalKind::interrupt())?;
|
||||||
let mut terminate = tokio::signal::unix::signal(SignalKind::terminate())?;
|
let mut terminate = tokio::signal::unix::signal(SignalKind::terminate())?;
|
||||||
|
|
||||||
if server.is_some() || federate.is_some() {
|
if server.is_some() || federate.is_some() || scheduled_tasks.is_some() {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
_ = tokio::signal::ctrl_c() => {
|
_ = tokio::signal::ctrl_c() => {
|
||||||
tracing::warn!("Received ctrl-c, shutting down gracefully...");
|
tracing::warn!("Received ctrl-c, shutting down gracefully...");
|
||||||
|
|
Loading…
Reference in a new issue