Merge branch 'main' into markdown-link-rule

markdown-link-rule
Felix Ableitner 2024-01-22 11:35:32 +01:00
commit 2fd7edd8ec
38 changed files with 156 additions and 174 deletions

View File

@ -39,25 +39,21 @@ steps:
- git submodule update - git submodule update
prettier_check: prettier_check:
group: format
image: tmknom/prettier:3.0.0 image: tmknom/prettier:3.0.0
commands: commands:
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations' - prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations'
toml_fmt: toml_fmt:
group: format
image: tamasfe/taplo:0.8.1 image: tamasfe/taplo:0.8.1
commands: commands:
- taplo format --check - taplo format --check
sql_fmt: sql_fmt:
group: format
image: backplane/pgformatter:latest image: backplane/pgformatter:latest
commands: commands:
- ./scripts/sql_format_check.sh - ./scripts/sql_format_check.sh
cargo_fmt: cargo_fmt:
group: format
image: rustlang/rust:nightly image: rustlang/rust:nightly
environment: environment:
# store cargo data in repo folder so that it gets cached between steps # store cargo data in repo folder so that it gets cached between steps
@ -67,7 +63,6 @@ steps:
- cargo +nightly fmt -- --check - cargo +nightly fmt -- --check
cargo_machete: cargo_machete:
group: format
image: rustlang/rust:nightly image: rustlang/rust:nightly
commands: commands:
- wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz - 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 - cargo machete
ignored_files: ignored_files:
group: format
image: alpine:3 image: alpine:3
commands: commands:
- apk add git - apk add git
- IGNORED=$(git ls-files --cached -i --exclude-standard) - IGNORED=$(git ls-files --cached -i --exclude-standard)
- if [[ "$IGNORED" ]]; then echo "Ignored files present:\n$IGNORED\n"; exit 1; fi - 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) # make sure api builds with default features (used by other crates relying on lemmy api)
check_api_common_default_features: check_api_common_default_features:
image: *rust_image image: *rust_image
@ -188,7 +155,6 @@ steps:
when: *slow_check_paths when: *slow_check_paths
cargo_test: cargo_test:
group: tests
image: *rust_image image: *rust_image
environment: environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
@ -200,7 +166,6 @@ steps:
when: *slow_check_paths when: *slow_check_paths
run_federation_tests: run_federation_tests:
group: tests
image: node:20-bookworm-slim image: node:20-bookworm-slim
environment: environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432 LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432
@ -213,35 +178,6 @@ steps:
- yarn api-test - yarn api-test
when: *slow_check_paths 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: publish_release_docker:
image: woodpeckerci/plugin-docker-buildx image: woodpeckerci/plugin-docker-buildx
secrets: [docker_username, docker_password] secrets: [docker_username, docker_password]

24
Cargo.lock generated
View File

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

View File

@ -1,6 +1,5 @@
[workspace.package] [workspace.package]
version = "0.19.2-rc.5" version = "0.19.3-rc.1"
publish = false
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"
@ -17,6 +16,7 @@ license.workspace = true
homepage.workspace = true homepage.workspace = true
documentation.workspace = true documentation.workspace = true
repository.workspace = true repository.workspace = true
publish = false
[lib] [lib]
doctest = false doctest = false
@ -85,16 +85,16 @@ unused_self = "deny"
unwrap_used = "deny" unwrap_used = "deny"
[workspace.dependencies] [workspace.dependencies]
lemmy_api = { version = "=0.19.2-rc.5", path = "./crates/api" } lemmy_api = { version = "=0.19.3-rc.1", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.2-rc.5", path = "./crates/api_crud" } lemmy_api_crud = { version = "=0.19.3-rc.1", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.2-rc.5", path = "./crates/apub" } lemmy_apub = { version = "=0.19.3-rc.1", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.2-rc.5", path = "./crates/utils" } lemmy_utils = { version = "=0.19.3-rc.1", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.2-rc.5", path = "./crates/db_schema" } lemmy_db_schema = { version = "=0.19.3-rc.1", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.2-rc.5", path = "./crates/api_common" } lemmy_api_common = { version = "=0.19.3-rc.1", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.2-rc.5", path = "./crates/routes" } lemmy_routes = { version = "=0.19.3-rc.1", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.2-rc.5", path = "./crates/db_views" } lemmy_db_views = { version = "=0.19.3-rc.1", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.2-rc.5", path = "./crates/db_views_actor" } lemmy_db_views_actor = { version = "=0.19.3-rc.1", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.2-rc.5", path = "./crates/db_views_moderator" } 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 = [ activitypub_federation = { version = "0.5.1-beta.1", default-features = false, features = [
"actix-web", "actix-web",
] } ] }
@ -166,7 +166,7 @@ lemmy_utils = { workspace = true }
lemmy_db_schema = { workspace = true } lemmy_db_schema = { workspace = true }
lemmy_api_common = { workspace = true } lemmy_api_common = { workspace = true }
lemmy_routes = { 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 } activitypub_federation = { workspace = true }
diesel = { workspace = true } diesel = { workspace = true }
diesel-async = { workspace = true } diesel-async = { workspace = true }

