Merge branch 'main' into markdown-link-rule

markdown-link-rule
Felix Ableitner 4 months ago
commit 2fd7edd8ec

@ -39,25 +39,21 @@ steps:
- git submodule update
prettier_check:
group: format
image: tmknom/prettier:3.0.0
commands:
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations'
toml_fmt:
group: format
image: tamasfe/taplo:0.8.1
commands:
- taplo format --check
sql_fmt:
group: format
image: backplane/pgformatter:latest
commands:
- ./scripts/sql_format_check.sh
cargo_fmt:
group: format
image: rustlang/rust:nightly
environment:
# store cargo data in repo folder so that it gets cached between steps
@ -67,7 +63,6 @@ steps:
- cargo +nightly fmt -- --check
cargo_machete:
group: format
image: rustlang/rust:nightly
commands:
- wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz
@ -77,40 +72,12 @@ steps:
- cargo machete
ignored_files:
group: format
image: alpine:3
commands:
- apk add git
- IGNORED=$(git ls-files --cached -i --exclude-standard)
- if [[ "$IGNORED" ]]; then echo "Ignored files present:\n$IGNORED\n"; exit 1; fi
restore-cache:
image: meltwater/drone-cache:v1
pull: true
settings:
restore: true
endpoint:
from_secret: MINIO_ENDPOINT
access-key:
from_secret: MINIO_WRITE_USER
secret-key:
from_secret: MINIO_WRITE_PASSWORD
bucket:
from_secret: MINIO_BUCKET
region: us-east-1
cache_key: "rust-cache"
path-style: true
backend_operation_timeout: 30m
compression_level: 0
exit_code: true
mount:
- ".cargo_home"
- "target"
- "api_tests/node_modules"
secrets:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
when: *slow_check_paths
# make sure api builds with default features (used by other crates relying on lemmy api)
check_api_common_default_features:
image: *rust_image
@ -188,7 +155,6 @@ steps:
when: *slow_check_paths
cargo_test:
group: tests
image: *rust_image
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
@ -200,7 +166,6 @@ steps:
when: *slow_check_paths
run_federation_tests:
group: tests
image: node:20-bookworm-slim
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432
@ -213,35 +178,6 @@ steps:
- yarn api-test
when: *slow_check_paths
rebuild-cache:
image: meltwater/drone-cache:v1
pull: true
settings:
rebuild: true
endpoint:
from_secret: MINIO_ENDPOINT
access-key:
from_secret: MINIO_WRITE_USER
secret-key:
from_secret: MINIO_WRITE_PASSWORD
bucket:
from_secret: MINIO_BUCKET
cache_key: "rust-cache"
region: us-east-1
path-style: true
backend_operation_timeout: 60m
compression_level: 0
exit_code: true
mount:
- ".cargo_home"
- "target"
- "api_tests/node_modules"
secrets:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
when:
- event: push
branch: main
publish_release_docker:
image: woodpeckerci/plugin-docker-buildx
secrets: [docker_username, docker_password]

24
Cargo.lock generated

