diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3d1bd7c72..cdf891126 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,3 +1,3 @@ -* @Nutomic @dessalines @phiresky +* @Nutomic @dessalines @phiresky @dullbananas crates/apub/ @Nutomic -migrations/ @dessalines @phiresky +migrations/ @dessalines @phiresky @dullbananas diff --git a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml index a4028afd0..3d3caa261 100644 --- a/.github/ISSUE_TEMPLATE/BUG_REPORT.yml +++ b/.github/ISSUE_TEMPLATE/BUG_REPORT.yml @@ -20,6 +20,8 @@ body: required: true - label: Is this only a single bug? Do not put multiple bugs in one issue. required: true + - label: Do you agree to follow the rules in our [Code of Conduct](https://join-lemmy.org/docs/code_of_conduct.html)? + required: true - label: Is this a backend issue? Use the [lemmy-ui](https://github.com/LemmyNet/lemmy-ui) repo for UI / frontend issues. required: true - type: textarea diff --git a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml index 40ef2caf3..f50a93ff2 100644 --- a/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml +++ b/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.yml @@ -20,6 +20,8 @@ body: required: true - label: Is this a backend issue? Use the [lemmy-ui](https://github.com/LemmyNet/lemmy-ui) repo for UI / frontend issues. required: true + - label: Do you agree to follow the rules in our [Code of Conduct](https://join-lemmy.org/docs/code_of_conduct.html)? + required: true - type: textarea id: problem attributes: diff --git a/Cargo.lock b/Cargo.lock index a5194f393..b9ebfa540 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,9 +16,9 @@ checksum = "8f27d075294830fcab6f66e320dab524bc6d048f4a151698e153205559113772" [[package]] name = "activitypub_federation" -version = "0.5.1-beta.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "866db431760d14a7360f12e75ad48f3265b5b89cd2303e548a02bcc8983e4fcd" +checksum = "a028034c642d3ed16b535f98f48b3df30397833c183d68852d79de16650d5ed5" dependencies = [ "activitystreams-kinds", "actix-web", @@ -35,7 +35,7 @@ dependencies = [ "http-signature-normalization", "http-signature-normalization-reqwest", "httpdate", - "itertools 0.12.0", + "itertools 0.12.1", "moka", "once_cell", "openssl", @@ -112,9 +112,9 @@ dependencies = [ [[package]] name = "actix-http" -version = "3.5.1" +version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "129d4c88e98860e1758c5de288d1632b07970a16d59bdf7b8d66053d582bb71f" +checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" dependencies = [ "actix-codec", "actix-rt", @@ -236,9 +236,9 @@ dependencies = [ [[package]] name = "actix-tls" -version = "3.2.0" +version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "929e47cc23865cdb856e59673cfba2d28f00b3bbd060dfc80e33a00a3cea8317" +checksum = "d4cce60a2f2b477bc72e5cde0af1812a6e82d8fd85b5570a5dcf2a5bf2c5be5f" dependencies = [ "actix-rt", "actix-service", @@ -265,9 +265,9 @@ dependencies = [ [[package]] name = "actix-web" -version = "4.4.1" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e43428f3bf11dee6d166b00ec2df4e3aa8cc1606aaa0b7433c146852e2f4e03b" +checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" dependencies = [ "actix-codec", "actix-http", @@ -851,9 +851,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "chrono" -version = "0.4.32" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41daef31d7a747c5c847246f36de49ced6f7403b4cdabc807a97b5cc184cda7a" +checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" dependencies = [ "android-tzdata", "iana-time-zone", @@ -1564,9 +1564,9 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" [[package]] name = "dyn-clone" -version = "1.0.16" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "545b22097d44f8a9581187cdf93de7a71e4722bf51200cfaba810865b49a495d" +checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" [[package]] name = "either" @@ -2026,7 +2026,7 @@ dependencies = [ "futures-sink", "futures-util", "http", - "indexmap 2.1.0", + "indexmap 2.2.5", "slab", "tokio", "tokio-util", @@ -2401,9 +2401,9 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.1.0" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +checksum = "7b0b929d511467233429c45a44ac1dcaa21ba0f5ba11e4879e6ed28ddb4f9df4" dependencies = [ "equivalent", "hashbrown 0.14.3", @@ -2465,9 +2465,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.12.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] @@ -2577,8 +2577,6 @@ version = "0.19.3" dependencies = [ "activitypub_federation", "actix-web", - "anyhow", - "async-trait", "chrono", "encoding", "enum-map", @@ -2600,7 +2598,6 @@ dependencies = [ "serde", "serde_with", "serial_test", - "task-local-extensions", "tokio", "tracing", "ts-rs", @@ -2649,7 +2646,7 @@ dependencies = [ "html2md", "html2text", "http", - "itertools 0.12.0", + "itertools 0.12.1", "lemmy_api_common", "lemmy_db_schema", "lemmy_db_views", @@ -2884,7 +2881,7 @@ dependencies = [ "futures", "html2text", "http", - "itertools 0.12.0", + "itertools 0.12.1", "lettre", "markdown-it", "once_cell", @@ -3236,9 +3233,9 @@ dependencies = [ [[package]] name = "moka" -version = "0.12.4" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad9dc9808102655926a6086abd0b9965ebefd4a39ef0d184f074c34ba5049ec6" +checksum = "b1911e88d5831f748a4097a43862d129e3c6fca831eecac9b8db6d01d93c9de2" dependencies = [ "async-lock", "async-trait", @@ -3380,9 +3377,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" -version = "0.10.63" +version = "0.10.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15c9d69dd87a29568d4d017cfe8ec518706046a05184e5aea92d0af890b803c8" +checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" dependencies = [ "bitflags 2.4.2", "cfg-if", @@ -3412,9 +3409,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.99" +version = "0.9.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e1bf214306098e4832460f797824c05d25aacdf896f64a985fb0fd992454ae" +checksum = "dda2b0f344e78efc2facf7d195d098df0dd72151b26ab98da807afc26c198dff" dependencies = [ "cc", "libc", @@ -3457,7 +3454,7 @@ checksum = "1e32339a5dc40459130b3bd269e9892439f55b33e772d2a9d402a789baaf4e8a" dependencies = [ "futures-core", "futures-sink", - "indexmap 2.1.0", + "indexmap 2.2.5", "js-sys", "once_cell", "pin-project-lite", @@ -3854,7 +3851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5699cc8a63d1aa2b1ee8e12b9ad70ac790d65788cd36101fa37f87ea46c4cef" dependencies = [ "base64 0.21.7", - "indexmap 2.1.0", + "indexmap 2.2.5", "line-wrap", "quick-xml 0.31.0", "serde", @@ -4308,9 +4305,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" [[package]] name = "reqwest" -version = "0.11.23" +version = "0.11.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41" +checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251" dependencies = [ "async-compression", "base64 0.21.7", @@ -4338,6 +4335,7 @@ dependencies = [ "serde", "serde_json", "serde_urlencoded", + "sync_wrapper", "system-configuration", "tokio", "tokio-native-tls", @@ -4690,9 +4688,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" +checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] @@ -4708,9 +4706,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.195" +version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" +checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", @@ -4719,11 +4717,11 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.111" +version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" +checksum = "c5f09b1bd632ef549eaa9f60a1f8de742bdbc698e6cee2095fc84dde5f549ae0" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "itoa", "ryu", "serde", @@ -4769,7 +4767,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", "serde_json", "serde_with_macros", @@ -5204,18 +5202,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.56" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -5284,9 +5282,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.35.1" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -5467,7 +5465,7 @@ version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", "serde_spanned", "toml_datetime", @@ -5480,7 +5478,7 @@ version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" dependencies = [ - "indexmap 2.1.0", + "indexmap 2.2.5", "serde", "serde_spanned", "toml_datetime", @@ -6068,9 +6066,9 @@ checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "wasm-streams" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4609d447824375f43e1ffbc051b50ad8f4b3ae8219680c94452ea05eb240ac7" +checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" dependencies = [ "futures-util", "js-sys", diff --git a/Cargo.toml b/Cargo.toml index 98bf79b8c..5e0cdb16e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -96,7 +96,7 @@ lemmy_routes = { version = "=0.19.3", path = "./crates/routes" } lemmy_db_views = { version = "=0.19.3", path = "./crates/db_views" } lemmy_db_views_actor = { version = "=0.19.3", path = "./crates/db_views_actor" } lemmy_db_views_moderator = { version = "=0.19.3", path = "./crates/db_views_moderator" } -activitypub_federation = { version = "0.5.1-beta.1", default-features = false, features = [ +activitypub_federation = { version = "0.5.2", default-features = false, features = [ "actix-web", ] } diesel = "2.1.4" diff --git a/api_tests/src/image.spec.ts b/api_tests/src/image.spec.ts index 65b60326a..7f736cf7e 100644 --- a/api_tests/src/image.spec.ts +++ b/api_tests/src/image.spec.ts @@ -68,6 +68,10 @@ test("Upload image and delete it", async () => { // ensure that image is deleted const content2 = downloadFileSync(upload.url); expect(content2).toBe(""); + + // Ensure that it shows the image is deleted + const deletedListMediaRes = await alphaImage.listMedia({}); + expect(deletedListMediaRes.images.length).toBe(0); }); test("Purge user, uploaded image removed", async () => { diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 000000000..d8975a171 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,86 @@ +# git-cliff ~ configuration file +# https://git-cliff.org/docs/configuration + +[remote.github] +owner = "LemmyNet" +repo = "lemmy" +# token = "" + +[changelog] +# template for the changelog body +# https://keats.github.io/tera/docs/#introduction +body = """ +## What's Changed + +{%- if version %} in {{ version }}{%- endif -%} +{% for commit in commits %} + {% if commit.github.pr_title -%} + {%- set commit_message = commit.github.pr_title -%} + {%- else -%} + {%- set commit_message = commit.message -%} + {%- endif -%} + * {{ commit_message | split(pat="\n") | first | trim }}\ + {% if commit.github.username %} by @{{ commit.github.username }}{%- endif -%} + {% if commit.github.pr_number %} in \ + [#{{ commit.github.pr_number }}]({{ self::remote_url() }}/pull/{{ commit.github.pr_number }}) \ + {%- endif %} +{%- endfor -%} + +{% if github.contributors | filter(attribute="is_first_time", value=true) | length != 0 %} + {% raw %}\n{% endraw -%} + ## New Contributors +{%- endif %}\ +{% for contributor in github.contributors | filter(attribute="is_first_time", value=true) %} + * @{{ contributor.username }} made their first contribution + {%- if contributor.pr_number %} in \ + [#{{ contributor.pr_number }}]({{ self::remote_url() }}/pull/{{ contributor.pr_number }}) \ + {%- endif %} +{%- endfor -%} + +{% if version %} + {% if previous.version %} + **Full Changelog**: {{ self::remote_url() }}/compare/{{ previous.version }}...{{ version }} + {% endif %} +{% else -%} + {% raw %}\n{% endraw %} +{% endif %} + +{%- macro remote_url() -%} + https://github.com/{{ remote.github.owner }}/{{ remote.github.repo }} +{%- endmacro -%} +""" +# remove the leading and trailing whitespace from the template +trim = true +# changelog footer +footer = """ + +""" +# postprocessors +postprocessors = [] + +[git] +# parse the commits based on https://www.conventionalcommits.org +conventional_commits = false +# filter out the commits that are not conventional +filter_unconventional = true +# process each line of a commit as an individual commit +split_commits = false +# regex for preprocessing the commit messages +commit_preprocessors = [ + # remove issue numbers from commits + { pattern = '\((\w+\s)?#([0-9]+)\)', replace = "" }, +] +# protect breaking changes from being skipped due to matching a skipping commit_parser +protect_breaking_commits = false +# filter out the commits that are not matched by commit parsers +filter_commits = false +# regex for matching git tags +tag_pattern = "[0-9].*" +# regex for skipping tags +skip_tags = "beta|alpha" +# regex for ignoring tags +ignore_tags = "rc" +# sort the tags topologically +topo_order = false +# sort the commits inside sections by oldest/newest order +sort_commits = "newest" diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index fda0bea6f..814dd67eb 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -135,11 +135,7 @@ pub(crate) fn generate_totp_2fa_secret() -> String { Secret::generate_secret().to_string() } -pub(crate) fn build_totp_2fa( - site_name: &str, - username: &str, - secret: &str, -) -> Result { +fn build_totp_2fa(hostname: &str, username: &str, secret: &str) -> Result { let sec = Secret::Raw(secret.as_bytes().to_vec()); let sec_bytes = sec .to_bytes() @@ -151,7 +147,7 @@ pub(crate) fn build_totp_2fa( 1, 30, sec_bytes, - Some(site_name.to_string()), + Some(hostname.to_string()), username.to_string(), ) .with_lemmy_type(LemmyErrorType::CouldntGenerateTotp) @@ -272,7 +268,7 @@ mod tests { #[test] fn test_build_totp() { let generated_secret = generate_totp_2fa_secret(); - let totp = build_totp_2fa("lemmy", "my_name", &generated_secret); + let totp = build_totp_2fa("lemmy.ml", "my_name", &generated_secret); assert!(totp.is_ok()); } } diff --git a/crates/api/src/local_user/list_media.rs b/crates/api/src/local_user/list_media.rs index 446ce00a5..8bb26bd14 100644 --- a/crates/api/src/local_user/list_media.rs +++ b/crates/api/src/local_user/list_media.rs @@ -17,11 +17,12 @@ pub async fn list_media( ) -> Result, LemmyError> { let page = data.page; let limit = data.limit; - let images = LocalImage::get_all_paged_by_local_user_id( + let images = LocalImage::get_all_by_local_user_id( &mut context.pool(), local_user_view.local_user.id, page, limit, + false, ) .await?; Ok(Json(ListMediaResponse { images })) diff --git a/crates/api/src/local_user/login.rs b/crates/api/src/local_user/login.rs index 1fe337f3c..4eae762be 100644 --- a/crates/api/src/local_user/login.rs +++ b/crates/api/src/local_user/login.rs @@ -50,7 +50,11 @@ pub async fn login( // Check the totp if enabled if local_user_view.local_user.totp_2fa_enabled { - check_totp_2fa_valid(&local_user_view, &data.totp_2fa_token, &site_view.site.name)?; + check_totp_2fa_valid( + &local_user_view, + &data.totp_2fa_token, + &context.settings().hostname, + )?; } let jwt = Claims::generate(local_user_view.local_user.id, req, &context).await?; diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs index 79b95133e..d918bdc00 100644 --- a/crates/api/src/local_user/save_settings.rs +++ b/crates/api/src/local_user/save_settings.rs @@ -14,6 +14,7 @@ use lemmy_db_schema::{ source::{ actor_language::LocalUserLanguage, local_user::{LocalUser, LocalUserUpdateForm}, + local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm}, person::{Person, PersonUpdateForm}, }, traits::Crud, @@ -136,5 +137,15 @@ pub async fn save_user_settings( .await .ok(); + // Update the vote display modes + let vote_display_modes_form = LocalUserVoteDisplayModeUpdateForm { + score: data.show_scores, + upvotes: data.show_upvotes, + downvotes: data.show_downvotes, + upvote_percentage: data.show_upvote_percentage, + }; + LocalUserVoteDisplayMode::update(&mut context.pool(), local_user_id, &vote_display_modes_form) + .await?; + Ok(Json(SuccessResponse::default())) } diff --git a/crates/api/src/local_user/update_totp.rs b/crates/api/src/local_user/update_totp.rs index 15833ae8a..8f37213e2 100644 --- a/crates/api/src/local_user/update_totp.rs +++ b/crates/api/src/local_user/update_totp.rs @@ -8,7 +8,7 @@ use lemmy_db_schema::{ source::local_user::{LocalUser, LocalUserUpdateForm}, traits::Crud, }; -use lemmy_db_views::structs::{LocalUserView, SiteView}; +use lemmy_db_views::structs::LocalUserView; use lemmy_utils::error::LemmyError; /// Enable or disable two-factor-authentication. The current setting is determined from @@ -25,12 +25,10 @@ pub async fn update_totp( local_user_view: LocalUserView, context: Data, ) -> Result, LemmyError> { - let site_view = SiteView::read_local(&mut context.pool()).await?; - check_totp_2fa_valid( &local_user_view, &Some(data.totp_token.clone()), - &site_view.site.name, + &context.settings().hostname, )?; // toggle the 2fa setting diff --git a/crates/api/src/site/purge/person.rs b/crates/api/src/site/purge/person.rs index 658846eab..7af01e139 100644 --- a/crates/api/src/site/purge/person.rs +++ b/crates/api/src/site/purge/person.rs @@ -31,8 +31,14 @@ pub async fn purge_person( // Read the local user to get their images, and delete them if let Ok(local_user) = LocalUserView::read_person(&mut context.pool(), data.person_id).await { - let pictrs_uploads = - LocalImage::get_all_by_local_user_id(&mut context.pool(), local_user.local_user.id).await?; + let pictrs_uploads = LocalImage::get_all_by_local_user_id( + &mut context.pool(), + local_user.local_user.id, + None, + None, + true, + ) + .await?; for upload in pictrs_uploads { delete_image_from_pictrs(&upload.pictrs_alias, &upload.pictrs_delete_token, &context) diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml index 0192099f3..3acd7d4ca 100644 --- a/crates/api_common/Cargo.toml +++ b/crates/api_common/Cargo.toml @@ -54,7 +54,6 @@ tracing = { workspace = true, optional = true } reqwest-middleware = { workspace = true, optional = true } regex = { workspace = true } rosetta-i18n = { workspace = true, optional = true } -anyhow = { workspace = true } futures = { workspace = true, optional = true } uuid = { workspace = true, optional = true } tokio = { workspace = true, optional = true } @@ -64,7 +63,6 @@ once_cell = { workspace = true, optional = true } actix-web = { workspace = true, optional = true } enum-map = { workspace = true } urlencoding = { workspace = true } -async-trait = { workspace = true } mime = { version = "0.3.17", optional = true } webpage = { version = "1.6", default-features = false, features = [ "serde", @@ -73,7 +71,6 @@ encoding = { version = "0.2.33", optional = true } jsonwebtoken = { version = "8.3.0", optional = true } # necessary for wasmt compilation getrandom = { version = "0.2.12", features = ["js"] } -task-local-extensions = "0.1.4" [package.metadata.cargo-machete] ignored = ["getrandom"] diff --git a/crates/api_common/src/build_response.rs b/crates/api_common/src/build_response.rs index 5ca819334..6fb96e113 100644 --- a/crates/api_common/src/build_response.rs +++ b/crates/api_common/src/build_response.rs @@ -87,7 +87,7 @@ pub async fn build_post_response( Ok(Json(PostResponse { post_view })) } -// TODO: this function is a mess and should be split up to handle email seperately +// TODO: this function is a mess and should be split up to handle email separately #[tracing::instrument(skip_all)] pub async fn send_local_notifs( mentions: Vec, diff --git a/crates/api_common/src/context.rs b/crates/api_common/src/context.rs index 8d8dc5013..ba9eb4074 100644 --- a/crates/api_common/src/context.rs +++ b/crates/api_common/src/context.rs @@ -1,6 +1,5 @@ use crate::request::client_builder; use activitypub_federation::config::{Data, FederationConfig}; -use anyhow::anyhow; use lemmy_db_schema::{ source::secret::Secret, utils::{build_db_pool_for_tests, ActualDbPool, DbPool}, @@ -9,10 +8,8 @@ use lemmy_utils::{ rate_limit::RateLimitCell, settings::{structs::Settings, SETTINGS}, }; -use reqwest::{Request, Response}; -use reqwest_middleware::{ClientBuilder, ClientWithMiddleware, Middleware, Next}; +use reqwest_middleware::{ClientBuilder, ClientWithMiddleware}; use std::sync::Arc; -use task_local_extensions::Extensions; #[derive(Clone)] pub struct LemmyContext { @@ -55,32 +52,16 @@ impl LemmyContext { &self.rate_limit_cell } - /// Initialize a context for use in tests, optionally blocks network requests. + /// Initialize a context for use in tests which blocks federation network calls. /// /// Do not use this in production code. pub async fn init_test_context() -> Data { - Self::build_test_context(true).await - } - - /// Initialize a context for use in tests, with network requests allowed. - /// TODO: get rid of this if possible. - /// - /// Do not use this in production code. - pub async fn init_test_context_with_networking() -> Data { - Self::build_test_context(false).await - } - - async fn build_test_context(block_networking: bool) -> Data { // call this to run migrations let pool = build_db_pool_for_tests().await; let client = client_builder(&SETTINGS).build().expect("build client"); - let mut client = ClientBuilder::new(client); - if block_networking { - client = client.with(BlockedMiddleware); - } - let client = client.build(); + let client = ClientBuilder::new(client).build(); let secret = Secret { id: 0, jwt_secret: String::new(), @@ -92,24 +73,11 @@ impl LemmyContext { let config = FederationConfig::builder() .domain(context.settings().hostname.clone()) .app_data(context) + // Dont allow any network fetches + .http_fetch_limit(0) .build() .await .expect("build federation config"); config.to_request_data() } } - -struct BlockedMiddleware; - -/// A reqwest middleware which blocks all requests -#[async_trait::async_trait] -impl Middleware for BlockedMiddleware { - async fn handle( - &self, - _req: Request, - _extensions: &mut Extensions, - _next: Next<'_>, - ) -> reqwest_middleware::Result { - Err(anyhow!("Network requests not allowed").into()) - } -} diff --git a/crates/api_common/src/person.rs b/crates/api_common/src/person.rs index f46bda1cc..a4f9b64d9 100644 --- a/crates/api_common/src/person.rs +++ b/crates/api_common/src/person.rs @@ -86,8 +86,6 @@ pub struct SaveUserSettings { pub show_nsfw: Option, pub blur_nsfw: Option, pub auto_expand: Option, - /// Show post and comment scores. - pub show_scores: Option, /// Your user's theme. pub theme: Option, pub default_sort_type: Option, @@ -122,6 +120,7 @@ pub struct SaveUserSettings { pub open_links_in_new_tab: Option, /// Enable infinite scroll pub infinite_scroll_enabled: Option, + /// A post-view mode that changes how multiple post listings look. pub post_listing_mode: Option, /// Whether to allow keyboard navigation (for browsing and interacting with posts and comments). pub enable_keyboard_navigation: Option, @@ -129,6 +128,11 @@ pub struct SaveUserSettings { pub enable_animated_images: Option, /// Whether to auto-collapse bot comments. pub collapse_bot_comments: Option, + /// Some vote display mode settings + pub show_scores: Option, + pub show_upvotes: Option, + pub show_downvotes: Option, + pub show_upvote_percentage: Option, } #[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq, Eq, Hash)] diff --git a/crates/api_common/src/request.rs b/crates/api_common/src/request.rs index 1eab50bc5..aaae7f866 100644 --- a/crates/api_common/src/request.rs +++ b/crates/api_common/src/request.rs @@ -4,7 +4,10 @@ use crate::{ utils::proxy_image_link, }; use encoding::{all::encodings, DecoderTrap}; -use lemmy_db_schema::newtypes::DbUrl; +use lemmy_db_schema::{ + newtypes::DbUrl, + source::images::{LocalImage, LocalImageForm}, +}; use lemmy_utils::{ error::{LemmyError, LemmyErrorType}, settings::structs::{PictrsImageMode, Settings}, @@ -184,7 +187,6 @@ struct PictrsResponse { #[derive(Deserialize, Debug)] struct PictrsFile { file: String, - #[allow(dead_code)] delete_token: String, } @@ -287,6 +289,14 @@ async fn generate_pictrs_thumbnail( context.settings().get_protocol_and_hostname(), response.files.first().expect("missing pictrs file").file ))?; + for uploaded_image in response.files { + let form = LocalImageForm { + local_user_id: None, + pictrs_alias: uploaded_image.file.to_string(), + pictrs_delete_token: uploaded_image.delete_token.to_string(), + }; + LocalImage::create(&mut context.pool(), &form).await?; + } Ok(thumbnail_url) } else { Err(LemmyErrorType::PictrsResponseError(response.msg))? @@ -327,7 +337,7 @@ mod tests { #[tokio::test] #[serial] async fn test_link_metadata() { - let context = LemmyContext::init_test_context_with_networking().await; + let context = LemmyContext::init_test_context().await; let sample_url = Url::parse("https://gitlab.com/IzzyOnDroid/repo/-/wikis/FAQ").unwrap(); let sample_res = fetch_link_metadata(&sample_url, false, &context) .await diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index 50df1edbf..29e16cb20 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -21,6 +21,7 @@ use lemmy_db_schema::{ source::{ captcha_answer::{CaptchaAnswer, CheckCaptchaAnswer}, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, registration_application::{RegistrationApplication, RegistrationApplicationInsertForm}, }, @@ -183,6 +184,7 @@ pub async fn register( if local_site.require_email_verification { let local_user_view = LocalUserView { local_user: inserted_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_person, counts: PersonAggregates::default(), }; diff --git a/crates/apub/assets/lemmy/objects/instance.json b/crates/apub/assets/lemmy/objects/instance.json index 1e07043d9..92053bac6 100644 --- a/crates/apub/assets/lemmy/objects/instance.json +++ b/crates/apub/assets/lemmy/objects/instance.json @@ -2,6 +2,7 @@ "type": "Application", "id": "https://enterprise.lemmy.ml/", "name": "Enterprise", + "preferredUsername": "enterprise.lemmy.ml", "summary": "A test instance", "content": "

Enterprise sidebar

\\n", "mediaType": "text/html", diff --git a/crates/apub/assets/mastodon/objects/page.json b/crates/apub/assets/mastodon/objects/page.json index ec4c13080..2965b4b8d 100644 --- a/crates/apub/assets/mastodon/objects/page.json +++ b/crates/apub/assets/mastodon/objects/page.json @@ -11,21 +11,21 @@ "votersCount": "toot:votersCount" } ], - "id": "https://dice.camp/users/thekernelinyellow/statuses/110830743680706519", + "id": "https://masto.qa.urbanwildlife.biz/users/mastodon/statuses/110830743680706519", "type": "Note", "summary": null, "inReplyTo": null, "published": "2023-08-04T09:55:39Z", - "url": "https://dice.camp/@thekernelinyellow/110830743680706519", - "attributedTo": "https://dice.camp/users/thekernelinyellow", + "url": "https://masto.qa.urbanwildlife.biz/110830743680706519", + "attributedTo": "https://masto.qa.urbanwildlife.biz/users/mastodon", "to": ["https://www.w3.org/ns/activitystreams#Public"], "cc": [ - "https://dice.camp/users/thekernelinyellow/followers", + "https://masto.qa.urbanwildlife.biz/users/mastodon/followers", "https://enterprise.lemmy.ml/c/tenforward", "https://enterprise.lemmy.ml/c/tenforward/followers" ], "sensitive": false, - "atomUri": "https://dice.camp/users/thekernelinyellow/statuses/110830743680706519", + "atomUri": "https://masto.qa.urbanwildlife.biz/statuses/110830743680706519", "inReplyToAtomUri": null, "conversation": "tag:dice.camp,2023-08-04:objectId=29969291:objectType=Conversation", "content": "

@tenforward Variable never resetting at refresh

Hi! I'm using a variable to count elements in my generator but every time I generate a new character, the counter's value carries on from the previous one. Is there a function to reset it (I set it to 0 at the beginning of the file)

", @@ -41,12 +41,12 @@ } ], "replies": { - "id": "https://dice.camp/users/thekernelinyellow/statuses/110830743680706519/replies", + "id": "https://masto.qa.urbanwildlife.biz/users/mastodon/statuses/110830743680706519/replies", "type": "Collection", "first": { "type": "CollectionPage", - "next": "https://dice.camp/users/thekernelinyellow/statuses/110830743680706519/replies?only_other_accounts=true&page=true", - "partOf": "https://dice.camp/users/thekernelinyellow/statuses/110830743680706519/replies", + "next": "https://masto.qa.urbanwildlife.biz/users/mastodon/statuses/110830743680706519/replies?only_other_accounts=true&page=true", + "partOf": "https://masto.qa.urbanwildlife.biz/users/mastodon/statuses/110830743680706519/replies", "items": [] } } diff --git a/crates/apub/src/api/user_settings_backup.rs b/crates/apub/src/api/user_settings_backup.rs index ebe2940d4..a2e6c55ac 100644 --- a/crates/apub/src/api/user_settings_backup.rs +++ b/crates/apub/src/api/user_settings_backup.rs @@ -17,6 +17,7 @@ use lemmy_db_schema::{ instance::Instance, instance_block::{InstanceBlock, InstanceBlockForm}, local_user::{LocalUser, LocalUserUpdateForm}, + local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm}, person::{Person, PersonUpdateForm}, person_block::{PersonBlock, PersonBlockForm}, post::{PostSaved, PostSavedForm}, @@ -50,6 +51,7 @@ pub struct UserSettingsBackup { // TODO: might be worth making a separate struct for settings backup, to avoid breakage in case // fields are renamed, and to avoid storing unnecessary fields like person_id or email pub settings: Option, + pub vote_display_mode_settings: Option, #[serde(default)] pub followed_communities: Vec>, #[serde(default)] @@ -80,6 +82,7 @@ pub async fn export_settings( matrix_id: local_user_view.person.matrix_user_id, bot_account: local_user_view.person.bot_account.into(), settings: Some(local_user_view.local_user), + vote_display_mode_settings: Some(local_user_view.local_user_vote_display_mode), followed_communities: vec_into(lists.followed_communities), blocked_communities: vec_into(lists.blocked_communities), blocked_instances: lists.blocked_instances, @@ -132,6 +135,27 @@ pub async fn import_settings( ) .await?; + // Update the vote display mode settings + let vote_display_mode_form = LocalUserVoteDisplayModeUpdateForm { + score: data.vote_display_mode_settings.as_ref().map(|s| s.score), + upvotes: data.vote_display_mode_settings.as_ref().map(|s| s.upvotes), + downvotes: data + .vote_display_mode_settings + .as_ref() + .map(|s| s.downvotes), + upvote_percentage: data + .vote_display_mode_settings + .as_ref() + .map(|s| s.upvote_percentage), + }; + + LocalUserVoteDisplayMode::update( + &mut context.pool(), + local_user_view.local_user.id, + &vote_display_mode_form, + ) + .await?; + let url_count = data.followed_communities.len() + data.blocked_communities.len() + data.blocked_users.len() diff --git a/crates/apub/src/http/community.rs b/crates/apub/src/http/community.rs index 73e620dbe..83ccdacf9 100644 --- a/crates/apub/src/http/community.rs +++ b/crates/apub/src/http/community.rs @@ -72,7 +72,7 @@ pub(crate) async fn get_apub_community_followers( } /// Returns the community outbox, which is populated by a maximum of 20 posts (but no other -/// activites like votes or comments). +/// activities like votes or comments). pub(crate) async fn get_apub_community_outbox( info: web::Path, context: Data, diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index 165378ab6..8f4f163db 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -96,6 +96,7 @@ impl Object for ApubSite { kind: ApplicationType::Application, id: self.id().into(), name: self.name.clone(), + preferred_username: data.domain().to_string(), content: self.sidebar.as_ref().map(|d| markdown_to_html(d)), source: self.sidebar.clone().map(Source::new), summary: self.description.clone(), diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 47d7f53c6..8b8ae093b 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -302,8 +302,7 @@ mod tests { use super::*; use crate::{ objects::{ - community::{tests::parse_lemmy_community, ApubCommunity}, - instance::ApubSite, + community::tests::parse_lemmy_community, person::{tests::parse_lemmy_person, ApubPerson}, }, protocol::tests::file_to_json_object, @@ -333,7 +332,10 @@ mod tests { assert!(!post.featured_community); assert_eq!(context.request_count(), 0); - cleanup(&context, person, site, community, post).await?; + Post::delete(&mut context.pool(), post.id).await?; + Person::delete(&mut context.pool(), person.id).await?; + Community::delete(&mut context.pool(), community.id).await?; + Site::delete(&mut context.pool(), site.id).await?; Ok(()) } @@ -341,29 +343,19 @@ mod tests { #[serial] async fn test_convert_mastodon_post_title() -> LemmyResult<()> { let context = LemmyContext::init_test_context().await; - let (person, site) = parse_lemmy_person(&context).await?; let community = parse_lemmy_community(&context).await?; + let json = file_to_json_object("assets/mastodon/objects/person.json")?; + let person = ApubPerson::from_json(json, &context).await?; + let json = file_to_json_object("assets/mastodon/objects/page.json")?; let post = ApubPost::from_json(json, &context).await?; assert_eq!(post.name, "Variable never resetting at refresh"); - cleanup(&context, person, site, community, post).await?; - Ok(()) - } - - async fn cleanup( - context: &Data, - person: ApubPerson, - site: ApubSite, - community: ApubCommunity, - post: ApubPost, - ) -> LemmyResult<()> { Post::delete(&mut context.pool(), post.id).await?; Person::delete(&mut context.pool(), person.id).await?; Community::delete(&mut context.pool(), community.id).await?; - Site::delete(&mut context.pool(), site.id).await?; Ok(()) } } diff --git a/crates/apub/src/protocol/objects/instance.rs b/crates/apub/src/protocol/objects/instance.rs index 17623719c..8f6a0f368 100644 --- a/crates/apub/src/protocol/objects/instance.rs +++ b/crates/apub/src/protocol/objects/instance.rs @@ -19,8 +19,10 @@ pub struct Instance { #[serde(rename = "type")] pub(crate) kind: ApplicationType, pub(crate) id: ObjectId, - // site name + /// site name pub(crate) name: String, + /// instance domain, necessary for mastodon authorized fetch + pub(crate) preferred_username: String, pub(crate) inbox: Url, /// mandatory field in activitypub, lemmy currently serves an empty outbox pub(crate) outbox: Url, diff --git a/crates/db_schema/src/impls/images.rs b/crates/db_schema/src/impls/images.rs index a5af893cb..01cd76247 100644 --- a/crates/db_schema/src/impls/images.rs +++ b/crates/db_schema/src/impls/images.rs @@ -2,7 +2,7 @@ use crate::{ newtypes::{DbUrl, LocalUserId}, schema::{local_image, remote_image}, source::images::{LocalImage, LocalImageForm, RemoteImage, RemoteImageForm}, - utils::{get_conn, limit_and_offset, DbPool}, + utils::{get_conn, limit_and_offset, limit_and_offset_unlimited, DbPool}, }; use diesel::{ dsl::exists, @@ -25,28 +25,19 @@ impl LocalImage { .await } - /// This should only be used in the internal API, since it has no page and limit pub async fn get_all_by_local_user_id( pool: &mut DbPool<'_>, user_id: LocalUserId, - ) -> Result, Error> { - let conn = &mut get_conn(pool).await?; - local_image::table - .filter(local_image::local_user_id.eq(user_id)) - .select(local_image::all_columns) - .load::(conn) - .await - } - - /// This is okay for API use. - pub async fn get_all_paged_by_local_user_id( - pool: &mut DbPool<'_>, - user_id: LocalUserId, page: Option, limit: Option, + ignore_page_limits: bool, ) -> Result, Error> { let conn = &mut get_conn(pool).await?; - let (limit, offset) = limit_and_offset(page, limit)?; + let (limit, offset) = if ignore_page_limits { + limit_and_offset_unlimited(page, limit) + } else { + limit_and_offset(page, limit)? + }; local_image::table .filter(local_image::local_user_id.eq(user_id)) diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 14da24bae..14ae65469 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -4,6 +4,7 @@ use crate::{ source::{ actor_language::{LocalUserLanguage, SiteLanguage}, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, + local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeInsertForm}, }, traits::Crud, utils::{ @@ -211,6 +212,12 @@ impl Crud for LocalUser { LocalUserLanguage::update(pool, vec![], local_user_.id).await?; } + // Create their vote_display_modes + let vote_display_mode_form = LocalUserVoteDisplayModeInsertForm::builder() + .local_user_id(local_user_.id) + .build(); + LocalUserVoteDisplayMode::create(pool, &vote_display_mode_form).await?; + Ok(local_user_) } async fn update( diff --git a/crates/db_schema/src/impls/local_user_vote_display_mode.rs b/crates/db_schema/src/impls/local_user_vote_display_mode.rs new file mode 100644 index 000000000..4458fc1b6 --- /dev/null +++ b/crates/db_schema/src/impls/local_user_vote_display_mode.rs @@ -0,0 +1,57 @@ +use crate::{ + newtypes::LocalUserId, + schema::local_user_vote_display_mode, + source::local_user_vote_display_mode::{ + LocalUserVoteDisplayMode, + LocalUserVoteDisplayModeInsertForm, + LocalUserVoteDisplayModeUpdateForm, + }, + utils::{get_conn, DbPool}, +}; +use diesel::{dsl::insert_into, result::Error, QueryDsl}; +use diesel_async::RunQueryDsl; + +impl LocalUserVoteDisplayMode { + pub async fn read(pool: &mut DbPool<'_>) -> Result { + let conn = &mut get_conn(pool).await?; + local_user_vote_display_mode::table + .first::(conn) + .await + } + + pub async fn create( + pool: &mut DbPool<'_>, + form: &LocalUserVoteDisplayModeInsertForm, + ) -> Result { + let conn = &mut get_conn(pool).await?; + insert_into(local_user_vote_display_mode::table) + .values(form) + .get_result::(conn) + .await + } + pub async fn update( + pool: &mut DbPool<'_>, + local_user_id: LocalUserId, + form: &LocalUserVoteDisplayModeUpdateForm, + ) -> Result<(), Error> { + // avoid error "There are no changes to save. This query cannot be built" + if form.is_empty() { + return Ok(()); + } + let conn = &mut get_conn(pool).await?; + diesel::update(local_user_vote_display_mode::table.find(local_user_id)) + .set(form) + .get_result::(conn) + .await?; + Ok(()) + } +} + +impl LocalUserVoteDisplayModeUpdateForm { + fn is_empty(&self) -> bool { + self.score.is_none() + && self.upvotes.is_none() + && self.downvotes.is_none() + && self.upvote_percentage.is_none() + } +} diff --git a/crates/db_schema/src/impls/mod.rs b/crates/db_schema/src/impls/mod.rs index 6d4549b14..711a6c4e6 100644 --- a/crates/db_schema/src/impls/mod.rs +++ b/crates/db_schema/src/impls/mod.rs @@ -18,6 +18,7 @@ pub mod language; pub mod local_site; pub mod local_site_rate_limit; pub mod local_user; +pub mod local_user_vote_display_mode; pub mod login_token; pub mod moderator; pub mod password_reset_request; diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index 739ff8482..a61b2d24f 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -342,7 +342,7 @@ diesel::table! { diesel::table! { local_image (pictrs_alias) { - local_user_id -> Int4, + local_user_id -> Nullable, pictrs_alias -> Text, pictrs_delete_token -> Text, published -> Timestamptz, @@ -454,6 +454,16 @@ diesel::table! { } } +diesel::table! { + local_user_vote_display_mode (local_user_id) { + local_user_id -> Int4, + score -> Bool, + upvotes -> Bool, + downvotes -> Bool, + upvote_percentage -> Bool, + } +} + diesel::table! { login_token (token) { token -> Text, @@ -961,6 +971,7 @@ diesel::joinable!(local_site_rate_limit -> local_site (local_site_id)); diesel::joinable!(local_user -> person (person_id)); diesel::joinable!(local_user_language -> language (language_id)); diesel::joinable!(local_user_language -> local_user (local_user_id)); +diesel::joinable!(local_user_vote_display_mode -> local_user (local_user_id)); diesel::joinable!(login_token -> local_user (user_id)); diesel::joinable!(mod_add_community -> community (community_id)); diesel::joinable!(mod_ban_from_community -> community (community_id)); @@ -1043,6 +1054,7 @@ diesel::allow_tables_to_appear_in_same_query!( local_site_rate_limit, local_user, local_user_language, + local_user_vote_display_mode, login_token, mod_add, mod_add_community, diff --git a/crates/db_schema/src/source/images.rs b/crates/db_schema/src/source/images.rs index 17965254d..da6be2a14 100644 --- a/crates/db_schema/src/source/images.rs +++ b/crates/db_schema/src/source/images.rs @@ -24,7 +24,7 @@ use typed_builder::TypedBuilder; #[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] #[cfg_attr(feature = "full", diesel(primary_key(local_user_id)))] pub struct LocalImage { - pub local_user_id: LocalUserId, + pub local_user_id: Option, pub pictrs_alias: String, pub pictrs_delete_token: String, pub published: DateTime, @@ -34,14 +34,13 @@ pub struct LocalImage { #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = local_image))] pub struct LocalImageForm { - pub local_user_id: LocalUserId, + pub local_user_id: Option, pub pictrs_alias: String, pub pictrs_delete_token: String, } /// Stores all images which are hosted on remote domains. When attempting to proxy an image, it /// is checked against this table to avoid Lemmy being used as a general purpose proxy. -#[skip_serializing_none] #[derive(PartialEq, Eq, Debug, Clone)] #[cfg_attr(feature = "full", derive(Queryable, Identifiable))] #[cfg_attr(feature = "full", diesel(table_name = remote_image))] diff --git a/crates/db_schema/src/source/local_user.rs b/crates/db_schema/src/source/local_user.rs index 97a0921da..f159232f7 100644 --- a/crates/db_schema/src/source/local_user.rs +++ b/crates/db_schema/src/source/local_user.rs @@ -36,6 +36,7 @@ pub struct LocalUser { pub show_avatars: bool, pub send_notifications_to_email: bool, /// Whether to show comment / post scores. + // TODO now that there is a vote_display_mode, this can be gotten rid of in future releases. pub show_scores: bool, /// Whether to show bot accounts. pub show_bot_accounts: bool, @@ -55,6 +56,7 @@ pub struct LocalUser { pub infinite_scroll_enabled: bool, /// Whether the person is an admin. pub admin: bool, + /// A post-view mode that changes how multiple post listings look. pub post_listing_mode: PostListingMode, pub totp_2fa_enabled: bool, /// Whether to allow keyboard navigation (for browsing and interacting with posts and comments). diff --git a/crates/db_schema/src/source/local_user_vote_display_mode.rs b/crates/db_schema/src/source/local_user_vote_display_mode.rs new file mode 100644 index 000000000..314d99e4a --- /dev/null +++ b/crates/db_schema/src/source/local_user_vote_display_mode.rs @@ -0,0 +1,51 @@ +use crate::newtypes::LocalUserId; +#[cfg(feature = "full")] +use crate::schema::local_user_vote_display_mode; +use serde::{Deserialize, Serialize}; +use serde_with::skip_serializing_none; +#[cfg(feature = "full")] +use ts_rs::TS; +use typed_builder::TypedBuilder; + +#[skip_serializing_none] +#[derive(PartialEq, Eq, Debug, Clone, Default, Serialize, Deserialize)] +#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))] +#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))] +#[cfg_attr(feature = "full", diesel(primary_key(local_user_id)))] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::local_site::LocalUser)) +)] +#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))] +#[cfg_attr(feature = "full", ts(export))] +/// The vote display settings for your user. +pub struct LocalUserVoteDisplayMode { + pub local_user_id: LocalUserId, + pub score: bool, + pub upvotes: bool, + pub downvotes: bool, + pub upvote_percentage: bool, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(Insertable))] +#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))] +pub struct LocalUserVoteDisplayModeInsertForm { + #[builder(!default)] + pub local_user_id: LocalUserId, + pub score: Option, + pub upvotes: Option, + pub downvotes: Option, + pub upvote_percentage: Option, +} + +#[derive(Clone, Default)] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = local_user_vote_display_mode))] +pub struct LocalUserVoteDisplayModeUpdateForm { + pub score: Option, + pub upvotes: Option, + pub downvotes: Option, + pub upvote_percentage: Option, +} diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs index 9a6e4941a..ab82a114c 100644 --- a/crates/db_schema/src/source/mod.rs +++ b/crates/db_schema/src/source/mod.rs @@ -23,6 +23,7 @@ pub mod language; pub mod local_site; pub mod local_site_rate_limit; pub mod local_user; +pub mod local_user_vote_display_mode; pub mod login_token; pub mod moderator; pub mod password_reset_request; @@ -44,6 +45,6 @@ pub mod tagline; /// value is not sent by Lemmy. Necessary for crates which rely on Rust API such as lemmy-stats-crawler. fn placeholder_apub_url() -> DbUrl { DbUrl(Box::new( - Url::parse("http://example.com").expect("parse placeholer url"), + Url::parse("http://example.com").expect("parse placeholder url"), )) } diff --git a/crates/db_schema/src/source/site.rs b/crates/db_schema/src/source/site.rs index 754acb2aa..40ba14f96 100644 --- a/crates/db_schema/src/source/site.rs +++ b/crates/db_schema/src/source/site.rs @@ -34,7 +34,9 @@ pub struct Site { pub last_refreshed_at: DateTime, /// The site inbox pub inbox_url: DbUrl, + #[serde(skip)] pub private_key: Option, + #[serde(skip)] pub public_key: String, pub instance_id: InstanceId, /// If present, nsfw content is visible by default. Should be displayed by frontends/clients diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 6f3322b39..1aa3bb6e7 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -18,10 +18,14 @@ use lemmy_db_schema::{ comment_aggregates, comment_like, comment_report, + comment_saved, community, + community_follower, community_moderator, community_person_ban, + local_user, person, + person_block, post, }, utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn}, @@ -64,6 +68,45 @@ fn queries<'a>() -> Queries< ), ), ) + .left_join( + aliases::community_moderator1.on( + community::id + .eq(aliases::community_moderator1.field(community_moderator::community_id)) + .and( + aliases::community_moderator1 + .field(community_moderator::person_id) + .eq(comment::creator_id), + ), + ), + ) + .left_join( + local_user::table.on( + comment::creator_id + .eq(local_user::person_id) + .and(local_user::admin.eq(true)), + ), + ) + .left_join( + person_block::table.on( + comment::creator_id + .eq(person_block::target_id) + .and(person_block::person_id.eq(my_person_id)), + ), + ) + .left_join( + community_follower::table.on( + post::community_id + .eq(community_follower::community_id) + .and(community_follower::person_id.eq(my_person_id)), + ), + ) + .left_join( + comment_saved::table.on( + comment::id + .eq(comment_saved::comment_id) + .and(comment_saved::person_id.eq(my_person_id)), + ), + ) .select(( comment_report::all_columns, comment::all_columns, @@ -73,6 +116,14 @@ fn queries<'a>() -> Queries< aliases::person1.fields(person::all_columns), comment_aggregates::all_columns, community_person_ban::community_id.nullable().is_not_null(), + aliases::community_moderator1 + .field(community_moderator::community_id) + .nullable() + .is_not_null(), + local_user::admin.nullable().is_not_null(), + person_block::target_id.nullable().is_not_null(), + community_follower::pending.nullable(), + comment_saved::published.nullable().is_not_null(), comment_like::score.nullable(), aliases::person2.fields(person::all_columns).nullable(), )) @@ -223,12 +274,14 @@ mod tests { community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, post::{Post, PostInsertForm}, }, traits::{Crud, Joinable, Reportable}, utils::{build_db_pool_for_tests, RANK_DEFAULT}, CommunityVisibility, + SubscribedType, }; use pretty_assertions::assert_eq; use serial_test::serial; @@ -258,6 +311,7 @@ mod tests { let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap(); let timmy_view = LocalUserView { local_user: timmy_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_timmy.clone(), counts: Default::default(), }; @@ -350,6 +404,11 @@ mod tests { comment_report: inserted_jessica_report.clone(), comment: inserted_comment.clone(), post: inserted_post, + creator_is_moderator: true, + creator_is_admin: false, + creator_blocked: false, + subscribed: SubscribedType::NotSubscribed, + saved: false, community: Community { id: inserted_community.id, name: inserted_community.name, diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index a72d0b210..40065672c 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -441,6 +441,7 @@ mod tests { instance::Instance, language::Language, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, person_block::{PersonBlock, PersonBlockForm}, post::{Post, PostInsertForm}, @@ -614,6 +615,7 @@ mod tests { let timmy_local_user_view = LocalUserView { local_user: inserted_timmy_local_user.clone(), + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_timmy_person.clone(), counts: Default::default(), }; diff --git a/crates/db_views/src/local_user_view.rs b/crates/db_views/src/local_user_view.rs index cb9ab86c4..96d6a3530 100644 --- a/crates/db_views/src/local_user_view.rs +++ b/crates/db_views/src/local_user_view.rs @@ -4,7 +4,7 @@ use diesel::{result::Error, BoolExpressionMethods, ExpressionMethods, JoinOnDsl, use diesel_async::RunQueryDsl; use lemmy_db_schema::{ newtypes::{LocalUserId, PersonId}, - schema::{local_user, person, person_aggregates}, + schema::{local_user, local_user_vote_display_mode, person, person_aggregates}, utils::{ functions::{coalesce, lower}, DbConn, @@ -33,6 +33,7 @@ fn queries<'a>( ) -> Queries>, impl ListFn<'a, LocalUserView, ListMode>> { let selection = ( local_user::all_columns, + local_user_vote_display_mode::all_columns, person::all_columns, person_aggregates::all_columns, ); @@ -58,6 +59,7 @@ fn queries<'a>( _ => query, }; query + .inner_join(local_user_vote_display_mode::table) .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id))) .select(selection) .first::(&mut conn) @@ -68,10 +70,11 @@ fn queries<'a>( match mode { ListMode::AdminsWithEmails => { local_user::table - .filter(local_user::email.is_not_null()) - .filter(local_user::admin.eq(true)) + .inner_join(local_user_vote_display_mode::table) .inner_join(person::table) .inner_join(person_aggregates::table.on(person::id.eq(person_aggregates::person_id))) + .filter(local_user::email.is_not_null()) + .filter(local_user::admin.eq(true)) .select(selection) .load::(&mut conn) .await diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index ddd12d226..0f1f3639c 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -14,15 +14,31 @@ use lemmy_db_schema::{ newtypes::{CommunityId, PersonId, PostId, PostReportId}, schema::{ community, + community_follower, community_moderator, community_person_ban, + local_user, person, + person_block, + person_post_aggregates, post, post_aggregates, + post_hide, post_like, + post_read, post_report, + post_saved, + }, + utils::{ + functions::coalesce, + get_conn, + limit_and_offset, + DbConn, + DbPool, + ListFn, + Queries, + ReadFn, }, - utils::{get_conn, limit_and_offset, DbConn, DbPool, ListFn, Queries, ReadFn}, }; fn queries<'a>() -> Queries< @@ -42,6 +58,67 @@ fn queries<'a>() -> Queries< .and(community_person_ban::person_id.eq(post::creator_id)), ), ) + .left_join( + aliases::community_moderator1.on( + aliases::community_moderator1 + .field(community_moderator::community_id) + .eq(post::community_id) + .and( + aliases::community_moderator1 + .field(community_moderator::person_id) + .eq(my_person_id), + ), + ), + ) + .left_join( + local_user::table.on( + post::creator_id + .eq(local_user::person_id) + .and(local_user::admin.eq(true)), + ), + ) + .left_join( + post_saved::table.on( + post::id + .eq(post_saved::post_id) + .and(post_saved::person_id.eq(my_person_id)), + ), + ) + .left_join( + post_read::table.on( + post::id + .eq(post_read::post_id) + .and(post_read::person_id.eq(my_person_id)), + ), + ) + .left_join( + post_hide::table.on( + post::id + .eq(post_hide::post_id) + .and(post_hide::person_id.eq(my_person_id)), + ), + ) + .left_join( + person_block::table.on( + post::creator_id + .eq(person_block::target_id) + .and(person_block::person_id.eq(my_person_id)), + ), + ) + .left_join( + person_post_aggregates::table.on( + post::id + .eq(person_post_aggregates::post_id) + .and(person_post_aggregates::person_id.eq(my_person_id)), + ), + ) + .left_join( + community_follower::table.on( + post::community_id + .eq(community_follower::community_id) + .and(community_follower::person_id.eq(my_person_id)), + ), + ) .left_join( post_like::table.on( post::id @@ -61,7 +138,21 @@ fn queries<'a>() -> Queries< person::all_columns, aliases::person1.fields(person::all_columns), community_person_ban::community_id.nullable().is_not_null(), + aliases::community_moderator1 + .field(community_moderator::community_id) + .nullable() + .is_not_null(), + local_user::admin.nullable().is_not_null(), + community_follower::pending.nullable(), + post_saved::post_id.nullable().is_not_null(), + post_read::post_id.nullable().is_not_null(), + post_hide::post_id.nullable().is_not_null(), + person_block::target_id.nullable().is_not_null(), post_like::score.nullable(), + coalesce( + post_aggregates::comments.nullable() - person_post_aggregates::read_comments.nullable(), + post_aggregates::comments, + ), post_aggregates::all_columns, aliases::person2.fields(person::all_columns.nullable()), )) @@ -206,6 +297,7 @@ mod tests { community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm}, instance::Instance, local_user::{LocalUser, LocalUserInsertForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, post::{Post, PostInsertForm}, post_report::{PostReport, PostReportForm}, @@ -241,6 +333,7 @@ mod tests { let timmy_local_user = LocalUser::create(pool, &new_local_user).await.unwrap(); let timmy_view = LocalUserView { local_user: timmy_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_timmy.clone(), counts: Default::default(), }; diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index ed9cd0952..b9522d47b 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -749,6 +749,7 @@ mod tests { instance_block::{InstanceBlock, InstanceBlockForm}, language::Language, local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::{Person, PersonInsertForm}, person_block::{PersonBlock, PersonBlockForm}, post::{Post, PostHide, PostInsertForm, PostLike, PostLikeForm, PostRead, PostUpdateForm}, @@ -871,11 +872,13 @@ mod tests { let inserted_bot_post = Post::create(pool, &new_bot_post).await?; let local_user_view = LocalUserView { local_user: inserted_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_person, counts: Default::default(), }; let blocked_local_user_view = LocalUserView { local_user: inserted_blocked_local_user, + local_user_vote_display_mode: LocalUserVoteDisplayMode::default(), person: inserted_blocked_person, counts: Default::default(), }; diff --git a/crates/db_views/src/site_view.rs b/crates/db_views/src/site_view.rs index 17819fdd7..52074c982 100644 --- a/crates/db_views/src/site_view.rs +++ b/crates/db_views/src/site_view.rs @@ -9,7 +9,7 @@ use lemmy_db_schema::{ impl SiteView { pub async fn read_local(pool: &mut DbPool<'_>) -> Result { let conn = &mut get_conn(pool).await?; - let mut res = site::table + site::table .inner_join(local_site::table) .inner_join( local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)), @@ -22,9 +22,6 @@ impl SiteView { site_aggregates::all_columns, )) .first::(conn) - .await?; - - res.site.private_key = None; - Ok(res) + .await } } diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 9b2d8d602..18fa7efa9 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -11,6 +11,7 @@ use lemmy_db_schema::{ local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, local_user::LocalUser, + local_user_vote_display_mode::LocalUserVoteDisplayMode, person::Person, post::Post, post_report::PostReport, @@ -41,6 +42,11 @@ pub struct CommentReportView { pub comment_creator: Person, pub counts: CommentAggregates, pub creator_banned_from_community: bool, + pub creator_is_moderator: bool, + pub creator_is_admin: bool, + pub creator_blocked: bool, + pub subscribed: SubscribedType, + pub saved: bool, pub my_vote: Option, pub resolver: Option, } @@ -73,6 +79,7 @@ pub struct CommentView { /// A local user view. pub struct LocalUserView { pub local_user: LocalUser, + pub local_user_vote_display_mode: LocalUserVoteDisplayMode, pub person: Person, pub counts: PersonAggregates, } @@ -90,7 +97,15 @@ pub struct PostReportView { pub creator: Person, pub post_creator: Person, pub creator_banned_from_community: bool, + pub creator_is_moderator: bool, + pub creator_is_admin: bool, + pub subscribed: SubscribedType, + pub saved: bool, + pub read: bool, + pub hidden: bool, + pub creator_blocked: bool, pub my_vote: Option, + pub unread_comments: i64, pub counts: PostAggregates, pub resolver: Option, } diff --git a/crates/routes/src/images.rs b/crates/routes/src/images.rs index f40b3c10c..58ca9c8ef 100644 --- a/crates/routes/src/images.rs +++ b/crates/routes/src/images.rs @@ -114,7 +114,7 @@ async fn upload( if let Some(images) = &images.files { for uploaded_image in images { let form = LocalImageForm { - local_user_id: local_user_view.local_user.id, + local_user_id: Some(local_user_view.local_user.id), pictrs_alias: uploaded_image.file.to_string(), pictrs_delete_token: uploaded_image.delete_token.to_string(), }; diff --git a/crates/routes/src/webfinger.rs b/crates/routes/src/webfinger.rs index ffceb4028..95092b244 100644 --- a/crates/routes/src/webfinger.rs +++ b/crates/routes/src/webfinger.rs @@ -1,6 +1,6 @@ use activitypub_federation::{ config::Data, - fetch::webfinger::{extract_webfinger_name, Webfinger, WebfingerLink}, + fetch::webfinger::{extract_webfinger_name, Webfinger, WebfingerLink, WEBFINGER_CONTENT_TYPE}, }; use actix_web::{web, web::Query, HttpResponse}; use lemmy_api_common::context::LemmyContext; @@ -38,39 +38,54 @@ async fn get_webfinger_response( ) -> Result { let name = extract_webfinger_name(&info.resource, &context)?; - let user_id: Option = Person::read_from_name(&mut context.pool(), name, false) - .await - .ok() - .map(|c| c.actor_id.into()); - let community_id: Option = Community::read_from_name(&mut context.pool(), name, false) - .await - .ok() - .and_then(|c| { - if c.visibility == CommunityVisibility::Public { - let id: Url = c.actor_id.into(); - Some(id) - } else { - None - } - }); + let links = if name == context.settings().hostname { + // webfinger response for instance actor (required for mastodon authorized fetch) + let url = Url::parse(&context.settings().get_protocol_and_hostname())?; + vec![webfinger_link_for_actor(Some(url), "none", &context)] + } else { + // webfinger response for user/community + let user_id: Option = Person::read_from_name(&mut context.pool(), name, false) + .await + .ok() + .map(|c| c.actor_id.into()); + let community_id: Option = Community::read_from_name(&mut context.pool(), name, false) + .await + .ok() + .and_then(|c| { + if c.visibility == CommunityVisibility::Public { + let id: Url = c.actor_id.into(); + Some(id) + } else { + None + } + }); - // Mastodon seems to prioritize the last webfinger item in case of duplicates. Put - // community last so that it gets prioritized. For Lemmy the order doesnt matter. - let links = vec![ - webfinger_link_for_actor(user_id, "Person", &context), - webfinger_link_for_actor(community_id, "Group", &context), - ] + // Mastodon seems to prioritize the last webfinger item in case of duplicates. Put + // community last so that it gets prioritized. For Lemmy the order doesnt matter. + vec![ + webfinger_link_for_actor(user_id, "Person", &context), + webfinger_link_for_actor(community_id, "Group", &context), + ] + } .into_iter() .flatten() - .collect(); + .collect::>(); - let json = Webfinger { - subject: info.resource.clone(), - links, - ..Default::default() - }; + if links.is_empty() { + Ok(HttpResponse::NotFound().finish()) + } else { + let json = Webfinger { + subject: info.resource.clone(), + links, + ..Default::default() + }; - Ok(HttpResponse::Ok().json(json)) + Ok( + HttpResponse::Ok() + .content_type(&WEBFINGER_CONTENT_TYPE) + .json(json), + ) + } } fn webfinger_link_for_actor( diff --git a/crates/utils/src/rate_limit/rate_limiter.rs b/crates/utils/src/rate_limit/rate_limiter.rs index 7c68003d8..d452fcbb3 100644 --- a/crates/utils/src/rate_limit/rate_limiter.rs +++ b/crates/utils/src/rate_limit/rate_limiter.rs @@ -212,7 +212,7 @@ impl RateLimitedGroup { now: InstantSecs, config: BucketConfig, ) -> bool { - #[allow(clippy::indexing_slicing)] // `EnumMap` has no `get` funciton + #[allow(clippy::indexing_slicing)] // `EnumMap` has no `get` function let bucket = &mut self.total[action_type]; let new_bucket = bucket.update(now, config); diff --git a/crates/utils/src/utils/validation.rs b/crates/utils/src/utils/validation.rs index da989a61b..23ef9744a 100644 --- a/crates/utils/src/utils/validation.rs +++ b/crates/utils/src/utils/validation.rs @@ -204,7 +204,7 @@ pub fn site_description_length_check(description: &str) -> LemmyResult<()> { ) } -/// Check minumum and maximum length of input string. If the string is too short or too long, the +/// Check minimum and maximum length of input string. If the string is too short or too long, the /// corresponding error is returned. /// /// HTML frontends specify maximum input length using `maxlength` attribute. diff --git a/docker/Dockerfile b/docker/Dockerfile index 1bbf4ddbd..0630b6e50 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -75,18 +75,19 @@ RUN --mount=type=cache,target=./target,uid=10001,gid=10001 set -ex; \ mv "./target/$CARGO_BUILD_TARGET/$RUST_RELEASE_MODE/lemmy_server" /home/lemmy/lemmy_server; \ fi + # amd64 base runner FROM ${AMD_RUNNER_IMAGE} AS runner-linux-amd64 -# Federation needs CA certificates -RUN apt update && apt install -y libssl-dev libpq-dev ca-certificates +# Add system packages that are needed: federation needs CA certificates, curl can be used for healthchecks +RUN apt update && apt install -y libssl-dev libpq-dev ca-certificates curl COPY --from=build-amd64 --chmod=0755 /lemmy/lemmy_server /usr/local/bin # arm base runner FROM ${ARM_RUNNER_IMAGE} AS runner-linux-arm64 -RUN apt update && apt install -y ca-certificates libssl-dev libpq-dev +RUN apt update && apt install -y libssl-dev libpq-dev ca-certificates curl COPY --from=build-arm64 --chmod=0755 /home/lemmy/lemmy_server /usr/local/bin diff --git a/migrations/2024-02-12-211114_add_vote_display_mode_setting/down.sql b/migrations/2024-02-12-211114_add_vote_display_mode_setting/down.sql new file mode 100644 index 000000000..e6daaedde --- /dev/null +++ b/migrations/2024-02-12-211114_add_vote_display_mode_setting/down.sql @@ -0,0 +1,2 @@ +DROP TABLE local_user_vote_display_mode; + diff --git a/migrations/2024-02-12-211114_add_vote_display_mode_setting/up.sql b/migrations/2024-02-12-211114_add_vote_display_mode_setting/up.sql new file mode 100644 index 000000000..a76e3c122 --- /dev/null +++ b/migrations/2024-02-12-211114_add_vote_display_mode_setting/up.sql @@ -0,0 +1,18 @@ +-- Create an extra table to hold local user vote display settings +-- Score and Upvote percentage are turned on by default. +CREATE TABLE local_user_vote_display_mode ( + local_user_id int REFERENCES local_user ON UPDATE CASCADE ON DELETE CASCADE NOT NULL, + score boolean DEFAULT TRUE NOT NULL, + upvotes boolean DEFAULT FALSE NOT NULL, + downvotes boolean DEFAULT FALSE NOT NULL, + upvote_percentage boolean DEFAULT TRUE NOT NULL, + PRIMARY KEY (local_user_id) +); + +-- Insert rows for every local user +INSERT INTO local_user_vote_display_mode (local_user_id) +SELECT + id +FROM + local_user; + diff --git a/migrations/2024-03-06-104706_local_image_user_opt/down.sql b/migrations/2024-03-06-104706_local_image_user_opt/down.sql new file mode 100644 index 000000000..45d890467 --- /dev/null +++ b/migrations/2024-03-06-104706_local_image_user_opt/down.sql @@ -0,0 +1,3 @@ +ALTER TABLE local_image + ALTER COLUMN local_user_id SET NOT NULL; + diff --git a/migrations/2024-03-06-104706_local_image_user_opt/up.sql b/migrations/2024-03-06-104706_local_image_user_opt/up.sql new file mode 100644 index 000000000..b80098161 --- /dev/null +++ b/migrations/2024-03-06-104706_local_image_user_opt/up.sql @@ -0,0 +1,3 @@ +ALTER TABLE local_image + ALTER COLUMN local_user_id DROP NOT NULL; +