View File

@ -86,7 +86,12 @@ test("Create a post", async () => {
throw "Missing beta community"; 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.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false); expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true); expect(postRes.post_view.creator.local).toBe(true);

View File

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

View File

@ -112,19 +112,18 @@ test("Delete user", async () => {
).toBe(true); ).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, { let invalid_auth = new LemmyHttp(alphaUrl, {
headers: { Authorization: "Bearer foobar" }, headers: { Authorization: "Bearer foobar" },
fetchFunction, fetchFunction,
}); });
await expect(getSite(invalid_auth)).rejects.toStrictEqual( let site = await getSite(invalid_auth);
Error("incorrect_login"), expect(site.my_user).toBeUndefined();
); expect(site.site_view).toBeDefined();
let form: GetPosts = {}; let form: GetPosts = {};
await expect(invalid_auth.getPosts(form)).rejects.toStrictEqual( let posts = invalid_auth.getPosts(form);
Error("incorrect_login"), expect((await posts).posts).toBeDefined();
);
}); });
test("Create user with Arabic name", async () => { test("Create user with Arabic name", async () => {

View File

@ -2,11 +2,15 @@ use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer}; use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine}; use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha; 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_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{ use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult}, error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
utils::slurs::check_slurs, utils::slurs::check_slurs,
}; };
use std::io::Cursor; use std::io::Cursor;
@ -137,6 +141,20 @@ pub(crate) fn build_totp_2fa(
.with_lemmy_type(LemmyErrorType::CouldntGenerateTotp) .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)] #[cfg(test)]
mod tests { mod tests {
#![allow(clippy::unwrap_used)] #![allow(clippy::unwrap_used)]

View File

@ -1,5 +1,13 @@
use crate::captcha_as_wav_base64; 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 captcha::{gen, Difficulty};
use lemmy_api_common::{ use lemmy_api_common::{
context::LemmyContext, context::LemmyContext,
@ -12,13 +20,13 @@ use lemmy_db_schema::source::{
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
pub async fn get_captcha( pub async fn get_captcha(context: Data<LemmyContext>) -> Result<HttpResponse, LemmyError> {
context: Data<LemmyContext>,
) -> Result<Json<GetCaptchaResponse>, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?; 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 { 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() { 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 // Stores the captcha item in the db
let captcha = CaptchaAnswer::insert(&mut context.pool(), &captcha_form).await?; let captcha = CaptchaAnswer::insert(&mut context.pool(), &captcha_form).await?;
Ok(Json(GetCaptchaResponse { let json = Json(GetCaptchaResponse {
ok: Some(CaptchaResponse { ok: Some(CaptchaResponse {
png, png,
wav, wav,
uuid: captcha.uuid.to_string(), uuid: captcha.uuid.to_string(),
}), }),
})) });
Ok(res.json(json))
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -64,6 +64,18 @@ impl Instance {
e => e, 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> { pub async fn delete(pool: &mut DbPool<'_>, instance_id: InstanceId) -> Result<usize, Error> {
let conn = &mut get_conn(pool).await?; let conn = &mut get_conn(pool).await?;
diesel::delete(instance::table.find(instance_id)) diesel::delete(instance::table.find(instance_id))

View File

@ -11,7 +11,7 @@ use activitypub_federation::{
protocol::context::WithContext, protocol::context::WithContext,
}; };
use anyhow::{Context, Result}; 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_api_common::{context::LemmyContext, federate_retry_sleep_duration};
use lemmy_apub::{activity_lists::SharedInboxActivities, FEDERATION_CONTEXT}; use lemmy_apub::{activity_lists::SharedInboxActivities, FEDERATION_CONTEXT};
use lemmy_db_schema::{ use lemmy_db_schema::{
@ -19,17 +19,17 @@ use lemmy_db_schema::{
source::{ source::{
activity::SentActivity, activity::SentActivity,
federation_queue_state::FederationQueueState, federation_queue_state::FederationQueueState,
instance::Instance, instance::{Instance, InstanceForm},
site::Site, site::Site,
}, },
utils::DbPool, utils::{naive_now, DbPool},
}; };
use lemmy_db_views_actor::structs::CommunityFollowerView; use lemmy_db_views_actor::structs::CommunityFollowerView;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use reqwest::Url; use reqwest::Url;
use std::{ use std::{
collections::{HashMap, HashSet}, collections::{HashMap, HashSet},
ops::Deref, ops::{Add, Deref},
time::Duration, time::Duration,
}; };
use tokio::{sync::mpsc::UnboundedSender, time::sleep}; 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(()) Ok(())
} }

View File

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

View File

@ -147,7 +147,7 @@ pub fn is_valid_matrix_id(matrix_id: &str) -> LemmyResult<()> {
} }
pub fn is_valid_post_title(title: &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); let check = (3..=200).contains(&length) && !has_newline(title);
if !check { if !check {
Err(LemmyErrorType::InvalidPostTitle.into()) Err(LemmyErrorType::InvalidPostTitle.into())
@ -380,6 +380,10 @@ mod tests {
#[test] #[test]
fn test_valid_post_title() { fn test_valid_post_title() {
assert!(is_valid_post_title("Post Title").is_ok()); 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(" POST TITLE 😃😃😃😃😃").is_ok());
assert!(is_valid_post_title("\n \n \n \n ").is_err()); // tabs/spaces/newlines assert!(is_valid_post_title("\n \n \n \n ").is_err()); // tabs/spaces/newlines
} }

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

View File

@ -17,7 +17,7 @@ services:
- "8536:8536" - "8536:8536"
volumes: volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro,Z - ./nginx.conf:/etc/nginx/nginx.conf:ro,Z
restart: always restart: unless-stopped
depends_on: depends_on:
- pictrs - pictrs
- lemmy-ui - lemmy-ui
@ -25,7 +25,7 @@ services:
lemmy: lemmy:
# use "image" to pull down an already compiled lemmy. make sure to comment out "build". # 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. # 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". # use "build" to build your local lemmy server image for development. make sure to comment out "image".
# run: docker compose up --build # run: docker compose up --build
@ -38,7 +38,7 @@ services:
# CARGO_BUILD_FEATURES: default # 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 # this hostname is used in nginx reverse proxy and also for lemmy ui to connect to the backend, do not change
hostname: lemmy hostname: lemmy
restart: always restart: unless-stopped
environment: 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_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 - RUST_BACKTRACE=full
@ -55,7 +55,7 @@ services:
lemmy-ui: lemmy-ui:
# use "image" to pull down an already compiled lemmy-ui. make sure to comment out "build". # 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. # 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". # use "build" to build your local lemmy ui image for development. make sure to comment out "image".
# run: docker compose up --build # run: docker compose up --build
@ -72,7 +72,7 @@ services:
- LEMMY_UI_DEBUG=true - LEMMY_UI_DEBUG=true
depends_on: depends_on:
- lemmy - lemmy
restart: always restart: unless-stopped
logging: *default-logging logging: *default-logging
init: true init: true
@ -95,7 +95,7 @@ services:
user: 991:991 user: 991:991
volumes: volumes:
- ./volumes/pictrs:/mnt:Z - ./volumes/pictrs:/mnt:Z
restart: always restart: unless-stopped
logging: *default-logging logging: *default-logging
postgres: postgres:
@ -127,5 +127,5 @@ services:
- POSTGRES_DB=lemmy - POSTGRES_DB=lemmy
volumes: volumes:
- ./volumes/postgres:/var/lib/postgresql/data:Z - ./volumes/postgres:/var/lib/postgresql/data:Z
restart: always restart: unless-stopped
logging: *default-logging logging: *default-logging

View File

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

View File

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

View File

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