@ -2529,7 +2529,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lemmy_api"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2558,7 +2558,7 @@ dependencies = [
[[package]]
name = "lemmy_api_common"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2598,7 +2598,7 @@ dependencies = [
[[package]]
name = "lemmy_api_crud"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2620,7 +2620,7 @@ dependencies = [
[[package]]
name = "lemmy_apub"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2658,7 +2658,7 @@ dependencies = [
[[package]]
name = "lemmy_db_schema"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"async-trait",
@ -2695,7 +2695,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"actix-web",
"chrono",
@ -2715,7 +2715,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views_actor"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"chrono",
"diesel",
@ -2733,7 +2733,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views_moderator"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"diesel",
"diesel-async",
@ -2745,7 +2745,7 @@ dependencies = [
[[package]]
name = "lemmy_federate"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"anyhow",
@ -2768,7 +2768,7 @@ dependencies = [
[[package]]
name = "lemmy_routes"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2793,7 +2793,7 @@ dependencies = [
[[package]]
name = "lemmy_server"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"activitypub_federation",
"actix-cors",
@ -2836,7 +2836,7 @@ dependencies = [
[[package]]
name = "lemmy_utils"
version = "0.19.2-rc.5"
version = "0.19.3-rc.1"
dependencies = [
"actix-web",
"anyhow",

@ -1,6 +1,5 @@
[workspace.package]
version = "0.19.2-rc.5"
publish = false
version = "0.19.3-rc.1"
edition = "2021"
description = "A link aggregator for the fediverse"
license = "AGPL-3.0"
@ -17,6 +16,7 @@ license.workspace = true
homepage.workspace = true
documentation.workspace = true
repository.workspace = true
publish = false
[lib]
doctest = false
@ -85,16 +85,16 @@ unused_self = "deny"
unwrap_used = "deny"
[workspace.dependencies]
lemmy_api = { version = "=0.19.2-rc.5", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.2-rc.5", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.2-rc.5", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.2-rc.5", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.2-rc.5", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.2-rc.5", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.2-rc.5", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.2-rc.5", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.2-rc.5", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.2-rc.5", path = "./crates/db_views_moderator" }
lemmy_api = { version = "=0.19.3-rc.1", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.3-rc.1", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.3-rc.1", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.3-rc.1", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.3-rc.1", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.3-rc.1", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.3-rc.1", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.3-rc.1", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.3-rc.1", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.3-rc.1", path = "./crates/db_views_moderator" }
activitypub_federation = { version = "0.5.1-beta.1", default-features = false, features = [
"actix-web",
] }
@ -166,7 +166,7 @@ lemmy_utils = { workspace = true }
lemmy_db_schema = { workspace = true }
lemmy_api_common = { workspace = true }
lemmy_routes = { workspace = true }
lemmy_federate = { version = "0.19.2-rc.5", path = "crates/federate" }
lemmy_federate = { version = "0.19.3-rc.1", path = "crates/federate" }
activitypub_federation = { workspace = true }
diesel = { workspace = true }
diesel-async = { workspace = true }

@ -86,7 +86,12 @@ test("Create a post", async () => {
throw "Missing beta community";
}
let postRes = await createPost(alpha, betaCommunity.community.id);
let postRes = await createPost(
alpha,
betaCommunity.community.id,
"https://example.com/",
"აშშ ითხოვს ირანს დაუყოვნებლივ გაანთავისუფლოს დაკავებული ნავთობის ტანკერი",
);
expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true);

@ -195,11 +195,11 @@ export async function setupLogins() {
export async function createPost(
api: LemmyHttp,
community_id: number,
// use example.com for consistent title and embed description
url: string = "https://example.com/",
body = randomString(10),
// use example.com for consistent title and embed description
name: string = randomString(5),
): Promise<PostResponse> {
let name = randomString(5);
let form: CreatePost = {
name,
url,

@ -112,19 +112,18 @@ test("Delete user", async () => {
).toBe(true);
});
test("Requests with invalid auth should throw error", async () => {
test("Requests with invalid auth should be treated as unauthenticated", async () => {
let invalid_auth = new LemmyHttp(alphaUrl, {
headers: { Authorization: "Bearer foobar" },
fetchFunction,
});
await expect(getSite(invalid_auth)).rejects.toStrictEqual(
Error("incorrect_login"),
);
let site = await getSite(invalid_auth);
expect(site.my_user).toBeUndefined();
expect(site.site_view).toBeDefined();
let form: GetPosts = {};
await expect(invalid_auth.getPosts(form)).rejects.toStrictEqual(
Error("incorrect_login"),
);
let posts = invalid_auth.getPosts(form);
expect((await posts).posts).toBeDefined();
});
test("Create user with Arabic name", async () => {

@ -2,11 +2,15 @@ use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha;
use lemmy_api_common::utils::{local_site_to_slur_regex, AUTH_COOKIE_NAME};
use lemmy_api_common::{
claims::Claims,
context::LemmyContext,
utils::{check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME},
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
utils::slurs::check_slurs,
};
use std::io::Cursor;
@ -137,6 +141,20 @@ pub(crate) fn build_totp_2fa(
.with_lemmy_type(LemmyErrorType::CouldntGenerateTotp)
}
#[tracing::instrument(skip_all)]
pub async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> Result<LocalUserView, LemmyError> {
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;
Ok(local_user_view)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]

@ -1,5 +1,13 @@
use crate::captcha_as_wav_base64;
use actix_web::web::{Data, Json};
use actix_web::{
http::{
header::{CacheControl, CacheDirective},
StatusCode,
},
web::{Data, Json},
HttpResponse,
HttpResponseBuilder,
};
use captcha::{gen, Difficulty};
use lemmy_api_common::{
context::LemmyContext,
@ -12,13 +20,13 @@ use lemmy_db_schema::source::{
use lemmy_utils::error::LemmyError;
#[tracing::instrument(skip(context))]
pub async fn get_captcha(
context: Data<LemmyContext>,
) -> Result<Json<GetCaptchaResponse>, LemmyError> {
pub async fn get_captcha(context: Data<LemmyContext>) -> Result<HttpResponse, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let mut res = HttpResponseBuilder::new(StatusCode::OK);
res.insert_header(CacheControl(vec![CacheDirective::NoStore]));
if !local_site.captcha_enabled {
return Ok(Json(GetCaptchaResponse { ok: None }));
return Ok(res.json(Json(GetCaptchaResponse { ok: None })));
}
let captcha = gen(match local_site.captcha_difficulty.as_str() {
@ -37,11 +45,12 @@ pub async fn get_captcha(
// Stores the captcha item in the db
let captcha = CaptchaAnswer::insert(&mut context.pool(), &captcha_form).await?;
Ok(Json(GetCaptchaResponse {
let json = Json(GetCaptchaResponse {
ok: Some(CaptchaResponse {
png,
wav,
uuid: captcha.uuid.to_string(),
}),
}))
});
Ok(res.json(json))
}

@ -1,10 +1,10 @@
use crate::read_auth_token;
use crate::{local_user_view_from_jwt, read_auth_token};
use actix_web::{
web::{Data, Json},
HttpRequest,
};
use lemmy_api_common::{claims::Claims, context::LemmyContext, SuccessResponse};
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
use lemmy_utils::error::{LemmyError, LemmyErrorType};
/// Returns an error message if the auth token is invalid for any reason. Necessary because other
/// endpoints silently treat any call with invalid auth as unauthenticated.
@ -15,9 +15,7 @@ pub async fn validate_auth(
) -> Result<Json<SuccessResponse>, LemmyError> {
let jwt = read_auth_token(&req)?;
if let Some(jwt) = jwt {
Claims::validate(&jwt, &context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
local_user_view_from_jwt(&jwt, &context).await?;
} else {
Err(LemmyErrorType::NotLoggedIn)?;
}

@ -6,7 +6,7 @@ use lemmy_db_schema::{
newtypes::LocalUserId,
source::login_token::{LoginToken, LoginTokenCreateForm},
};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
@ -25,7 +25,8 @@ impl Claims {
validation.required_spec_claims.remove("exp");
let jwt_secret = &context.secret().jwt_secret;
let key = DecodingKey::from_secret(jwt_secret.as_ref());
let claims = decode::<Claims>(jwt, &key, &validation)?;
let claims =
decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let user_id = LocalUserId(claims.claims.sub.parse()?);
let is_valid = LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
if !is_valid {

@ -125,7 +125,6 @@ impl ActivityHandler for BlockUser {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
match self.target.dereference(context).await? {
SiteOrCommunity::Site(site) => {
@ -149,6 +148,7 @@ impl ActivityHandler for BlockUser {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let expires = self.expires.map(Into::into);
let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.dereference(context).await?;

@ -89,7 +89,6 @@ impl ActivityHandler for UndoBlockUser {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
self.object.verify(context).await?;
@ -98,6 +97,7 @@ impl ActivityHandler for UndoBlockUser {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let expires = self.object.expires.map(Into::into);
let mod_person = self.actor.dereference(context).await?;
let blocked_person = self.object.object.dereference(context).await?;

@ -145,14 +145,14 @@ impl ActivityHandler for AnnounceActivity {
}
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
async fn verify(&self, _context: &Data<Self::DataType>) -> Result<(), LemmyError> {
verify_is_public(&self.to, &self.cc)?;
Ok(())
}
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let object: AnnouncableActivities = self.object.object(context).await?.try_into()?;
// This is only for sending, not receiving so we reject it.

@ -115,7 +115,6 @@ impl ActivityHandler for CollectionAdd {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
@ -125,6 +124,7 @@ impl ActivityHandler for CollectionAdd {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let (community, collection_type) =
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
match collection_type {

@ -110,7 +110,6 @@ impl ActivityHandler for CollectionRemove {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
@ -120,6 +119,7 @@ impl ActivityHandler for CollectionRemove {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let (community, collection_type) =
Community::get_by_collection_url(&mut context.pool(), &self.target.into()).await?;
match collection_type {

@ -81,7 +81,6 @@ impl ActivityHandler for UndoLockPage {
}
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
@ -91,6 +90,7 @@ impl ActivityHandler for UndoLockPage {
}
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), Self::Error> {
insert_received_activity(&self.id, context).await?;
let form = PostUpdateForm {
locked: Some(false),
..Default::default()

@ -92,7 +92,6 @@ impl ActivityHandler for Report {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
Ok(())
@ -100,6 +99,7 @@ impl ActivityHandler for Report {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let actor = self.actor.dereference(context).await?;
let reason = self.reason()?;
match self.object.dereference(context).await? {

@ -77,7 +77,6 @@ impl ActivityHandler for UpdateCommunity {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
@ -88,6 +87,7 @@ impl ActivityHandler for UpdateCommunity {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let community = self.community(context).await?;
let community_update_form = CommunityUpdateForm {

@ -115,7 +115,6 @@ impl ActivityHandler for CreateOrUpdateNote {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let post = self.object.get_parents(context).await?.0;
let community = self.community(context).await?;
@ -131,6 +130,7 @@ impl ActivityHandler for CreateOrUpdateNote {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
// Need to do this check here instead of Note::from_json because we need the person who
// send the activity, not the comment author.
let existing_comment = self.object.id.dereference_local(context).await.ok();

@ -105,7 +105,6 @@ impl ActivityHandler for CreateOrUpdatePage {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_is_public(&self.to, &self.cc)?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
@ -140,6 +139,7 @@ impl ActivityHandler for CreateOrUpdatePage {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let post = ApubPost::from_json(self.object, context).await?;
// author likes their own post by default

@ -58,7 +58,6 @@ impl ActivityHandler for CreateOrUpdateChatMessage {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_person(&self.actor, context).await?;
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;
@ -68,6 +67,7 @@ impl ActivityHandler for CreateOrUpdateChatMessage {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
ApubPrivateMessage::from_json(self.object, context).await?;
Ok(())
}

@ -43,13 +43,13 @@ impl ActivityHandler for Delete {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_delete_activity(self, self.summary.is_some(), context).await?;
Ok(())
}
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
if let Some(reason) = self.summary {
// We set reason to empty string if it doesn't exist, to distinguish between delete and
// remove. Here we change it back to option, so we don't write it to db.

@ -42,7 +42,6 @@ impl ActivityHandler for UndoDelete {
}
async fn verify(&self, data: &Data<Self::DataType>) -> Result<(), Self::Error> {
insert_received_activity(&self.id, data).await?;
self.object.verify(data).await?;
verify_delete_activity(&self.object, self.object.summary.is_some(), data).await?;
Ok(())
@ -50,6 +49,7 @@ impl ActivityHandler for UndoDelete {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
if self.object.summary.is_some() {
UndoDelete::receive_undo_remove_action(
&self.actor.dereference(context).await?,

@ -53,7 +53,6 @@ impl ActivityHandler for AcceptFollow {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_urls_match(self.actor.inner(), self.object.object.inner())?;
self.object.verify(context).await?;
if let Some(to) = &self.to {
@ -64,6 +63,7 @@ impl ActivityHandler for AcceptFollow {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let community = self.actor.dereference(context).await?;
let person = self.object.actor.dereference(context).await?;
// This will throw an error if no follow was requested

@ -77,7 +77,6 @@ impl ActivityHandler for Follow {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_person(&self.actor, context).await?;
let object = self.object.dereference(context).await?;
if let UserOrCommunity::Community(c) = object {
@ -91,6 +90,7 @@ impl ActivityHandler for Follow {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let actor = self.actor.dereference(context).await?;
let object = self.object.dereference(context).await?;
match object {

@ -65,7 +65,6 @@ impl ActivityHandler for UndoFollow {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
verify_person(&self.actor, context).await?;
self.object.verify(context).await?;
@ -77,6 +76,7 @@ impl ActivityHandler for UndoFollow {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let person = self.actor.dereference(context).await?;
let object = self.object.object.dereference(context).await?;

@ -57,7 +57,6 @@ impl ActivityHandler for UndoVote {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
@ -67,6 +66,7 @@ impl ActivityHandler for UndoVote {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let actor = self.actor.dereference(context).await?;
let object = self.object.object.dereference(context).await?;
match object {

@ -56,7 +56,6 @@ impl ActivityHandler for Vote {
#[tracing::instrument(skip_all)]
async fn verify(&self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let community = self.community(context).await?;
verify_person_in_community(&self.actor, &community, context).await?;
let enable_downvotes = LocalSite::read(&mut context.pool())
@ -72,6 +71,7 @@ impl ActivityHandler for Vote {
#[tracing::instrument(skip_all)]
async fn receive(self, context: &Data<LemmyContext>) -> Result<(), LemmyError> {
insert_received_activity(&self.id, context).await?;
let actor = self.actor.dereference(context).await?;
let object = self.object.dereference(context).await?;

@ -111,7 +111,6 @@ pub struct PostAggregates {
/// A newest comment time, limited to 2 days, to prevent necrobumping
pub newest_comment_time_necro: DateTime<Utc>,
/// The time of the newest comment in the post.
#[serde(skip)]
pub newest_comment_time: DateTime<Utc>,
/// If the post is featured on the community.
#[serde(skip)]

@ -64,6 +64,18 @@ impl Instance {
e => e,
}
}
pub async fn update(
pool: &mut DbPool<'_>,
instance_id: InstanceId,
form: InstanceForm,
) -> Result<usize, Error> {
let mut conn = get_conn(pool).await?;
diesel::update(instance::table.find(instance_id))
.set(form)
.execute(&mut conn)
.await
}
pub async fn delete(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?;
diesel::delete(instance::table.find(instance_id))

@ -11,7 +11,7 @@ use activitypub_federation::{
protocol::context::WithContext,
};
use anyhow::{Context, Result};
use chrono::{DateTime, TimeZone, Utc};
use chrono::{DateTime, Days, TimeZone, Utc};
use lemmy_api_common::{context::LemmyContext, federate_retry_sleep_duration};
use lemmy_apub::{activity_lists::SharedInboxActivities, FEDERATION_CONTEXT};
use lemmy_db_schema::{
@ -19,17 +19,17 @@ use lemmy_db_schema::{
source::{
activity::SentActivity,
federation_queue_state::FederationQueueState,
instance::Instance,
instance::{Instance, InstanceForm},
site::Site,
},
utils::DbPool,
utils::{naive_now, DbPool},
};
use lemmy_db_views_actor::structs::CommunityFollowerView;
use once_cell::sync::Lazy;
use reqwest::Url;
use std::{
collections::{HashMap, HashSet},
ops::Deref,
ops::{Add, Deref},
time::Duration,
};
use tokio::{sync::mpsc::UnboundedSender, time::sleep};
@ -257,6 +257,18 @@ impl InstanceWorker {
}
}
}
// Activity send successful, mark instance as alive if it hasn't been updated in a while.
let updated = self.instance.updated.unwrap_or(self.instance.published);
if updated.add(Days::new(1)) < Utc::now() {
self.instance.updated = Some(Utc::now());
let form = InstanceForm::builder()
.domain(self.instance.domain.clone())
.updated(Some(naive_now()))
.build();
Instance::update(pool, self.instance.id, form).await?;
}
}
Ok(())
}

@ -1,6 +1,6 @@
use lemmy_api_common::{claims::Claims, context::LemmyContext, utils::check_user_valid};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
use lemmy_utils::error::LemmyError;
pub mod feeds;
pub mod images;
@ -12,9 +12,7 @@ async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> Result<LocalUserView, LemmyError> {
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let local_user_id = Claims::validate(jwt, context).await?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;

@ -147,7 +147,7 @@ pub fn is_valid_matrix_id(matrix_id: &str) -> LemmyResult<()> {
}
pub fn is_valid_post_title(title: &str) -> LemmyResult<()> {
let length = title.trim().len();
let length = title.trim().chars().count();
let check = (3..=200).contains(&length) && !has_newline(title);
if !check {
Err(LemmyErrorType::InvalidPostTitle.into())
@ -380,6 +380,10 @@ mod tests {
#[test]
fn test_valid_post_title() {
assert!(is_valid_post_title("Post Title").is_ok());
assert!(is_valid_post_title(
"აშშ ითხოვს ირანს დაუყოვნებლივ გაანთავისუფლოს დაკავებული ნავთობის ტანკერი"
)
.is_ok());
assert!(is_valid_post_title(" POST TITLE 😃😃😃😃😃").is_ok());
assert!(is_valid_post_title("\n \n \n \n ").is_err()); // tabs/spaces/newlines
}

@ -1 +1 @@
Subproject commit 2139975ef383077e4709a4f2cae42922fd63b860
Subproject commit 3c217c609aa8826fc725f708221c8b3eb825f41a

@ -17,7 +17,7 @@ services:
- "8536:8536"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro,Z
restart: always
restart: unless-stopped
depends_on:
- pictrs
- lemmy-ui
@ -25,7 +25,7 @@ services:
lemmy:
# use "image" to pull down an already compiled lemmy. make sure to comment out "build".
# image: dessalines/lemmy:0.19.1
# image: dessalines/lemmy:0.19.2
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
# use "build" to build your local lemmy server image for development. make sure to comment out "image".
# run: docker compose up --build
@ -38,7 +38,7 @@ services:
# CARGO_BUILD_FEATURES: default
# this hostname is used in nginx reverse proxy and also for lemmy ui to connect to the backend, do not change
hostname: lemmy
restart: always
restart: unless-stopped
environment:
- RUST_LOG="warn,lemmy_server=debug,lemmy_api=debug,lemmy_api_common=debug,lemmy_api_crud=debug,lemmy_apub=debug,lemmy_db_schema=debug,lemmy_db_views=debug,lemmy_db_views_actor=debug,lemmy_db_views_moderator=debug,lemmy_routes=debug,lemmy_utils=debug,lemmy_websocket=debug"
- RUST_BACKTRACE=full
@ -55,7 +55,7 @@ services:
lemmy-ui:
# use "image" to pull down an already compiled lemmy-ui. make sure to comment out "build".
image: dessalines/lemmy-ui:0.19.1
image: dessalines/lemmy-ui:0.19.2
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
# use "build" to build your local lemmy ui image for development. make sure to comment out "image".
# run: docker compose up --build
@ -72,7 +72,7 @@ services:
- LEMMY_UI_DEBUG=true
depends_on:
- lemmy
restart: always
restart: unless-stopped
logging: *default-logging
init: true
@ -95,7 +95,7 @@ services:
user: 991:991
volumes:
- ./volumes/pictrs:/mnt:Z
restart: always
restart: unless-stopped
logging: *default-logging
postgres:
@ -127,5 +127,5 @@ services:
- POSTGRES_DB=lemmy
volumes:
- ./volumes/postgres:/var/lib/postgresql/data:Z
restart: always
restart: unless-stopped
logging: *default-logging

@ -2,7 +2,7 @@ version: "3.7"
x-ui-default: &ui-default
init: true
image: dessalines/lemmy-ui:0.19.1
image: dessalines/lemmy-ui:0.19.2
# assuming lemmy-ui is cloned besides lemmy directory
# build:
# context: ../../../lemmy-ui

@ -495,10 +495,7 @@ async fn update_instance_software(
}
};
if let Some(form) = form {
diesel::update(instance::table.find(instance.id))
.set(form)
.execute(&mut conn)
.await?;
Instance::update(pool, instance.id, form).await?;
}
}
info!("Finished updating instances software and versions...");

@ -7,14 +7,8 @@ use actix_web::{
};
use core::future::Ready;
use futures_util::future::LocalBoxFuture;
use lemmy_api::read_auth_token;
use lemmy_api_common::{
claims::Claims,
context::LemmyContext,
lemmy_db_views::structs::LocalUserView,
utils::check_user_valid,
};
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
use lemmy_api::{local_user_view_from_jwt, read_auth_token};
use lemmy_api_common::context::LemmyContext;
use reqwest::header::HeaderValue;
use std::{future::ready, rc::Rc};
@ -73,14 +67,14 @@ where
let jwt = read_auth_token(req.request())?;
if let Some(jwt) = &jwt {
let local_user_id = Claims::validate(jwt, &context)
.await
.with_lemmy_type(LemmyErrorType::IncorrectLogin)?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id)
.await
.map_err(LemmyError::from)?;
check_user_valid(&local_user_view.person)?;
req.extensions_mut().insert(local_user_view);
// Ignore any invalid auth so the site can still be used
// TODO: this means it will be impossible to get any error message for invalid jwt. Need
// to add a separate endpoint for that.
// https://github.com/LemmyNet/lemmy/issues/3702
let local_user_view = local_user_view_from_jwt(jwt, &context).await.ok();
if let Some(local_user_view) = local_user_view {
req.extensions_mut().insert(local_user_view);
}
}
let mut res = svc.call(req).await?;

Loading…
Cancel
Save