Compare commits

...

489 commits

Author SHA1 Message Date
Nutomic
e82f72d3c8
Avoid breaking changes, keep response fields as deprecated (#5058) 2024-09-27 09:23:19 -04:00
Joseph Silva
50ce7961d1
Apply scheduled post limit to future posts instead of past posts, and verify this in test (#5054)
* test scheduled_post_count

* fix syntax error

* fix formatting

* fix argument order

* fix user_scheduled_post_count function
2024-09-27 08:51:10 -04:00
SleeplessOne1917
33cbd95b7e
Add skip_serialize_none to OAuth structs with option fields (#5046)
* Add skip_serialize_none to OAuth structs with option fields

* PR feedback

* Remove serde and ts export from SSO db-only structs
2024-09-26 10:24:51 +02:00
Nutomic
f6a24e133a
Replace clippy allow annotation with expect (fixes #5012) (#5048) 2024-09-24 13:29:02 -04:00
Nutomic
61a02482ff
Cleanup remaining use of Result<bool, Error> (fixes #4862) (#5047) 2024-09-24 13:25:33 -04:00
Dessalines
0fab5bed24
Add ability to search for Community by its description (or title only). (#5044)
- This changes the post_title_only for Search to title_only, since its
  also used in the community query now.
- Fixes #4785
2024-09-24 13:24:28 -04:00
Dessalines
a65be776e3
Remove redundant local_user.auto_expand setting. (#5041)
- Fixes #4643

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-09-24 08:55:09 -04:00
Nutomic
9eee61dd06
Post scheduling (fixes #234) (#5025)
* Post scheduling (fixes #234)

* clippy

* replace map_err with inspect_err

* ignore unpublished posts in read queries

* add api test

* fmt

* add some checks

* address some review comments

* allow updating schedule time

* rewrite scheduled task

* fmt

* machete

* compare date in sql, more filters

* check for community ban in sql

* remove api test (scheduled task only runs every 10 mins)

* remove mut

* add index

* remove Post::read impl

* fmt

* fix

* correctly handle changes to schedule time

* normal users can only schedule up to 10 posts
2024-09-24 05:39:40 -04:00
Nutomic
bab5c93062
Conditionally hide comments on nsfw posts (fixes #4237) (#5028)
* Conditionally hide comments on nsfw posts (fixes #4237)

* fix test
2024-09-24 10:33:53 +02:00
Dessalines
d476d32200
Removing a few more Result<bool> . (#4977)
* Removing a few more Result<bool> .

* Running taplo fmt.

* Running fmt.

* Adding email taken test.

* Fixing tests.

* Adding back in missing admin check.

* Rename check_has_local_followers function.
2024-09-23 20:55:35 -04:00
Dessalines
62e1790ae7
Adding saved_only, liked_only, and disliked_only filters to search. (#5034)
* Adding saved_only, liked_only, and disliked_only filters to search.

- Fixes #4547

* Removing duplicate Url return type for search (was actually post).

- This now works like the post_title_only filter.

* Address PR comments.

* Add saved_only post_view test.
2024-09-23 11:27:06 -04:00
Nutomic
a8843335a6
Simplify handling of NotFound SQL errors (fixes #4633) (#5031)
* Simplify handling of NotFound SQL errors (fixes #4633)

* fmt

* wip

* compiling

* clippy

* api tests

* fix
2024-09-23 11:26:50 -04:00
Dessalines
458bb60144
Get rid of a lot of pointless mut form initializations. (#5037)
* Get rid of a lot of pointless mut form initializations.

- Fixes #5036

* Fix clippy.
2024-09-23 12:05:18 +02:00
Sander Saarend
25df9d255b
Always save remote image data (#4875)
* Always save remote image data

* cleanup

---------

Co-authored-by: Felix Ableitner <me@nutomic.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-09-23 09:58:49 +02:00
Nutomic
8cdfc148d7
Ignore zero values when setting rate limits (fixes #4280) (#5029)
* Ignore zero values when setting rate limits (fixes #4280)

Havent bothered to add an error message for such an uncommon case.

* fmt

* reorder, add test
2024-09-20 14:43:04 +02:00
Nutomic
ad75192bae
Remove TypedBuilder in favor of derive_new (fixes #4863) (#5020)
* Remove TypedBuilder in favor of derive_new (fixes #4863)

* fix

* fix
2024-09-20 08:15:25 -04:00
Dessalines
5a722146b5
Upgrading to rust 1.81 (#5032) 2024-09-19 17:00:20 -04:00
leoseg
dbb8f9553a
Unittest for Search by title only (#5033)
* added test for search by title only

* formatted rust files
2024-09-19 17:00:07 -04:00
Nutomic
89745bb37d
Add category to RSS feeds (fixes #3446) (#5030) 2024-09-19 09:43:58 -04:00
Freakazoid182
43f20881cb
Feature/custom emoji and tagline views (#4580)
* Add custom_emoji list route

* Add tagline list route

* Apply linting

* Remove unecessary TaglineView

* Add category filter for custom emoji

* Add create tagline endpoint

* Add update tagline endpoint

* Add delete tagline endpoint

* Format through lint.sh

* Remove custom_emojis and taglines from site resource

* Get random tagline on site requets

* Impl Crud for Tagline

Remove superfluous properties

* Move tagline endpoints under /admin

* Impl Crud for CustomEmoji

* Remove delete from tagline and custom emoji impls

* Check  markdown for tagline

* Validate markdown on tagline

* Make content fields non optional

Add error types for tagline validation

* Use process_markdown instead of process_markdown_opt

* Consolidate Tagline error types

* Remove unecessary clone

* Updat misleading comments

* Remove local_site_id from tagline and custom_emoji

* Update TaglineInserForm and TaglineUpdateForm

* Add ignore_page_limits for custom emojis

EmojiPicker needs to be able to retrieve all emojis in 1 call

* Update custom_emoji_view

Only keep get_all als helper function calling list with paging ignored

Only order on category when filtering on category

* Removing pointless get_all fn.

* remove tagline length checks

* make fields of TaglineInsertForm and TaglineUpdateForm mandatory

* move emoji order statement

* add comment for GetSiteResponse.tagline

---------

Co-authored-by: Freakazoid182 <>
Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
Co-authored-by: Dessalines <tyhou13@gmx.com>
Co-authored-by: Felix Ableitner <me@nutomic.com>
2024-09-19 05:15:04 -04:00
Nutomic
026e23cf32
Simplify tests using default (#5026) 2024-09-19 04:43:27 -04:00
Dessalines
6b6457cc54
Adding a default_comment_sort_type column for local_site and local_user. (#4469)
* Adding a default_comment_sort_type column for local_site and local_user.

- Renamed SortType to PostSortType in the DB and code.
- Renamed references to default_sort_type to default_post_sort_type.
- Fixes #4128

* Renaming migration to current date.

* Simplifying PostSortType.
2024-09-19 10:03:58 +02:00
Dessalines
2b3fd70afd
Adding ability to restore content on user unban. (#4845)
* Adding ability to restore content on user unban.

- Fixes #4721

* Fixing api tests.

* Fix package.json

* Fixing lemmy-js-client dep.

* Adding API test for restoring content.
2024-09-18 09:11:42 -04:00
privacyguard
b26aaac523
SSO Support (#4881)
* Added OAUTH2 OIDC support

* Fixes and improvements based on review feedback

* use derive_new::new instead of TypedBuilder

* merge migrations into a single file

* fixes based on review feedback

* remove unnecessary hostname_ui config

* improvement based on review feedback

* improvements based on review feedback

* delete user oauth accounts at account deletion

* fixes and improvements based on review feedback

* removed auto_approve_application

* support registration application with sso

* improvements based on review feedback

* making the TokenResponse an internal struct as it should be

* remove duplicate struct

* prevent oauth linking to unverified accounts

* switched to manually entered username and removed the oauth name claim

* fix cargo fmt

* fix compile error

* improvements based on review feedback

* fixes and improvements based on review feedback

---------

Co-authored-by: privacyguard <privacyguard@users.noreply.github.com>
2024-09-18 14:52:33 +02:00
Nutomic
6454a4d43d
Remove enable nsfw (#5017)
* Remove `local_site.enable_nsfw` in favor of `site.content_warning` (fixes #4627)

* cleanup usage of SiteView::read_local

* test

* uppercase
2024-09-16 11:18:16 -04:00
Dessalines
5febf2b8fb
Adding clearurls crate to clean tracking params from links and markdown. (#5018)
* Adding clearurls crate to clean tracking params from links and markdown.

- Thanks to @jenrdikw for creating this
- Fixes #4905

* Upgrading to new version of clearurls

* Fix clippy
2024-09-16 11:15:41 -04:00
Dessalines
ff939e04fd
Removing embedded pict-rs. (#5023)
Some reasons for removing this:

- Even as an optional dependency, it locks us to many specific versions
  of rust deps.
- Pict-rs is a large app that can and should be run in on its own.
- Violates the philosophy of separation of concerns.
2024-09-16 11:08:18 -04:00
Dessalines
987e3f8026
Upgrading webmention to 0.6.0, removes native-tls (#4976) 2024-09-16 10:11:02 +02:00
Carlos Cabello
fa192f16bd
Add option to search exclusively by post title (#5015)
* Add option to search exclusively by post title

* Address format issues

* Remove duplicated 'removed' filter

* Replace url_search with search_term

* Build generic PostQuery before search match

* Create default queries. Move title_only to Search struct. Rename Url to PostURL

* Revert PostUrl to Url
2024-09-16 09:59:09 +02:00
Nutomic
dea6ee462c
Upgrade http crate (#5006)
* Remove opentelemetry

* remove unused deps, use backtrace

* always print db migration messages regardless of log level (fixes #4725)

* fix ci

* Remove useless root span builder

* Upgrade http and opentelemetry crates

* more fixes

* cleanup

* use release

* upgrade more deps
2024-09-12 05:49:09 -04:00
Dessalines
a6220537b5
Increase bio max length from 300 to 1000. (#5014)
- Fixes #4972
2024-09-11 15:27:16 +00:00
Sander Saarend
fbb3960c36
Image proxy fixes (#4871)
* Fix remote image urls

* Remove unnecessary URL decode

* Removing the proxy changes.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
Co-authored-by: Dessalines <tyhou13@gmx.com>
2024-09-11 10:11:25 -04:00
Nutomic
fc13009611
In debug mode allow resolving objects without auth (#5013) 2024-09-11 08:53:24 -04:00
Dessalines
ef5e2d96cd
Fixing woodpecker warnings 1. (#5011) 2024-09-10 17:42:20 -04:00
Dessalines
d1866cbd04
Remove pointless block_views. (#4841)
- Fixes #4793
2024-09-10 14:32:12 -04:00
Dessalines
84794714da
Removing local_user.show_scores column, since its now on the (#4497)
local_user_vote_display_mode table.

- See https://github.com/LemmyNet/lemmy/pull/4450
2024-09-10 14:27:17 -04:00
Nutomic
c90ee3094d
Remove opentelemetry (#4741)
* Remove opentelemetry

* remove unused deps, use backtrace

* always print db migration messages regardless of log level (fixes #4725)

* fix ci

* Remove useless root span builder

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-09-10 13:36:03 -04:00
Dessalines
ef49a0eb8d
Changing list_logins to return a ListLoginsResponse object. (#4888)
- Fixes #4873
2024-09-10 12:41:30 +02:00
Dessalines
c8ad0f2d09
Remove pointless local_user_id from LocalUserVoteDisplayMode (#4890)
- Fixes #4866
2024-09-10 12:39:45 +02:00
Nutomic
234f5c2060
Remove nonstandard field expires from apub block activity (ref #2316) (#4542)
* Migrate apub block activity to standard `endTime` property (fixes #2316)

* Remove nonstandard apub field `expires` (ref #2316)
2024-09-10 12:35:58 +02:00
renovate[bot]
f98b15511a
chore(deps): update rust crate derive-new to 0.7.0 (#4995)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 16:47:16 +00:00
renovate[bot]
5683d5ed7a
chore(deps): update pnpm to v9.9.0 (#4994)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 12:30:05 +00:00
renovate[bot]
b1afd98d29
chore(deps): update dependency @types/node to v22.5.1 (#4991)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 09:40:23 +00:00
renovate[bot]
f617227f0d
chore(deps): update dependency ts-jest to v29.2.5 (#4990)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 06:41:42 +00:00
renovate[bot]
fa94a5869e
chore(deps): update dependency eslint to v9.9.1 (#4992)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-09-01 03:11:01 +00:00
Joseph Silva
ae3df4db72
Replace "post" with "comment" in comment_report_view.rs (#4989) 2024-08-31 22:36:47 -04:00
Dessalines
d479bb96c6 Version 0.19.6-beta.7 2024-08-27 16:32:08 -04:00
dullbananas
101476df87
Make scripts work in path that contains spaces (#4986) 2024-08-22 17:33:00 +02:00
flamingos-cant
bd1b7aa6ef
Let federation modify groups (#4937)
* Don't check if group is local

* Add API test for mods modifying communities
2024-08-20 16:16:14 +02:00
Dessalines
2913cdf556 Version 0.19.6-beta.6 2024-08-19 19:44:13 -04:00
Dessalines
6b9e5ecb48 Version 0.19.6-beta.5 2024-08-15 08:50:27 -04:00
Richard Schwab
76a2c6e79b
Fix scheduled task to delete users with denied applications (#4907)
After an admin interacted with an application it can only be accepted or denied.
Denial reasons are not required by Lemmys backend, even though they're mandatory in Lemmy-UI, and therefore they are not a reliable indicator of an application being denied.
This aligns the cleanup logic  with the logic used for the unread application count.
2024-08-13 18:54:56 -04:00
Dessalines
254ef6dab3
Make site metadata fetch endpoint require auth. (#4968)
* Make site metadata fetch require auth.

* Update crates/api/src/post/get_link_metadata.rs

Co-authored-by: dullbananas <dull.bananas0@gmail.com>

---------

Co-authored-by: dullbananas <dull.bananas0@gmail.com>
2024-08-13 16:53:05 -04:00
Richard Schwab
63a686d390
Approve applications in transaction (#4970)
* Implement tests for registration application count and list api

* Use transaction when approving applications to ensure consistent approval state
2024-08-13 16:18:26 -04:00
flamingos-cant
ea18d462b0
Throw error when non-mod posts to mod-only comm (#4966) 2024-08-13 14:56:26 -04:00
Dessalines
128e78f7c2 Version 0.19.6-beta.4 2024-08-08 20:01:58 -04:00
phiresky
606545ccaf
fix: Run extract_opengraph_data only on first 64kB of data and if Content-Type html (#4957)
* fix: Run extract_opengraph_data only on first 64kB of data and if data is not binary.

* use mime type for determination

* chore: simplify collect function
2024-08-07 10:35:08 -04:00
Dessalines
88fbcea246 Version 0.19.6-beta.3 2024-08-06 10:32:41 -04:00
Dessalines
eff87b2764
Upgrading arm image. (#4962) 2024-08-05 22:25:18 -04:00
Dessalines
33fd31754a
Adding a URL max length lemmy error. (#4960)
* Adding a URL max length error.

- Also increasing the post.url max length to 2000 (seems standard)
- I ran into this when fixing torrent support, which often use longer
  urls.

* Fixing sql_format.
2024-08-04 09:45:53 -04:00
Dessalines
f8c7375731
Upgrading deps. (#4955) 2024-08-01 17:04:28 -04:00
renovate[bot]
c6cbc2f61d
chore(deps): update dependency typescript to v5.5.4 (#4942)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 14:46:59 -04:00
renovate[bot]
4729255514
chore(deps): update typescript-eslint monorepo to v8 (#4952)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 14:24:16 -04:00
renovate[bot]
b25f9c3e66
chore(deps): update typescript-eslint monorepo to v7.18.0 (#4950)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 13:59:55 -04:00
renovate[bot]
c0f3dff5cf
chore(deps): update rust crate mockall to 0.13.0 (#4946)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 13:59:40 -04:00
renovate[bot]
79fddc965f
chore(deps): update rust crate typed-builder to 0.19.0 (#4949)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 16:22:55 +00:00
renovate[bot]
2cab7935db
chore(deps): update docker/dockerfile docker tag to v1.9 (#4938)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 10:53:41 -04:00
renovate[bot]
2318473456
fix(deps): update rust crate console-subscriber to 0.4.0 (#4944)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 10:52:40 -04:00
renovate[bot]
75d8d704e7
chore(deps): update pnpm to v9.6.0 (#4939)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 10:52:24 -04:00
renovate[bot]
4e7e92da94
chore(deps): update dependency eslint-plugin-prettier to v5.2.1 (#4940)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 10:51:37 -04:00
renovate[bot]
fa836bb35a
chore(deps): update dependency ts-jest to v29.2.4 (#4941)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 10:51:26 -04:00
renovate[bot]
4e48054b17
chore(deps): update rust crate tokio to v1.39.2 (#4935)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 14:50:52 +00:00
Dessalines
6c11c013b0 Version 0.19.6-beta.2 2024-08-01 08:29:22 -04:00
renovate[bot]
0bd0c0f11b
chore(deps): update rust crate clap to v4.5.13 (#4932)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-08-01 11:38:06 +00:00
renovate[bot]
c6db2ad9b7
chore(deps): update rust crate serde_json to v1.0.121 (#4933)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 21:46:20 -04:00
Dessalines
60a7829638
Adding deny unimplemented to clippy lints. (#4922)
* Adding deny unimplemented to clippy lints.

- Context: #4782

* Update crates/apub/src/fetcher/site_or_community_or_user.rs

Thanks, I like that better.

Co-authored-by: dullbananas <dull.bananas0@gmail.com>

* Update crates/apub/src/fetcher/search.rs

Co-authored-by: dullbananas <dull.bananas0@gmail.com>

* Running fmt.

* Adding debug_assert(false)

* Removing some commands.

* Format.

* Remove todo.

---------

Co-authored-by: dullbananas <dull.bananas0@gmail.com>
2024-07-31 20:28:41 -04:00
renovate[bot]
cbb37fa2f1
chore(deps): update dependency @types/node to v20.14.13 (#4928)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-31 19:28:17 -04:00
Dessalines
0a6186d7aa
Fixing apt-key deprecation, and using apt-get. (#4931)
* Fixing apt-key deprecation, and using apt-get.

* Try 2

* Try 3

* Try 4

* Try 5

* Try 6

* Try 7

* Try 8

* Try 9

* Try 10

* Try 11

* Try 12

* Try 13

* Try 14

* Try 15

* Try 16

* Try 17
2024-07-31 18:54:44 -04:00
renovate[bot]
5421e9a3c7
chore(deps): update dependency prettier to v3.3.3 (#4929)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-30 15:38:17 +00:00
SleeplessOne1917
1ef375e5c3
Replace 3rd party once_cell crate with recently stabilized standard library API (#4919)
* Replace 3rd party once_cell crate with recently stabilized standard library API

* Bump CI rust version
2024-07-30 10:11:39 -04:00
Dessalines
069a9509d5
Adding renovate automerge. (#4927) 2024-07-30 09:41:06 -04:00
abdel-m
2ccb46b66d
pass local user to send local notifs (#4920)
* pass local user to send local notif

* pass local user to all crud calls

* execution of command "cargo +nightly fmt"

* Formatting and mode fixes.

---------

Co-authored-by: Dessalines <tyhou13@gmx.com>
2024-07-30 08:34:58 -04:00
Nutomic
fb911679d0
Fix admin notification for new user registration (fixes #4916) (#4925) 2024-07-30 08:34:17 -04:00
John Spurlock
7321a63005
Fix instance AP outbox url (#4917)
//site_outbox to /site_outbox
2024-07-24 12:18:28 +02:00
Dessalines
9738d87f38
Add the ability to fetch a registration application by person_id. (#4913)
* Add the ability to fetch a registration application by person_id.

- Fixes #4908

* Cleaning up PR.
2024-07-23 19:01:24 -04:00
Dessalines
32b73193df
Make sure you can view your moderated deleted and removed communities. (#4912)
* Make sure you can view your moderated deleted and removed communities.

- The front end checks to see whether you are a mod, in order to be
  able to restore deleted / removed communities. This removes a filter
  which prevents that.
- Fixes #4911

* Only show deleted communities to creator, and removed to admins.

* Addressing PR comments.
2024-07-23 12:38:54 -04:00
SleeplessOne1917
db390a2f3a
Make eligible enums convertable to static strs (#4915)
* Make eligible enums convertable to static strs

* Run cargo fmt

* Remove unnecessary derives
2024-07-23 11:05:19 -04:00
Nutomic
572a42d880
Change type of concurrent sends in config (#4914) 2024-07-22 09:58:50 -04:00
Darren M
3d80ac2ebb
Fix tls pool (#4910)
* Cargo: add rustls as a dependency

* install tls provider in main

* Cargo: re-define rustls dependency
2024-07-21 22:33:42 -04:00
phiresky
a08642f813
federation: parallel sending per instance (#4623)
* federation: parallel sending

* federation: some comments

* lint and set force_write true when a request fails

* inbox_urls return vec

* split inbox functions into separate file

* cleanup

* extract sending task code to separate file

* move federation concurrent config to config file

* off by one issue

* improve msg

* fix both permanent stopping of federation queues and multiple creation of the same federation queues

* fix after merge

* lint fix

* Update crates/federate/src/send.rs

Co-authored-by: dullbananas <dull.bananas0@gmail.com>

* comment about reverse ordering

* remove crashable, comment

* comment

* move comment

* run federation tests twice

* fix test run

* prettier

* fix config default

* upgrade rust to 1.78 to fix diesel cli

* fix clippy

* delay

* add debug to make localhost urls not valid in ap crate, add some debug logs

* federation tests: ensure server stop after test and random activity id

* ci fix

* add test to federate 100 events

* fix send 100 test

* different data every time so activities are distinguishable

* allow out of order receives in test

* lint

* comment about https://github.com/LemmyNet/lemmy/pull/4623#discussion_r1565437391

* move sender for clarity, add comment

* move more things to members

* update test todo comment, use same env var as worker test but default to 1

* remove else below continue

* some more cleanup

* handle todo about smooth exit

* add federate inboxes collector tests

* lint

* actor max length

* don't reset fail count if activity skipped

* fix some comments

* reuse vars

* format

* Update .woodpecker.yml

* fix recheck time

* fix inboxes tests under fast mode

* format

* make i32 and ugly casts

* clippy

---------

Co-authored-by: dullbananas <dull.bananas0@gmail.com>
2024-07-21 11:50:50 -04:00
Dessalines
073ff44676
Reverting webmention git dep to not break publish. (#4904)
- Context: #4901
2024-07-19 10:18:45 +02:00
藍+85CD
847c01f348
refactor!: use rustls instead of native-tls (#4901)
* refactor(utils): remove apub

* refactor(utils): remove apub

* refactor(utils): remove openssl

* refactor(utils): remove openssl

* Use rustls instead of native-tls.

* refactor(utils): remove apub

* refactor(utils): remove apub

* refactor(utils): remove openssl

* refactor(utils): remove openssl

* Use rustls instead of native-tls.

* Upping activitypub_federation dep

* Using git dep of webmention.

---------

Co-authored-by: Dessalines <tyhou13@gmx.com>
2024-07-18 08:40:43 -04:00
Daniel Lo Nigro
8abbd56400
Switch PostgreSQL container to use pgautoupgrade (#4892)
* Switch PostgreSQL container to use pgautoupgrade

This handles automatically upgrading the data files to newer versions of PostgreSQL.

* Fixing other uses of postgres:16-alpine image.

* Simplifying upgrade scripts.

---------

Co-authored-by: Dessalines <tyhou13@gmx.com>
2024-07-17 09:59:21 +02:00
Dessalines
ba044c7d98
Adding checks for higher admin and mod. (#4860)
* Adding checks for higher admin and mod.

* Adding admin/mod checks for ban and comment removal.

* Combining mod or admin check with an SQL union.

* Making community ban or add mod also allow higher admins.

* Making sure remove post also checks higher mods or admins.

* Add unit test for is_higher_mod_or_admin_check

* Fixing comment.

* Addressing PR comments.

* Get rid of pointless wrapper functions, return lemmyresult directly.
2024-07-16 12:22:47 +02:00
Dessalines
6ff128341c
Do pictrs transformations for proxied image urls. (#4895)
- Fixes #4893
2024-07-11 17:31:31 +02:00
Dessalines
53a226b944
Add show_nsfw override filter to GetPosts. (#4889)
- Fixes #4124
2024-07-09 13:44:23 -04:00
Dessalines
f229f09f92
Changing nodeinfo test from voyager.lemmy.ml to lemmy.ml (#4891)
- Fixes #4870
2024-07-07 19:01:03 -04:00
Dessalines
32cee9cbca
Fixing not being able to create comments on local community posts. (#4854)
* Fixing not being able to create comments on local community posts.

- This was caused by not passing my_person_id into various
  `CommentView::read` functions.
- Fixes #4853

* Refactoring views to use local_user, rather than person

* Addressing PR comments.

* Fixing API tests.
2024-07-07 12:28:42 -04:00
dullbananas
78702b59fd
Use trigger to generate apub URL in insert instead of update, and fix query planner options not being set when TLS is disabled (#4797)
* Update create.rs

* Update utils.rs

* Update utils.sql

* Update triggers.sql

* Update utils.sql

* Update create.rs

* Update create.rs

* Update create.rs

* Update create.rs

* Update create.rs

* Update create.rs

* Update create.rs

* Update create.rs

* Create up.sql

* Update up.sql

* Update triggers.sql

* Update utils.rs

* stuff

* stuff

* revert some changed files

* Revert "revert some changed files"

This reverts commit 028eabb4bd.

* revert the correct files

* partial reverts

* migration, tests, fix establish_connection

* lint

* pg_format
2024-07-02 11:23:21 -04:00
renovate[bot]
117a8b42c8
Update Rust crate serde_json to v1.0.120 (#4877)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-02 10:02:32 -04:00
dullbananas
fd58b4f809
Exponential controversy rank (#4872)
* Update utils.sql

* add migration
2024-07-02 09:40:18 -04:00
Dessalines
a7c39226e2
Remove unused PersonBlockId. (#4880)
- Fixes #4879
2024-07-02 09:39:37 -04:00
renovate[bot]
d90e8f8550
Update Rust crate clap to v4.5.8 (#4876)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-07-02 09:37:37 -04:00
Richard Schwab
2c57f42022
Relax timeout for sending activities (#4864)
* Relax timeout for sending activities

Lemmy considers timeouts during activity sending as retryable errors.
While it is frequently enough to retry sending the same activity again after
the original submission attempt resulted in a timeout, allowing the receiving
side to use more time for synchronous processing should reduce the number of
retries needed overall and improve overall compatibility.

Some ActivityPub software, such as Mastodon, implements a queue for processing
received activities asynchronously, which allows immediately returning a
response for activity submissions. Other software, such as Lemmy or Hubzilla
implement synchronous processing of activities before returning a response.

ActivityPub does not specify specific timeouts to be used:
https://github.com/w3c/activitypub/issues/365

* Simplify usage of federation_sender_config Option
2024-07-02 09:30:13 -04:00
dullbananas
9120207314
Format replaceable_schema files in lint.sh (#4868) 2024-06-26 10:47:09 +02:00
Dessalines
d09854a722
Adding a show_read override to GetPosts. (#4846)
* Adding a show_read override to GetPosts.

- If show_read is true, it overrides the local user show_read
  setting.
- Fixes #4124

* Addressing PR comments.

* Update crates/db_views/src/post_view.rs

Co-authored-by: dullbananas <dull.bananas0@gmail.com>

* Fixing formatting.

---------

Co-authored-by: dullbananas <dull.bananas0@gmail.com>
2024-06-21 17:39:40 -04:00
Dessalines
c8d155102a
Removing renovate from git cliff (#4858)
* Removing renovate from git cliff

* Formatting.
2024-06-21 17:38:44 -04:00
dullbananas
36e6f7ec78
Fix order in CommunityModeratorView::get_community_first_mods (#4859)
* Fix order in `CommunityModeratorView::get_community_first_mods`

* Update community_moderator_view.rs

* Update community_moderator_view.rs
2024-06-21 13:44:55 -04:00
Dessalines
6d8d23130d
Adding an image_details table to store image dimensions. (#4704)
* Adding an image_details table to store image dimensions.

- Adds an image_details table, which stores the height,
  width, and content_type for local and remote images.
- For LocalImages, this information already comes back with
  the upload.
- For RemoteImages, it calls the pictrs details endpoint.
- Fixed some issues with proxying non-image urls.
- Fixes #3328
- Also fixes #4703

* Running sql format.

* Running fmt.

* Don't fetch metadata in background for local API requests.

* Dont export remote_image table to typescript.

* Cleaning up validate.

* Dont proxy url.

* Fixing tests, fixing issue with federated thumbnails.

* Fix tests.

* Updating corepack, fixing issue.

* Refactoring image inserts to use transactions.

* Use select exists again.

* Fixing imports.

* Fix test.

* Removing pointless backgrounded metadata generation version.

* Removing public pictrs details route.

* Fixing clippy.

* Running prettier.

* A few more fixes.

* Moving diesel schema check back down.

* Addressing PR comments.

* Changing back request head to get.

* Fixing lockfile.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-06-20 12:44:06 +02:00
Dessalines
9cf6da1b9e Version 0.19.5 2024-06-19 08:17:45 -04:00
Dessalines
59f274e531
Revert "Removing renovate schedule. (#4808)" (#4847)
This reverts commit 65620913fc.
2024-06-18 16:55:06 -04:00
Dessalines
fa143f72eb Version 0.19.5-alpha.3 2024-06-18 11:49:02 -04:00
Dessalines
63a824a2ed
Fixing TLS connection by installing provider. (#4844)
- Fixes #4795
2024-06-18 09:59:24 -04:00
renovate[bot]
b9dc7612a8
Update dependency @types/node to v20.14.5 (#4843)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-18 10:11:20 +00:00
renovate[bot]
393f65db8d
Update dependency @types/node to v20.14.4 (#4842)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 20:59:40 -04:00
Dessalines
bfefdfd15d Version 0.19.5-alpha.2 2024-06-17 20:26:44 -04:00
renovate[bot]
41d1b054fe
Update dependency @types/node to v20.14.3 (#4840)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 18:07:36 -04:00
dullbananas
42a6d8ab0f
Fix not-equals check in post aggregates update trigger (#4837)
* Fix not-equals check in post aggregates update trigger

Should fix #4836

* Create up.sql

* Create down.sql

* Update down.sql
2024-06-17 15:25:54 -04:00
renovate[bot]
f080400826
Update typescript-eslint monorepo to v7.13.1 (#4838)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 15:04:24 -04:00
renovate[bot]
c55636b0d0
Update pnpm to v9.4.0 (#4839)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 15:03:43 -04:00
renovate[bot]
32b7ee76e3
Update dependency ts-jest to v29.1.5 (#4834)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-16 19:30:34 +00:00
Dessalines
5cc798a146 Version 0.19.5-alpha.1 2024-06-15 15:12:10 -04:00
renovate[bot]
4974dbb1dd
Update Rust crate console-subscriber to 0.3.0 (#4817)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-06-15 08:17:01 -04:00
renovate[bot]
d7a453dd68
Update Rust crate regex to v1.10.5 (#4811)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 07:29:41 -04:00
renovate[bot]
393b221be0
Update typescript-eslint monorepo to v7.13.0 (#4827)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:56:47 -04:00
renovate[bot]
966d949f73
Update dependency eslint to v9 (#4830)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:55:59 -04:00
renovate[bot]
b245bf48c0
Update pnpm to v9.3.0 (#4826)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:55:06 -04:00
renovate[bot]
b569c7df17
Update docker/dockerfile Docker tag to v1.8 (#4824)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:54:51 -04:00
renovate[bot]
0f6bd94407
Update dependency prettier to v3.3.2 (#4823)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:54:31 -04:00
renovate[bot]
5cf1593c9f
Update dependency @types/node to v20.14.2 (#4822)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:54:17 -04:00
renovate[bot]
04400ceac3
Update Rust crate rustls to v0.23.10 (#4816)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:54:00 -04:00
renovate[bot]
dede17bf24
Update Rust crate pict-rs to v0.5.16 (#4815)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:53:44 -04:00
renovate[bot]
b0e2a14d04
Update Rust crate actix-web to v4.7.0 (#4814)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:53:28 -04:00
renovate[bot]
027017b0a8
Update asonix/pictrs Docker tag to v0.5.16 (#4813)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:53:11 -04:00
renovate[bot]
b27b38b9a9
Update Rust crate url to v2.5.1 (#4812)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:52:56 -04:00
renovate[bot]
a7771ff385
Update Rust crate clap to v4.5.7 (#4810)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:52:40 -04:00
renovate[bot]
27e7aa1e04
Update Rust crate actix-web-httpauth to v0.8.2 (#4809)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-15 06:52:21 -04:00
dullbananas
6497ec519e
Refactor LocalUser settings conditions in database views (#4746)
* Create viewer.rs

* Rename viewer.rs to viewer.rs

* Update viewer.rs

* Update post_view.rs

* Update distinguish.rs

* Update like.rs

* Update viewer.rs

* Update list_comment_likes.rs

* Update like.rs

* Update save.rs

* Update like.rs

* revert changes in api crate

* Update post_view.rs

* Update post_view.rs

* Update comment_view.rs

* Update post_view.rs

* Update community_view.rs

* Update comment_view.rs

* Update post_view.rs

* Update viewer.rs

* Update post_view.rs

* Update community_view.rs

* Update local_user_view.rs

* Update viewer.rs

* Update community_view.rs

* Update viewer.rs

* Update lib.rs

* Update comment_view.rs

* Update post_view.rs

* Update viewer.rs

* Update viewer.rs

* Update viewer.rs

* Update viewer.rs

* Update local_user_view.rs

* Update viewer.rs

* Update viewer.rs

* Update local_user_view.rs

* Update community_view.rs

* Update viewer.rs

* Update crates/db_schema/src/viewer.rs

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>

* Update viewer.rs

* Update viewer.rs

* Update viewer.rs

* Update viewer.rs

* Update post_view.rs

* Update community_view.rs

* Update comment_view.rs

* Update viewer.rs

* Update post_view.rs

* Update save.rs

* Update resolve_object.rs

* Update viewer.rs

* Update save.rs

* Update resolve_object.rs

* Update comment_view.rs

* Update post_view.rs

* Update community_view.rs

* Update local_user_view.rs

* Update post_view.rs

* Update viewer.rs

* Update comment_view.rs

* Update post_view.rs

* Update community_view.rs

* Update viewer.rs

* Update viewer.rs

* Update viewer.rs

* Update viewer.rs

* Update viewer.rs

* Some additions to localuser DB view helpers. (#39)

* Some additions to localuser DB view helpers.

- Getting rid of generics.
- Passing in only LocalUser to views.

* Formatting fixes.

* Getting rid of unecessary as_refs

* Fixing clippy.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
Co-authored-by: Dessalines <tyhou13@gmx.com>
2024-06-14 21:51:24 -04:00
Dessalines
65620913fc
Removing renovate schedule. (#4808) 2024-06-14 21:51:10 -04:00
Nutomic
a3c8761bed
Revert "Remove unneeded error "last successful id is higher than latest id" (fixes #4363) (#4486)" (#4806)
This reverts commit c895e57086.
2024-06-14 08:15:12 -04:00
dullbananas
99160228ae
Remove unimplemented in <Comment as Crud>::create (#4796)
* Remove `unimplemented` in `<Comment as Crud>::create`

* Update comment.rs
2024-06-14 10:40:57 +02:00
Dessalines
fc6f46c1ac
Fix issue with GetPost not returning bot cross_posts. (#4804)
- Fixes #4803
2024-06-13 14:32:03 -04:00
dullbananas
046375171e
Don't change encoding style in clean_url_params (#4802)
* Don't change encoding style in `clean_url_params`

Fixes #4801

* fmt

* fix
2024-06-12 20:35:27 -04:00
Dessalines
b2a480f55c
Fixing sed command for postgres upgrade. (#4791)
- Context: https://github.com/LemmyNet/lemmy-ansible/issues/245
2024-06-07 12:39:23 -04:00
Dessalines
9236cf7d21
Remove ansible tagging lines. (#4790) 2024-06-07 11:26:43 -04:00
dullbananas
b559e0206b
Replace wav with hound (#4788)
* Update lib.rs

* Update Cargo.toml

* Update lib.rs

* cargo.lock

* fix simultaneous mutable references
2024-06-07 10:27:49 -04:00
Dessalines
f5f2b5ffc6 Version 0.19.4 2024-06-07 07:51:56 -04:00
dullbananas
1e11faf741
Improve comment in triggers.sql (#4789)
* Clarified existing info
* Added prohibition of inconsistent update order
2024-06-07 07:42:34 -04:00
Dessalines
5d31f0d516 Version 0.19.4-rc.11 2024-06-06 23:02:38 -04:00
Dessalines
844b84a01a Version 0.19.4-rc.10 2024-06-06 20:46:03 -04:00
Dessalines
b0447ad94d
Upgrading lemmy-js-client version to 0.19.4 (#4787)
* Upgrading lemmy-js-client version to 0.19.4

* Upgrading deps before renovate.
2024-06-06 20:44:36 -04:00
Dessalines
3d25322089 Version 0.19.4-rc.9 2024-06-06 10:43:40 -04:00
Dessalines
16a82862b8
Allow empty string to clear URL-type DB fields. (#4780)
* Allow empty string to clear URL-type DB fields.

- To address difficulties with clearing URL-type fields like
  avatars, banners, site icons, this PR turns the URL type form
  fields into strings.
- This allows an empty string to be used as a "clear data", as
  in the case with the regular text form fields.
- Also includes various cleanups.
- Fixes #4777
- Context: #2287

* Fixing comment.

* Use Option<&str> and deref.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-06-06 09:55:08 -04:00
dullbananas
79e6dbf0de
Remove PersonInsertForm builder (#4779)
* Update session_middleware.rs

* Update private_message_report_view.rs

* Update session_middleware.rs

* Update private_message_view.rs

* Update private_message.rs

* Update registration_application_view.rs

* Update actor_language.rs

* Update vote_view.rs

* Update code_migrations.rs

* Update comment_aggregates.rs

* Update person_view.rs

* Update user_settings_backup.rs

* Update person.rs

* Update create.rs

* Update comment_view.rs

* Update moderator.rs

* Update site_aggregates.rs

* Update claims.rs

* Update community_aggregates.rs

* Update post_report.rs

* Update person_mention_view.rs

* Update community_view.rs

* Update comment_report_view.rs

* Update post_report_view.rs

* Update community_moderators.rs

* Update comment.rs

* Update person_aggregates.rs

* Update comment_reply_view.rs

* Update password_reset_request.rs

* Update post_aggregates.rs

* Update community.rs

* Update main.rs

* Update post.rs

* Update person.rs

* Update person.rs

* Update claims.rs

* Update person.rs

* Update create.rs

* Update user_settings_backup.rs

* Update community_moderators.rs

* Update main.rs

* Update comment_aggregates.rs

* Update community_aggregates.rs

* Update person.rs

* Update Cargo.toml

* Update Cargo.toml

* Update person.rs

* fix

* Update code_migrations.rs

* fix submodule

* Update person.rs
2024-06-06 08:29:18 -04:00
Dessalines
fda5ce4482 Version 0.19.4-rc.8 2024-06-05 19:01:37 -04:00
Dessalines
e8cfb5665f
When banning from local communities, make sure they aren't deleted or removed. (#4784)
- This is causing some federation issues.
- Context: #4782
2024-06-05 18:59:46 -04:00
Nutomic
bb94fb1c79
Revert apub library 0.5.7 (#4783)
Wasnt necessary after all
2024-06-05 18:04:02 -04:00
Dessalines
92214a9364 Version 0.19.4-rc.7 2024-06-05 17:30:43 -04:00
Nutomic
78ae874b89
Apub library 0.5.7 (#4781) 2024-06-05 17:28:33 -04:00
Dessalines
a947474c64 Version 0.19.4-rc.6 2024-06-04 08:32:08 -04:00
Dessalines
8bf17946bd
Fix issue with avatar / icon deletion when saving settings. (#4774)
* Fix issue with avatar / icon deletion when saving settings.

- Fixes #4763

* Update crates/api_common/src/request.rs

Co-authored-by: dullbananas <dull.bananas0@gmail.com>

* Fixing an existing test, and adding another for replace images.

---------

Co-authored-by: dullbananas <dull.bananas0@gmail.com>
2024-06-04 08:28:22 -04:00
dullbananas
9ceb5b6386
Clean up build_update_instance_form in scheduled_tasks.rs (#4775)
* Clean up build_update_instance_form in scheduled_tasks.rs

* remove unused import
2024-06-04 08:04:16 -04:00
Dessalines
aefb41b551
Remove .json from nodeinfo urls, according to spec. (#4773) 2024-06-03 17:30:16 -04:00
Dessalines
4195a9b5a1
Fetch nodeinfo href from .well-known/nodeinfo . Fixes #4757 (#4765)
* Fetch nodeinfo href from .well-known/nodeinfo . Fixes #4757

* Addressing PR comments.

* Fixing clippy.

* Adding tests.
2024-06-03 17:30:00 -04:00
Dessalines
69b4c6647b Version 0.19.4-rc.5 2024-06-01 13:30:00 -04:00
renovate[bot]
f7fe0d46fc
Update Rust crate console-subscriber to 0.2.0 (#4771)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 22:35:34 -04:00
renovate[bot]
609a6411a7
Update pnpm to v9.1.4 (#4770)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 22:24:17 -04:00
renovate[bot]
44666a34a2
Update dependency ts-jest to v29.1.4 (#4768)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 22:04:44 -04:00
renovate[bot]
6db878f761
Update dependency typescript to v5.4.5 (#4769)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 21:34:29 -04:00
renovate[bot]
6031709fcf
Update Rust crate serde to v1.0.203 (#4766)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 21:14:05 -04:00
renovate[bot]
4d9e38d875
Update asonix/pictrs Docker tag to v0.5.14 (#4767)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-05-31 21:13:39 -04:00
Dessalines
6a6c915014
Changing NodeInfo metadata to HashMap from vector. Fixes #4762 (#4764) 2024-05-31 16:38:46 -04:00
phiresky
96b7afc0b1
upgrade rust to 1.78 to fix diesel cli (#4761) 2024-05-31 08:39:45 -04:00
Felix Ableitner
d2083f79d9 Version 0.19.4-rc.4 2024-05-30 11:55:34 +02:00
phiresky
e8a7bb07a3
fix both permanent stopping of federation queues and multiple creation of the same federation queues (#4754)
Co-authored-by: Nutomic <me@nutomic.com>
2024-05-30 05:08:27 -04:00
Richard Schwab
91e57ff954
Prevent bot replies from increasing unread reply count when bot accounts are not shown (#4747)
* Prevent bot replies from increasing unread reply count when bot accounts are not shown

* Pass LocalUser for unread replies count query

* Prevent bot mentions from increasing unread reply count when bot accounts are not shown
2024-05-29 17:55:15 -04:00
phiresky
7d80a3c7d6
replace instanceid with domain (#4753) 2024-05-29 23:10:25 +02:00
Dessalines
abcfa266af
Fixing slowness in saved post fetching. #4756 (#4758)
* Fixing slowness in saved post fetching. #4756

* Also fix comment_view.rs
2024-05-29 17:03:42 -04:00
SleeplessOne1917
51970ffc81
Update dependencies to alleviate cargo audit peer dependency vulnerability (#4750) 2024-05-28 17:47:21 -07:00
Dessalines
fd6a1283a5 Version 0.19.4-rc.3 2024-05-27 09:37:58 -04:00
Nutomic
af034f3b5e
Unit tests and cleanup for outgoing federation code (#4733)
* test setup

* code cleanup

* cleanup

* move stats to own file

* basic test working

* cleanup

* processes test

* more test cases

* fmt

* add file

* add assert

* error handling

* fmt

* use instance id instead of domain for stats channel
2024-05-27 09:34:58 -04:00
Dessalines
0d5db29bc9
After creating a comment, update the unread comments for the post. (#4742)
* After creating a comment, update the unread comments for the post.

- Fixes #3863

* Addressing PR comments.

* Add comment.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-05-27 12:55:44 +02:00
dullbananas
ec77c00ef8
Fix lost separation caused by comment width change (#4739)
* Update post_view.rs

* Update structs.rs

* Update worker.rs

* Update worker.rs
2024-05-23 14:05:35 -04:00
Dessalines
69bdcb3069 Version 0.19.4-rc.2 2024-05-23 12:10:33 -04:00
Dessalines
6a6108ac55
Fixing proxied images for federated posts. (#4737)
* Fixing proxied images for federated posts.

- Also added test.
- Fixes #4736

* Address PR comments.
2024-05-23 11:11:25 -04:00
Nutomic
b2c1a14234
Correct url for nodeinfo version (#4734)
* Correct url for nodeinfo version

* add compat redirect

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-05-23 10:59:56 -04:00
Nutomic
d8dc38eb06
Upgrade dependencies (#4740) 2024-05-23 10:55:20 -04:00
Nutomic
c96017c009
Configure max comment width in clippy (#4738)
* Configure max comment width in clippy

* update default config
2024-05-23 08:46:26 -04:00
Dessalines
9aa565b559 Version 0.19.4-rc.1 2024-05-22 08:58:31 -04:00
Dessalines
7d7cd8ded4
Dont show replies / mentions from blocked users. Fixes #4227 (#4727)
* Dont show replies / mentions from blocked users. Fixes #4227

* Adding unit tests for reply and mention views.

- Also cleaned up some unwraps in the tests.

* Add allow deprecated to pass clippy for deprecated wav crate.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-05-22 08:50:26 -04:00
Nutomic
943c31cc72
Allow passing command line params via environment (fixes #4603) (#4729)
* Allow passing command line params via environment (fixes #4603)

* add prefix

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-05-22 08:39:01 -04:00
Nutomic
973f39601c
Dont allow removing comment which was deleted (fixes #4731) (#4732) 2024-05-22 08:29:01 -04:00
Felix Ableitner
a39c19c9db Version 0.19.4-beta.8 2024-05-22 10:30:38 +02:00
Dessalines
55f84dd38a
Fixing proxy images (#4722)
* Adding an image_details table to store image dimensions.

- Adds an image_details table, which stores the height,
  width, and content_type for local and remote images.
- For LocalImages, this information already comes back with
  the upload.
- For RemoteImages, it calls the pictrs details endpoint.
- Fixed some issues with proxying non-image urls.
- Fixes #3328
- Also fixes #4703

* Running sql format.

* Running fmt.

* Don't fetch metadata in background for local API requests.

* Dont export remote_image table to typescript.

* Cleaning up validate.

* Dont proxy url.

* Fixing tests, fixing issue with federated thumbnails.

* Fix tests.

* Updating corepack, fixing issue.

* Refactoring image inserts to use transactions.

* Use select exists again.

* Fixing imports.

* Fix test.

* Removing pointless backgrounded metadata generation version.

* Removing public pictrs details route.

* Fixing clippy.

* Fixing proxy image fetching. Fixes #4703

- This extracts only the proxy image fixes from #4704, leaving off
  thumbnails.

* Fix test.

* Addressing PR comments.

* Address PR comments 2.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-05-22 10:28:47 +02:00
Nutomic
6b46a70535
Extra logging to debug duplicate activities (ref #4609) (#4726)
* Extra logging to debug duplicate activities (ref #4609)

* Fix logging for api tests

* fmt
2024-05-21 14:47:06 -04:00
Nutomic
4ffaa93431
Dont allow reusing password reset token, use normal rate limit (#4719)
* Dont allow reusing password reset token, use normal rate limit

* fix
2024-05-21 14:46:49 -04:00
flamingos-cant
a0ad7806cb
Increase alt_text size to 1500 (#4724) 2024-05-17 13:03:19 -04:00
Nutomic
99aac07714
Mark database fields as sensitive so they dont show up in logs (#4720)
* Mark database fields as sensitive so they dont show up in logs

* add file

* fix test

* Update crates/apub/src/objects/person.rs

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>

* Update crates/apub/src/objects/community.rs

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>

* Update crates/apub/src/objects/instance.rs

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-05-16 20:41:57 -04:00
Dessalines
1a4aa3eaba
Stop using a diesel_cli docker image, use cargo-install in woodpecker. (#4723)
* Stop using a diesel_cli docker image, use cargo-binstall in woodpecker.

- The diesel_cli image is 500MB, and rebuilt daily. Much easier to use
  binstall to install it.

* Trying out a multiline var.

* Try sequence merges 1

* Try removing features.

* Try path fix.

* Abstracting diesel cli install.

* Try installing mysql and postgres.

* Try installing mysql and postgres 2.

* Try installing mysql and postgres 3.

* Try installing mysql and postgres 4.

* Try installing mysql and postgres 5.

* Try installing mysql and postgres 6.

* Try installing mysql and postgres 7.

* Try installing mysql and postgres 8.

* Try installing mysql and postgres 9.

* Try installing mysql and postgres 10.

* Try installing mysql and postgres 11.

* Try installing mysql and postgres 12.

* Try installing mysql and postgres 13.

* Add logging line.

* Add logging line 2.

* Add logging line 3.

* Add logging line 4.

* Removing binstall.

* Extract taplo into its own image, ignore binstall.

* Use a smaller taplo.

* taplo is the same image.
2024-05-16 16:41:36 -04:00
Nutomic
93c9a5f2b1
Dont federate post locking via Update activity (#4717)
* Dont federate post locking via Update activity

* cleanup

* add missing mod log entries

* update assets
2024-05-15 07:36:00 -04:00
Nutomic
9a9d518153
Fix import blocked objects (#4712)
* Allow importing partial backup (fixes #4672)

* Fetch blocked objects if not known locally (fixes #4669)

* extract helper fn

* add comment

* cleanup

* remove test

* fmt

* remove .ok()
2024-05-14 23:03:43 -04:00
Nutomic
7fb03c502e
Add test to ensure reports are sent to user's home instance (ref #4701) (#4711)
* Add test to ensure reports are sent to user's home instance (ref #4701)

* enable all tests

* set package-manager-strict=false
2024-05-14 22:48:24 -04:00
Nutomic
49bb17b583
Stricter rate limit for login (#4718) 2024-05-14 22:43:43 -04:00
Nutomic
723cb549d4
Allow importing partial backup (fixes #4672) (#4705)
* Allow importing partial backup (fixes #4672)

* Dont throw error on empty LocalUser::update

* fix tests
2024-05-14 22:37:30 -04:00
Nutomic
8b6a4c060e
Make nodeinfo standard compliant, upgrade to nodeinfo 2.1 (fixes #4702) (#4706)
* Always set activitypub protocol in nodeinfo response (fixes #4702)

* Add mandatory fields
2024-05-13 22:53:20 -04:00
Dessalines
cb80980027 Version 0.19.4-beta.7 2024-05-11 13:51:09 -04:00
dullbananas
c4fc3a8ede
Optimize stuff in attempt to fix high amount of locks, and fix comment_aggregates.child_count (#4696)
* separate triggers

* auto_explain.log_triggers=on

* Revert "auto_explain.log_triggers=on"

This reverts commit 078b2dbb9b.

* Revert "separate triggers"

This reverts commit 95600da4af.

* bring back migration

* re-order statements

* add comment about statement ordering

* no redundant updates

* optimize post_aggregates update in comment trigger

* set comment path in trigger

* update comment_aggregates.child_count using trigger

* move `LEFT JOIN post` to inner query

* clean up newest_comment_time_necro

* add down.sql
2024-05-09 08:18:55 -04:00
Nutomic
b4f9ef24a5
Dont exit early when running only scheduled tasks (#4707)
* Dont exit early when running only scheduled tasks (fixes #4709)

* fix
2024-05-08 14:56:44 +02:00
Nutomic
866d752a3c
Instance.preferred_username should be optional (fixes #4701) (#4713) 2024-05-08 08:01:04 -04:00
Nutomic
e0b1d0553d
Add timeout for processing incoming activities (#4708)
* Add timeout for processing incoming activities

* move to const
2024-05-08 08:00:55 -04:00
Nutomic
7c146272c3
Federate with wordpress, improvements for NodeBB, Discourse federation (#4692)
* Federate with wordpress

* upgrade apub lib with fix

* Also read post's community from `audience`

* cleanup

* cargo update

* upgrade apub lib

* add wordpress test activity
2024-05-07 16:20:43 -04:00
Nutomic
cfdc732d3a
On registration set show_nsfw based on site.content_warning (#4616)
Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-05-07 16:18:58 -04:00
Tim Coombs
522f974e30
fix: use docker compose v2 (#4622)
* fix: use docker compose v2

* Using sudo tee.

* fix: correct postgres sed command

---------

Co-authored-by: Dessalines <tyhou13@gmx.com>
Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-05-07 11:41:40 +02:00
SleeplessOne1917
b152be7951
Update rustls (#4690)
* Update rustls

* Format code
2024-05-03 16:06:14 -04:00
SleeplessOne1917
485b0f1a54
Replace unmaintained encoding dep with maintained encoding_rs dep (#4694)
* Replace dependency on unmaintained encoding crate with dep on maintained encoding_rs crate

* Update lockfile

* Taplo format Cargo.toml

* Use better variable name

* Replace into_owned with into
2024-05-03 10:42:48 +00:00
Nutomic
7540b02723
Update activitypub library (#4691)
https://github.com/LemmyNet/activitypub-federation-rust/releases/tag/0.5.5
2024-05-02 12:46:34 -04:00
Nutomic
7746db4169
Testing and minor fix for federation with Discourse (#4628)
* Testing and minor fix for federation with Discourse

* prettier
2024-05-02 07:49:19 -04:00
Dessalines
db2ce81fc4
Show trigger logging. #4681 (#4688) 2024-05-01 18:46:14 -04:00
renovate[bot]
4175a1af80
chore(deps): update rust crate serde_with to 3.8.1 (#4687)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 23:47:02 -04:00
renovate[bot]
563280456e
chore(deps): update pnpm to v9.0.6 (#4682)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 23:43:14 -04:00
Dessalines
2fecb7ecdf
Dont show own comments for liked and disliked_only. Fixes #4675 (#4679)
Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-30 23:26:55 -04:00
renovate[bot]
2c6f9c7fd5
chore(deps): update rust crate serde to 1.0.199 (#4684)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 23:17:23 -04:00
renovate[bot]
e338e59868
fix(deps): update rust crate lettre to 0.11.7 (#4685)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 22:54:16 -04:00
renovate[bot]
b0caa85ed4
chore(deps): update rust crate base64 to 0.22.1 (#4683)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-30 22:30:19 -04:00
Dessalines
ad60d91f5c
Dont publish lemmy_db_perf to fix crates.io publish. Fixes #4678 (#4680) 2024-04-30 17:51:12 +00:00
Dessalines
6423d2dde5 Version 0.19.4-beta.6 2024-04-30 06:38:44 -04:00
Nutomic
12163701e7
Avoid crash when handling urls without domain (#4676)
* Avoid crash when handling urls without domain

* Add some extra checks
2024-04-30 06:33:37 -04:00
Dessalines
5c35e97a75
Dont show deleted / removed posts when searching. Fixes #4576 (#4671)
* Dont show deleted / removed posts when searching. Fixes #4576

* Address PR comments.

* Clean up comment removed also.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-30 12:24:18 +02:00
SleeplessOne1917
b05f221565
Remove login step from publish to crates.io (#4674) 2024-04-30 00:07:03 +00:00
Nutomic
beec080274
Testing for federation with NodeBB, make community.followers_url optional (#4629)
* Testing for federation with NodeBB, make community.followers_url optional

* clippy
2024-04-29 12:34:11 +02:00
Dessalines
492d8f1b01
Fix communities with broken outboxes, and use PostView. Fixes #4658 (#4668)
* Fix communities with broken outboxes, and use PostView. Fixes #4658

* Fixing tests.

* Dont pass ref and clone.
2024-04-29 12:22:00 +02:00
dullbananas
d3737d4453
Optimize actor_language.rs (#4612)
* Remove useless transaction in actor_language.rs

* Update actor_language.rs

* site

* community

* Update actor_language.rs

* Update actor_language.rs

* Update actor_language.rs

* Update actor_language.rs

* Update actor_language.rs
2024-04-27 10:59:58 -04:00
Dessalines
b459949f57 Version 0.19.4-beta.5 2024-04-25 19:59:24 -04:00
Dessalines
93f5df2d2a
Adding post_id desc to all post_aggregates indexes. Fixes #4618 (#4662)
* Adding post_id desc to all post_aggregates indexes. Fixes #4618

* Running pg_format

* Not rebuilding indexes which had no changes.
2024-04-25 18:19:02 -04:00
Nutomic
cf426493e1
Fix community add mod check (fixes #4624) (#4667) 2024-04-25 11:47:38 -04:00
Dessalines
8e3ff0408e
Fixing extra modlog entries when post_id or comment_id is given. (#4664)
- Previously when given a post_id, it didn't filter out any other
  modlog entries, such as community removals. This fixes that problem.
- Context: https://github.com/LemmyNet/lemmy-ui/pull/2437

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-25 10:26:17 +02:00
Dessalines
66e06b3952
Removing scheme from block urls. Fixes #4656 (#4659)
* Removing scheme from block urls. Fixes #4656

* Fix comment.

* Fixing domain checking.

* Removing pointless URL building in url blocklist regex.

* Remove trailing /
2024-04-23 23:15:20 -04:00
Kroese
6b9d9dfaa5
Fix broken thumbnails (#4661)
* Check is_image_post flag

* Keep cargo_fmt happy

* Filter on is_image_post

* Trigger CI

* Keep cargo_fmt happy
2024-04-23 22:52:56 -04:00
tracyspacy
0eaf8d33e7
Filter_removed_comments_from_search (#4634)
* filter_removed_comments_from_search

* Revert "filter_removed_comments_from_search"

This reverts commit c6d6490afa.

* filtering_removed_comments_search

* filter_deleted_comments

* Revert "filter_deleted_comments"

This reverts commit 7dc1d13d24.

* Revert "filtering_removed_comments_search"

This reverts commit 6e9b1de7a2.

* filtering_removed_dELeted_comments_search
2024-04-22 11:33:02 -04:00
renovate[bot]
c31a29ec7f
chore(deps): update dependency @types/node to v20.12.7 (#4647)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-19 18:56:35 -04:00
renovate[bot]
80635c9e24
chore(deps): update rust crate base64 to 0.22.0 (#4651)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-19 18:40:38 -04:00
renovate[bot]
95d75e07b2
chore(deps): update pnpm to v9.0.4 (#4649)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-19 18:23:06 -04:00
renovate[bot]
efbfdc9340
chore(deps): update docker/dockerfile docker tag to v1.7 (#4650)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-19 18:12:26 -04:00
renovate[bot]
1ae3aab764
chore(deps): update dependency typescript to v5.4.5 (#4648)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-04-19 18:11:21 -04:00
renovate[bot]
f68881c552
chore: Configure Renovate (#4644)
* Add renovate.json

* Updating renovate.

---------

Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Dessalines <tyhou13@gmx.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-04-19 17:00:23 -04:00
Dessalines
2ba1ba88b8
Upgrading deps. (#4645) 2024-04-19 16:50:27 -04:00
Dessalines
079fa0b8f6 Version 0.19.4-beta.4 2024-04-18 21:11:15 -04:00
dependabot[bot]
b0a740d5c5
Bump h2 from 0.3.25 to 0.3.26 (#4639)
Bumps [h2](https://github.com/hyperium/h2) from 0.3.25 to 0.3.26.
- [Release notes](https://github.com/hyperium/h2/releases)
- [Changelog](https://github.com/hyperium/h2/blob/v0.3.26/CHANGELOG.md)
- [Commits](https://github.com/hyperium/h2/compare/v0.3.25...v0.3.26)

---
updated-dependencies:
- dependency-name: h2
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-04-18 21:05:45 -04:00
Kroese
ee46242a43
Update aarch64-lemmy-linux-gnu to v0.3.0 (#4638) 2024-04-18 20:34:55 -04:00
dullbananas
4ba6221e04
Move SQL triggers from migrations into reusable sql file (#4333)
* stuff

* stuff including batch_upsert function

* stuff

* do things

* stuff

* different timestamps

* stuff

* Revert changes to comment.rs

* Update comment.rs

* Update comment.rs

* Update post_view.rs

* Update utils.rs

* Update up.sql

* Update up.sql

* Update down.sql

* Update up.sql

* Update main.rs

* use anyhow macro

* Create down.sql

* Create up.sql

* Create replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update utils.rs

* Update .woodpecker.yml

* Update sql_format_check.sh

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Create dump_schema.sh

* Update start_dev_db.sh

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* stuff

* Update replaceable_schema.sql

* Update .pg_format

* fmt

* stuff

* stuff (#21)

* Update replaceable_schema.sql

* Update up.sql

* Update replaceable_schema.sql

* fmt

* update cargo.lock

* stuff

* Update replaceable_schema.sql

* Remove truncate trigger because truncate is already restricted by foreign keys

* Update replaceable_schema.sql

* fix some things

* Update replaceable_schema.sql

* Update replaceable_schema.sql

* Update .woodpecker.yml

* stuff

* fix TG_OP

* Psql env vars

* try to fix combine_transition_tables parse error

* Revert "try to fix combine_transition_tables parse error"

This reverts commit 75d00a4626.

* refactor combine_transition_tables

* try to fix create_triggers

* fix some things

* try to fix combined_transition_tables

* fix sql errors

* update comment count in post trigger

* fmt

* Revert "fmt"

This reverts commit a5bcd0834b.

* Revert "update comment count in post trigger"

This reverts commit 0066a4b42b.

* fix everything

* Update replaceable_schema.sql

* actually fix everything

* refactor create_triggers

* fix

* add semicolons

* add is_counted function and fix incorrect bool operator in update_comment_count_from_post

* refactor comment trigger

* refactor post trigger

* fix

* Delete crates/db_schema/src/utils/series.rs

* subscribers_local

* edit migrations

* move migrations

* remove utils::series module declaration

* fix everything

* stuff

* Move sql to schema_setup dir

* utils.sql

* delete .pg_format

* Update .woodpecker.yml

* Update sql_format_check.sh

* Update .woodpecker.yml

* Merge remote-tracking branch 'upstream/main' into bliss

* fmt

* Create main.rs

* Update lib.rs

* Update main.rs

* Update .woodpecker.yml

* Update main.rs

* Update Cargo.toml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update triggers.sql

* YAY

* Update mod.rs

* Update Cargo.toml

* a

* Update Cargo.toml

* Update Cargo.toml

* Delete crates/db_schema/src/main.rs

* Update Cargo.toml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update utils.sql

* Update utils.sql

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update down.sql

* Update up.sql

* Update triggers.sql

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update triggers.sql

* Update down.sql

* Update .woodpecker.yml

* Update Cargo.toml

* Update .woodpecker.yml

* Update Cargo.toml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update mod.rs

* Update Cargo.toml

* Update mod.rs

* make dump_schema.sh executable

* fix dump_schema.sh

* defer

* diff dumps

* fmt

* Update utils.sql

* Update .woodpecker.yml

* use correct version for pg_dump

* Update .woodpecker.yml

* Update .woodpecker.yml

* change migration date

* atomic site_aggregates insert

* temporarily repeat tests in CI

* drop r schema in CI migration check

* show ReceivedActivity::create error

* move check_diesel_migration CI step

* Update .woodpecker.yml

* Update scheduled_tasks.rs

* Update scheduled_tasks.rs

* update cargo.lock

* move sql files

* move rank functions

* filter post_aggregates update

* fmt

* cargo fmt

* replace post_id with id

* update cargo.lock

* avoid locking rows that need no change in up.sql

* only run replaceable_schema if migrations were run

* debug ci test failure

* make replaceable_schema work in CI

* Update .woodpecker.yml

* remove println

* Use migration revert and git checkout

* Update schema_setup.rs

* Fix

* Update schema_setup.rs

* Update schema_setup.rs

* Update .woodpecker.yml

---------

Co-authored-by: Nutomic <me@nutomic.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-04-17 20:58:44 -04:00
Nutomic
31829b6c05
Untangle thumbnail generation logic (ref #4604) (#4615)
* Untangle thumbnail generation logic (ref #4604)

* prettier

* test cleanup

* fix tests

* also consider opengraph image for local thumbnail generation
2024-04-17 10:36:45 -04:00
TechVest
b0370ae2fd
chore: fix some comments (#4637)
Signed-off-by: TechVest <techdashen@qq.com>
2024-04-17 14:35:54 +02:00
Dessalines
6efab9aab1
Creating a LocalImageView, so that front ends have the Person struct. (#4631)
* Creating a LocalImageView, so that front ends have the Person struct.

* Removing local_user from LocalImageView.

* Add uploader check.
2024-04-16 19:20:44 -04:00
Dessalines
d075acce43
Make all single-fetch database calls return an Option. (#4617)
- Diesel ordinarily throws an error when no results are returned for a
  single fetch, which is a bit confusing. This PR ensures that the
  missing value cases are all caught, and wrapped with new LemmyErrors,
  rather than diesel errors.
- Fixes #4601
2024-04-16 14:48:15 +02:00
Nutomic
3a0c1dca90
Avoid overwriting local objects via federation (#4611)
* Dont allow federation to overwrite local objects

* is_local check in apub lib

* use imports

* fix check, update lib

* use verify_is_remote_object()

* submodule
2024-04-11 10:05:49 -04:00
dullbananas
0f6b13a4ec
Test coverage (#4596)
* update .gitignore

* add test-with-coverage.sh

* coverage gutters extension comment

* move lcov.info to target folder

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-11 10:32:07 +02:00
Dessalines
64760ec960 Version 0.19.4-beta.3 2024-04-10 11:03:11 -04:00
Dessalines
555f789269
Fixing custom_thumbnail updates. (#4593)
* Fixing custom_thumbnail updates.

* Fixing issue with image posts.

* Fixing upgrade deps script.

* Adding API tests for custom thumbnails.

* Remove pointless todo.

* Address PR comments.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-10 10:59:46 -04:00
Dessalines
5dea21d531
Convert all Result<..., LemmyError> into LemmyResult<...> Fixes #4613 (#4614)
* Convert all Result<..., LemmyError> into LemmyResult<...> Fixes #4613

* Fixing clippy.
2024-04-10 10:14:11 -04:00
Kroese
d5622a65f8
Fix for PictrsImageMode::None (#4604)
* Fix PictrsImageMode::None

* Update crates/api_common/src/request.rs

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>

* Fix formatting

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-10 10:09:54 -04:00
Nutomic
9059de8569
Allow fetching from local url, add fetch redirect test (fixes #4526) (#4607)
* Allow fetching from local url, at fetch redirect test (fixes #4526)

* prettier

* update lib

* update apub lib
2024-04-10 10:04:57 -04:00
Nutomic
0203b62a6d
Ignore old federated post edits (ref #4529) (#4586)
* Ignore old federated post edits (ref #4529)

* use filter on insert

* coalesce(updated, published)

* avoid comment conflict clause

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-10 10:03:51 -04:00
Dessalines
99d585b7be
Change defaults on user vote display mode to upvotes + downvotes (#4599)
* Change defaults on user vote display mode to upvotes + downvotes

* Forgot to regenerate the rows.

* Drop and re-add columns instead.
2024-04-10 10:47:05 +02:00
Nutomic
b4670988b5
Change exponential backoff algorithm for federation send (#4597)
* Limit federation send retry interval to one hour

* clippy

* avoid overflow

* change base for exp backoff

* ignore first error

* fix day duration
2024-04-09 19:33:01 -04:00
Nutomic
1d0a6ac08f
Avoid breaking api change, reduce api cache duration (#4610)
* Dont mark site.public_key as `serde(skip)` to avoid breaking change (fixes #4605)

* Reduce cache duration for api
2024-04-09 10:10:20 -04:00
Dessalines
8e54a4a6cc
Fixing bug where comment replies wouldn't be sent to blocked instances. (#4595)
* Fixing bug where comment replies wouldn't be sent to blocked instances.

- Instance blocks should only affect communities, not comments.
- Fixes #4590

* Revert "Fixing bug where comment replies wouldn't be sent to blocked instances."

This reverts commit 1349aa351a.

* Only block replies from the community's instance id.

- Also refactor send_local_notifs slightly, since it has to fetch the
  community now.
- Fixes #4590

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-04-08 16:26:24 +02:00
jim-taylor-business
a14ebefd24
When env variable is set, any config file will be ignored and the default settings will be used (#4594)
* do not panic when no config file found use defaults

* formatting

* implement env variable

* ermove commented code

* remove redundant comment

* remove redundant space

* simplify check logic

* format

* returns and messages

* correct mistake
2024-04-08 12:05:54 +02:00
Dessalines
705e86eb4c
Fixing docker release (#4592)
* Re-add notif on tag failure.

* Upping rust version.

* Version 0.19.4-beta.1a

* Try again.

* Version 0.19.4-beta.1b

* Removing unstable inspect.

* Version 0.19.4-beta.1c

* Remove use release cache.

* Trying to fix cargo publish 1.

* Version 0.19.4-beta.1d

* Re-adding publish release

* Version 0.19.4-beta.2

* Fixing workspace for lemmy_federate
2024-04-04 16:14:59 +02:00
Dessalines
a1d632e582
Re-add notif on tag failure. (#4591) 2024-04-04 10:21:31 +02:00
Nutomic
087684658a
Cache result of LocalSite::read to avoid unnecessary db calls (#4585)
* Cache result of LocalSite::read to avoid unnecessary db calls

* single const for cache duration

* clippy

* revert apub send changes

* clippy

* fmt
2024-04-03 17:38:31 -04:00
Dessalines
aaaa362b98
Remove latest tag for pgformatter. (#4589) 2024-04-03 17:29:24 -04:00
Dessalines
5237233f97 Version 0.19.4-beta.1 2024-04-03 16:50:35 -04:00
Dessalines
94438a8516
Removing cardano, this was never used. (#4588) 2024-04-03 10:38:57 +02:00
Dessalines
4d9c16a336
Fix private message sort order. #4581 (#4587) 2024-04-02 13:25:28 -04:00
Nutomic
ae9f82b452
Read crate version from cargo.toml (fixes #4583) (#4584) 2024-04-02 11:19:51 -04:00
dullbananas
007e9b7aab
Optimize Community::set_featured_posts (#4579)
* Don't lock excess rows in Community::set_featured_posts

* Update community.rs

* Update community.rs

* Update community.rs

* Update community.rs
2024-04-02 11:19:04 -04:00
tracyspacy
60f9a97dfa
Fix unnecessarily duplicated notifs (#4578)
* add check to remove duplicated notifs

* added comments
2024-03-29 17:09:19 -04:00
Dessalines
067332553d
Fixing woodpecker. (#4575) 2024-03-27 11:09:44 -04:00
Nutomic
a4b79ca610
Generate post thumbnail/metadata in background (ref #4529) (#4564)
* Generate post thumbnail/metadata in background (ref #4529)

* fix api test

* Apply suggestions from code review

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>

* fix test

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-03-27 10:54:42 -04:00
Dessalines
a632a86852
Delete a person's local images on delete account. (#4506)
* Delete a person's local images on delete account.

* Rename purge function to delete.

* Use purge_user_account instead of Person::delete_account in purge person.

* Fixing clippy
2024-03-27 10:28:02 -04:00
Nutomic
85ee89f4e8
When uploading new icon/avatar/banner, delete old one (#4573) 2024-03-27 09:00:52 -04:00
Dessalines
6bfbb9332d
Adding listMedia endpoint, to view all your local image uploads. (#4509)
* Adding listMedia endpoint, to view all your local image uploads.

- Fixes #4445

* Fix ts import.

* Forgot to order by published desc

* Adding an endpoint to list all images, for admins only.

* Forgot to add file.

* Add additional test.

* Use better logic for no-limit version.

* Better call sites.

* Adding another test.

* Fix tests.

* Moving list_media to /account action.

* Addressing PR comments.

* Removing pointless comment.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-03-26 12:06:11 -04:00
Dessalines
945064726f
Add creator_banned_from_community to vote_view. (#4568)
* Add creator_banned_from_community to vote_view.

- Fixes #4561

* Adding tests.
2024-03-26 16:22:04 +01:00
Dessalines
7929e77602
Fixing issue with comment replies wrongly marked as read. (#4567)
* Fixing issue with comment replies wrongly marked as read.

- Fixes #4566

* Elaborating on a comment.
2024-03-26 10:46:37 -04:00
Dessalines
95069d7648
Fixing some clippy and woodpecker lints. (#4565)
* Fixing some clippy and woodpecker lints.

* Try fixing woodpecker 1.

* Revert "Try fixing woodpecker 1."

This reverts commit 7c2020a08d.
2024-03-26 10:17:42 +01:00
Dessalines
e4356a7701
Fixing a few broken tests from the change in LocalUser::create (#4569) 2024-03-25 19:14:35 -04:00
Nutomic
846848c4f6
On registration, automatically set content languages from accept-language header (#4550)
* On registration, automatically set content languages from accept header

* no need to set site language or default language for new user anymore

* fix test

* fix langs

* avoid duplicate writing of new user languages
2024-03-25 16:02:12 -04:00
Nutomic
d06ef2c47e
Migrate apub block activity to standard endTime property and deprecate expires (fixes #2316) (#4541)
* Migrate apub block activity to standard `endTime` property (fixes #2316)

* add todo
2024-03-25 08:10:09 -04:00
Sander Saarend
99d01e186a
Fix rate limiter (#4560) 2024-03-25 07:56:03 -04:00
Nutomic
ef4bb3cc40
Add delete user field removeData to apub assets (fixes #4544) (#4549) 2024-03-22 18:41:59 -04:00
Nutomic
21547dedf7
Fix handling of apub downvote (fixes #4545) (#4551)
* Fix handling of apub downvote (fixes #4545)

* fmt

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-03-22 18:41:09 -04:00
Nutomic
baf5921d2c
Add comment about console feature requiring tokio_unstable (fixes #4553) (#4555) 2024-03-22 18:40:08 -04:00
SleeplessOne1917
6679b2559f
Add SleeplessOne1917 to CODEOWNERS (#4558)
Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
2024-03-22 18:31:45 -04:00
SleeplessOne1917
38c22d9453
Add banned_from_community to PostView and CommentView (#4552)
* Add banned_from_community to PostView and CommentView

* Add post view test

* Add tests for CommentView

* Add tests for case where local user is not banned from community

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
2024-03-22 18:31:08 -04:00
Tomas
78581bd696
Add ARG RUSTFLAGS into Dockerfile (#4556)
* Add ARG RUSTFLAGS into Dockerfile 

Allows passing RUSTFLAGS through --build-arg

* Remove invalid ARG syntax
2024-03-21 16:42:30 +01:00
Dessalines
2fd81067c7
Temporarily comment sticky test. (#4538) 2024-03-18 12:54:45 +01:00
Dessalines
0f77951e05
Upgrading deps. (#4537)
* Upgrading deps.

* Addressing PR comments
2024-03-18 10:36:49 +01:00
Nutomic
9d4299aaac
Dont require leading ! or @ for webfinger resolve (#4513)
* Dont require leading ! or @ for webfinger resolve

* fmt

* clippy
2024-03-15 08:42:09 -04:00
Nutomic
43378c5bb3
Fix video thumbnail generation (fixes #3484) (#4539)
* Fix video thumbnail generation (fixes #3484)

* fix test
2024-03-15 08:41:16 -04:00
flamingos-cant
19a1a077c5
Add a blocklist for URLs. (#4515)
* Add a blocklist for URLs.

* Fix SQL format

* Make clippy happy.

* Use regex for URL matching.

* Escape regex chars in URLs.

* Use post for modification.

* Make URL block regex static and remove API routes.

* Add date fields to table and use transaction.

* Use Cache for blocklist.

* Rename check_links + move list to parameters of process_markdown.

* SQL format.

* Format, again.

* Remove println.

* Add API test.

* Set a shorter lifetime for regex in debug mode.

* Add missing macro.

* Update lemmy-js-client

* Update api_test/pnpm-lock.yaml

* Don't break other tests

* Use different URL for test

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
Co-authored-by: Nutomic <me@nutomic.com>
2024-03-15 07:03:29 -04:00
Nutomic
0e7080337b
Dont allow admins to post in community with posting_restricted_to_mods (fixes #3571) (#4534)
* Dont allow admins to post in community with `posting_restricted_to_mods` (fixes #3571)

* fmt
2024-03-14 17:31:54 -04:00
Nutomic
835d329134
Fix longstanding bug that breaks initial community view (fixes #3529) (#4535) 2024-03-14 16:57:56 -04:00
Nutomic
f1de7b7590
Automatically include apub hashtag with posts (fixes #3906) (#4533) 2024-03-14 12:16:45 -04:00
Dessalines
255e695633
Adding extra fields to PostReport and CommentReport views. (#4520)
- Fixes #4200
2024-03-13 12:11:24 -04:00
Dessalines
15f02f00a9
Add a vote_display_mode local_user setting. (#4450)
* Add a vote_display_mode local_user setting.

- Fixes #4449

* Changing HideDownvotes to Score.

* Adding ScoreAndDownvote display mode.

* Adding upvote and downvote mode.

* Extracting vote_display_mode to another table.

* Fixing fmt.

* Remove published and updated columns.

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-03-13 12:10:58 -04:00
Dessalines
45c56df4e8
Adding git cliff to help generate changelogs. (#4522) 2024-03-12 11:24:27 +01:00
Nutomic
5d361d63ef
Change 2FA to use hostname as issuer (fixes #4518) (#4525) 2024-03-11 16:27:05 -04:00
Dessalines
10bf7464b1
Add code of conduct rules to issue template. (#4523) 2024-03-08 11:53:57 -05:00
Nutomic
5859502a2a
Fix missing private key for signed fetch (#4516)
* Fix missing private key for signed fetch (fixes #4451)

* clippy

* instance actor name and webfinger

* better webfinger handling

* upgrade lib

* update test asset
2024-03-08 10:23:15 -05:00
Nutomic
00f7778485
Store thumbnails in db table local_image (#4512)
* Store thumbnails in db table local_image

* fmt
2024-03-08 10:17:26 -05:00
Nutomic
bc2e75d5a3
Add @dullbananas to codeowners (#4521) 2024-03-08 10:16:05 -05:00
battmdpkq
f228f9d7a9
fix some typos (#4519)
Signed-off-by: battmdpkq <cmaker@163.com>
2024-03-08 10:38:20 +01:00
Nutomic
fed6b61eaf
Upgrade apub lib, correct webfinger content-type (#4498)
* Upgrade apub lib, correct webfinger content-type

* fmt

* fix test by avoiding network fetch
2024-03-06 11:21:46 -05:00
Raphael Lullis
bdabc5e827
Add curl to base images (#4510)
Having a tool like curl or wget can help us to run healthchecks on
docker-based deployments more easily. This commit adds curl to the
list of deb packages that are installed as external dependencies
2024-03-06 11:15:13 +01:00
Nutomic
97e2dbdfd7
Update contact section in readme (#4508)
* Update contact section in readme

* Update README.md

* Update README.md
2024-03-05 13:07:52 -05:00
Nutomic
22c2e20fc5
Remove contributing.md (#4507) 2024-03-05 13:07:20 -05:00
Dessalines
6778279bb6
When purging a federated user, federate local community removals. (#4505) 2024-03-05 09:31:04 -05:00
Nutomic
1ad9a211c9
Improve contributing instructions (#4504) 2024-03-05 09:14:15 -05:00
Nutomic
157378b4c9
Clear text of deleted/removed comments (#4503) 2024-03-05 08:52:35 -05:00
Dessalines
7f9950fe85
Add alt_text for posts. Fixes #1086 (#4477)
* Add alt_text for posts. Fixes #1086

* Moving alt_text to attachment name.

* Cleaning up mod action line.

* Addressing PR comments

* Addressing PR comments.

* Fixing clones.
2024-03-05 11:34:57 +01:00
Dessalines
52155c74cb
View report history for a post or comment. Fixes #4190 (#4492) 2024-03-05 11:31:40 +01:00
SleeplessOne1917
36ad1868b3
Make SiteAggregates derive Copy and Hash (#4501)
Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
2024-03-05 11:14:12 +01:00
Dessalines
65da4e7dbd
View mod history for a post or comment. Fixes #4162 (#4491) 2024-03-04 11:42:25 -05:00
Nutomic
3c358e5b0b
Mods and admins can comment in locked posts (fixes #4116) (#4488)
* Mods and admins can comment in locked posts (fixes #4116)

* fmt

* fix

* fix test
2024-03-04 09:15:21 -05:00
Dessalines
eb1245bceb
When using saved_only, sort posts / comments by the saved publish time, not the item creation time (#4479)
* Work on saved selection.

* Using single value for join.

* Removing unecessary check.

* Remove saved_only pointless block.
2024-03-04 14:19:51 +01:00
Dessalines
7eec8714d7
When site banning a federated user, also remove their content from our local communities. (#4464)
* When banning a federated user, also remove their content from our local
communities.

- This works by:
  - Before a site ban, find all posts and comments to local communities
  - Send a federated community ban action for each local comm.
  - This also removes their content in the apub receive code.
- Adding back in federated community ban api tests.
- Adding in two more api tests for site bans.
- Fixes #4118

* Add local community ban, and nonlocal person check.

* Ignoring errors.

* Move local check into function.

* Addressing PR comments 2
2024-03-01 13:45:06 -05:00
Nutomic
08b01a377d
Support listing type for person (fixes #4146) (#4487)
* Support listing type for person (fixes #4146)

* add test
2024-03-01 11:53:20 -05:00
Nutomic
c5e54a318a
Store password reset token after email successfully sent (fixes #3757) (#4489) 2024-03-01 11:32:59 -05:00
Nutomic
a7fa075e8c
Make logs less verbose (fixes #3627) (#4490) 2024-03-01 11:32:13 -05:00
Nutomic
c895e57086
Remove unneeded error "last successful id is higher than latest id" (fixes #4363) (#4486) 2024-03-01 11:31:37 -05:00
Dessalines
e1b26897be
Add nlnet grant line in readme. (#4484) 2024-03-01 11:22:53 -05:00
Dessalines
87b577467b
Adding ability to hide posts. (#4480)
* Adding ability to hide posts.

- Adds an post/hide API route.
- Adds a `show_hidden` (default false) to `GetPosts`.
- Adds a `hidden` field to `PostView`.
- Removes the single `post_id` from MarkPostAsRead.
- Fixes #1403

* Add a check to make sure hidden field is true.

* Fixing test.

* Add back semicolon
2024-02-29 10:42:34 -05:00
Nutomic
6d815db375
Require verified email to reset password (#4482) 2024-02-29 09:12:45 -05:00
Nutomic
328a48c9f5
Remove error-type feature from lemmy-utils (#4474)
* Remove error-type feature from lemmy-utils

* fixes

---------

Co-authored-by: SleeplessOne1917 <28871516+SleeplessOne1917@users.noreply.github.com>
2024-02-27 11:13:52 -05:00
Nutomic
ab4deaa49a
Add api test for synchronizing featured posts (ref #4475) (#4476)
* Correctly synchronize collection of community featured posts (fixes #3867)

* Add api test for synchronizing featured posts (ref #4475)

* prettier
2024-02-27 09:11:41 -05:00
Dessalines
e01ea32928
Fix doctype check issue for metadata fetching. Fixes #4468 (#4472)
* Fix doctype check issue for metadata fetching. Fixes #4468

* Change warn to info.
2024-02-26 10:24:09 -05:00
Dessalines
f3d48f2c2c
Adding some recommended fixes from nightly clippy. (#4473) 2024-02-26 09:47:10 -05:00
Nutomic
7316dd281a
Correctly synchronize collection of community featured posts (fixes #3867) (#4475) 2024-02-26 09:45:23 -05:00
Richard Schwab
80bfd23b4d
Ensure rustfmt is installed in cargo fmt CI job (#4466) 2024-02-26 11:22:22 +01:00
SleeplessOne1917
f42420809b
Expose LemmyErrorType in lemmy_api_common (#4439)
* Expose LemmyErrorType in lemmy_api_common

* Make conditional compilation gates for utils

* Make it so api_common doesn't pull in unnecessary deps

* Make error type non exhaustive

* Fix formatting

* Format toml

* Add some convenience derives to LemmyError

* Simplify features

* Fix CI compile error

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
2024-02-24 19:54:27 -05:00
dullbananas
f56b84615c
Move DbUrl trait impls to newtypes.rs (#4463)
* Move DbUrl trait impls to newtypes.rs

* Update utils.rs
2024-02-19 12:41:28 -05:00
dullbananas
d79502dff3
Escape backslashes in fuzzy_search (#4462)
* Escape backslashes in fuzzy_search

* Update utils.rs
2024-02-18 09:12:56 -05:00
dullbananas
ae62ef2b7e
Ignore expired bans in CommentReportView::read, just like in CommentReportQuery::list (#4457)
* Update comment_report_view.rs

* Update comment_report_view.rs

* Update comment_report_view.rs

* Update comment_report_view.rs
2024-02-18 09:12:12 -05:00
SleeplessOne1917
39345466da
Make it so the signed in user can see if they're banned from a community (#4458)
* Make it so the signed in user can see if they're banned from a community

* Use more appropriate field name

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
2024-02-18 09:09:46 -05:00
Dessalines
5d551e6da5
Adding an instance-level default_sort_type (#4454)
* Adding an instance-level default_sort_type

- Fixes #3796

* Fixing comment.

* Put user sort before site sort.
2024-02-16 09:36:46 -05:00
Nutomic
ffcf415cac
Dont log db url on connection error (fixes #4453) (#4456)
* Dont log db url on connection error (fixes #4453)

* remove format
2024-02-16 08:50:06 -05:00
Nutomic
86b44c2a4d
Add site.content_warning, local_site.default_post_listing_mode (#4393)
* Include local_site.content_warning setting for showing nsfw by default

* Add community setting `only_followers_can_vote`

* clippy

* add auto_expand_images site setting

* cleanup

* add missing api params

* postquery/communityquery changes

* clippy

* change error

* replace auto_expand_images with default_site_post_listing_mode

* change post/community query params

* get rid of only_followers_can_vote

* machete

* fix

* clippy

* revert remaining vote changes

* remove dead code

* remove unused var

* fmt
2024-02-16 07:24:35 -05:00
Dessalines
a3bf2f1cf1
Auto resolve reports on removing a comment or post. Fixes #4390 (#4402)
* Automatically resolve report when post/comment is removed (#3850)

* Automatically resolve report when post/comment is removed

* also handle apub removes

* Removing auto-resolve report triggers.

* Dont allow creating reports for deleted / removed items.

* Running pgformat.

* Fixing test.

* Addressing PR comments.

* Forgot comment report.

---------

Co-authored-by: Nutomic <me@nutomic.com>
2024-02-15 08:52:04 -05:00
Dessalines
890565ca14
Deleting denied local_users older than a week. Fixes #4434 (#4448)
* Deleting denied local_users older than a week. Fixes #4434

* Addressing PR comments.

* Upping rust to 1.76

* Delete the person rows also.
2024-02-15 07:50:53 -05:00
Dessalines
3f7cc07b02
Adding ability to specify a custom post thumbnail. (#4425)
* Adding ability to specify a custom post thumbnail.

- Context: #4204

* Fixing ts-rs serialization.

* Fixing conversion, adding checks.

* Proxying custom_thumbnail. Fixed logic for update.

* Only generate metadata thumbnail is theres no custom thumbnail.
2024-02-15 10:42:23 +01:00
Dessalines
33989f5518
Blocking an instance also hides private messages from their users. (#4447)
* Blocking an instance also hides private messages from their users.

- Fixes #4444

* Separating private message tests.
2024-02-14 10:49:55 +01:00
Lcchy
8a6a86c1bb
Add support for RSS media enclosures in feeds (#4442)
* Add support for RSS media enclosures in feeds

* Use post.url_content_type
2024-02-13 10:46:46 +01:00
dullbananas
677d54ae57
Allow better query plans (#4424)
* Update utils.rs

* Create bind_if_some.rs

* limit connection age

* Delete crates/db_schema/src/utils/bind_if_some.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs

* Update utils.rs
2024-02-12 16:44:29 +01:00
SleeplessOne1917
300869d397
Make request models derive PartialEq, Eq, and Hash (#4443)
* Make request models derive PartialEq, Eq, and Hash

* Fix clippy error

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
2024-02-11 00:32:14 -05:00
Dessalines
609de3e9e2
Remove front end size. Fixes #4437 (#4440) 2024-02-09 10:42:12 +01:00
Elara
3c5b1ac6dd
Use the Accept-Language header to set new users' language (#4435)
* Use the Accept-Language header to set new users' language

* Implement clippy suggestions

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-02-08 10:53:03 +01:00
dullbananas
f631f43024
Run analyze in db_perf (#4420) 2024-02-07 22:37:44 -05:00
Dessalines
9367cbdb00
Upgrading from postgres 15 -> 16-alpine. (#4426)
- Includes an upgrade script.
- Fixes #4406
2024-02-07 11:21:02 +01:00
Dessalines
3647a46e86
Remove ansible tagging. (#4417)
- See https://github.com/LemmyNet/lemmy-ansible/issues/215
2024-01-31 10:46:13 +01:00
Elara
328d48ef7e
Remove invalid XML characters from RSS feeds (#4416)
* Remove all characters that are disallowed by XML

* Combine contiguous unicode ranges into one range
2024-01-30 15:55:45 +01:00
Nutomic
a09027c4c0
Silence warnings from ts-rs (#4415)
* Silence warnings from ts-rs

https://github.com/Aleph-Alpha/ts-rs/issues/108

* fmt
2024-01-29 09:22:53 -05:00
Elara
0e9924a2b3
Add media:content thumbnail to RSS feed (#4413)
* Add media:content thumbnail to RSS feed

* Run formatter

* Add media namespace definition

* Add comment linking to media-rss documentation
2024-01-29 08:56:35 -05:00
Dessalines
eb0dc2fda4
Moving from yarn to pnpm. (#4414)
* Moving from yarn to pnpm.

* Prettier check.
2024-01-29 11:38:39 +01:00
Dessalines
9a2fb8e7c2
Fix image_mode for docker lemmy.hjson (#4403) 2024-01-26 10:51:59 +01:00
dullbananas
f481a607d0
Show server output if federation tests fail in CI (#4389)
* Show server output if federation tests fail in CI

* dummy failure

* Revert dummy failure

* Update private_message.rs

* Fix errors

* Update private_message.rs

* Update private_message.rs

* Update .woodpecker.yml

* correct exit code

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update .woodpecker.yml

* Update prepare-drone-federation-test.sh

* Update .woodpecker.yml

* Update prepare-drone-federation-test.sh

* Update .woodpecker.yml

* Update prepare-drone-federation-test.sh

* Update private_message.rs

* Update lib.rs

* Update lib.rs

* Update session_middleware.rs

* Update session_middleware.rs

* Update .woodpecker.yml
2024-01-26 10:38:15 +01:00
dullbananas
ade1cb1495
Remove outdated comment from .woodpecker.yml (#4404) 2024-01-26 10:33:03 +01:00
Nutomic
0f414a95d5
Local only community (#4350)
* Add support for local only community (fixes #1576)

* add filters and tests to db views

* dont federate local only community

* test get apub community http

* tests

* more checks

* wip

* api test

* fix tests

* change community.local_only column to visibility enum
(for private communities)

* sql fmt

* rename vars

* clippy

* fix tests

* update lib

* review

* fix js client version

* update client
2024-01-25 11:04:25 -05:00
Dessalines
8cde452fca
Add a comment to clarify the ban expires field. (#4400)
* Add a comment to clarify the ban expires field.

* Add comment about simpler client implementation.

* Better language.
2024-01-25 16:45:42 +01:00
Nutomic
dadf8f28f9
Send purges to federated instances (fixes #4119) (#4398)
* Send purges to federated instances (fixes #4119)

* clippy

* review

* remove unused function

* clippy
2024-01-25 09:24:09 -05:00
Nutomic
e8a52d3a5c
Rewrite images to use local proxy (#4035)
* Add markdown rule to add rel=nofollow for all links

* Add markdown image rule to add local image proxy (fixes #1036)

* comments

* rewrite markdown image links working

* add comment

* perform markdown image processing in api/apub receivers

* clippy

* add db table to validate proxied links

* rewrite link fields for avatar, banner etc

* sql fmt

* proxy links received over federation

* add config option

* undo post.url rewriting, move http route definition

* add tests

* proxy images through pictrs

* testing

* cleanup request.rs file

* more cleanup (fixes #2611)

* include url content type when sending post over apub (fixes #2611)

* store post url content type in db

* should be media_type

* get rid of cache_remote_thumbnails setting, instead automatically
take thumbnail from federation data if available.

* fix tests

* add setting disable_external_link_previews

* federate post url as image depending on mime type

* change setting again

* machete

* invert

* support custom emoji

* clippy

* update defaults

* add image proxy test, fix test

* fix test

* clippy

* revert accidental changes

* address review

* clippy

* Markdown link rule-dess (#4356)

* Extracting opengraph_data to its own type.

* A few additions for markdown-link-rule.

---------

Co-authored-by: Nutomic <me@nutomic.com>

* fix setting

* use enum for image proxy setting

* fix test configs

* add config backwards compat

* clippy

* machete

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-01-25 09:22:11 -05:00
Dessalines
1782aafd10
Upgrading deps. (#4401) 2024-01-25 10:24:07 +01:00
dullbananas
d8f9e8a64c
Post view: move cursor pagination to separate library, add backward pagination to PostQuery (#4320)
* stuff

* stuff

* crates.io

* Update up.sql

* Rerun federation tests

* Update post_view.rs

* Update post_view.rs

* Update up.sql

* Update utils.rs

* Fix precision loss

* Update up.sql

* Update down.sql

* remove unwrap

* Update post_view.rs

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-01-24 10:50:11 -05:00
dullbananas
759f6d8a9a
Better query plan viewing experience (#4285)
* stuff

* stuff including batch_upsert function

* stuff

* do things

* stuff

* different timestamps

* stuff

* Revert changes to comment.rs

* Update comment.rs

* Update comment.rs

* Update post_view.rs

* Update utils.rs

* Update up.sql

* Update up.sql

* Update down.sql

* Update up.sql

* Update main.rs

* use anyhow macro

* replace get(0) with first()

* as_slice

* Update series.rs

* Update db_perf.sh

* Update and rename crates/db_schema/src/utils/series.rs to crates/db_perf/src/series.rs

* Update utils.rs

* Update main.rs

* Update main.rs

* Update .woodpecker.yml

* fmt main.rs

* Update .woodpecker.yml

* Instance::delete at end

* Update main.rs

* Update Cargo.toml

---------

Co-authored-by: Nutomic <me@nutomic.com>
2024-01-24 10:22:33 -05:00
İsmail Karslı
8670403a67
Add local_subscribers field to CommunityAggregates. Fixes #4144 (#4166)
* Add upload timeout to PictrsConfig

* Bad space 🤔

* Update PictrsConfig upload timeout to include units.

* Add local_subscribers field to CommunityAggregates
struct and schema

* sql format

* local_subscribers test

* fix local_subscribers test

* Revert "fix local_subscribers test"

This reverts commit 4bbac5ce4a.

* Revert "local_subscribers test"

This reverts commit 735107e1f7.

* Create trigger for local_subscribers

* Rename variable

* re-trigger ci

* re-trigger ci

* Add local_subscribers count to follow.spec.ts

* Rename local_subscribers to subscribers_local

* Add subscribers_local to community_aggregates

* added subscribers_local to the aggregate tests

* Check if person exists on community_follower trigger

* Delete community follows before deleting person

* Update lemmy-js-client in api_tests

* Refactor local_subscriber migration

* fix format

* Move migration files date to now

* Fix test to wait for aggregates to federate

* re-trigger ci

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-01-24 10:22:05 -05:00
Dessalines
922ec7c2cd
Forgot to add skip_serializing_none to persondetailsres. (#4399) 2024-01-24 09:58:58 -05:00
jim-taylor-business
f43d2eca70
make inner type pub so it can be read and created easily in other crates (#4397)
this is for use in SSR pagination in Lemmy-UI-Leptos
2024-01-24 09:03:31 -05:00
Dessalines
20fd4b5869
Clippy fixes for 1.77.0 nightly (#4395)
* A few 1.77.0-nightly clippy fixes.

* Dead code warnings.

* More fixes.
2024-01-24 10:34:09 +01:00
Dessalines
eb56d9253c
Adding site to GetPersonDetails. Fixes #4373 (#4394)
* Adding site to GetPersonDetails. Fixes #4373

* Removing the conditioned site return.
2024-01-24 10:32:14 +01:00
SleeplessOne1917
4b4b99aa78
Allow community mods to see votes in addition to admins (#4392)
* Allow community mods to see votes in addition to admins

* Use Post instead of PostView

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
2024-01-23 18:47:28 -05:00
Dessalines
2133bcea4e Version 0.19.3 2024-01-22 08:56:08 -05:00
Nutomic
0868910570
Add secondary sort by published date for post view (fixes #4383) (#4384) 2024-01-22 08:52:21 -05:00
Dessalines
e78fe5a34c
Removing group from woodpecker, as its deprecated. (#4387)
* Removing group from woodpecker, as its deprecated.

* Removing meltwater drone cache.
2024-01-22 10:57:07 +01:00
Dessalines
f652513030 Version 0.19.3-rc.1 2024-01-19 11:25:45 -05:00
Dessalines
df11d77a0d Updating translations. 2024-01-19 11:23:47 -05:00
Nutomic
3d6f7ff911
Revert "Dont ignore errors during login (fixes #4319) (#4321)" (#4380)
This reverts commit 4163e0465e.

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-01-19 11:21:43 -05:00
Nutomic
516db012bf
Dont allow caching captcha response (#4381)
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-01-19 10:41:05 -05:00
Nutomic
b58da11fb7
Mark instance as alive after successful activity send (fixes #4039) (#4377)
* Mark instance as alive after successful activity send (fixes #4039)

* clippy

* Instance::update

* domain

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2024-01-19 09:40:12 -05:00
Nutomic
1be7dbde33
Count chars, not bytes for max title length (fixes #4366) (#4367)
* Count chars, not bytes for max title length (fixes #4366)

* fix api test
2024-01-15 09:33:39 -05:00
Nutomic
9240a653c0
Fix bug with Mastodon undo follow activities (#4364) 2024-01-11 18:56:19 -05:00
Dessalines
0d35c247f9 Version 0.19.2 2024-01-10 10:18:09 -05:00
Dessalines
143fdb62b1 Updating translations. 2024-01-10 10:16:36 -05:00
Nutomic
2d16d12cb7
Move publish = false to correct place in lemmy_server (fixes #4359) (#4361) 2024-01-10 09:07:09 -05:00
Dessalines
3b717cfc88
Removing serde_skip from newest_comment_time. Fixes #4351 (#4360) 2024-01-10 10:44:27 +01:00
Dessalines
ea0b856f1e Version 0.19.2-rc.5 2024-01-09 12:45:54 -05:00
Nutomic
92b49dea3a
Increase CI timeout for restore cache (#4358) 2024-01-09 12:37:48 -05:00
dullbananas
1ef90773e0
Refactor post view tests (#4313)
* Refactor post view tests

* Update post_view.rs

* Update post_view.rs

* Update post_view.rs

* Update post_view.rs

* Update post_view.rs

* Update post_view.rs

* Update post_view.rs

* remove unused import
2024-01-09 12:19:25 -05:00
Dessalines
3d9dda4677
Woodpecker rust 1.75 (#4355)
* Upgrading woodpecker rust to 1.75

* Trying to get publish working, try 1.

* Version 0.19.2-rc.3.publish1

* Temporarily disable arm builds.

* Version 0.19.2-rc.3.publish2

* Comment out arm blocks.

* Version 0.19.2-rc.3.publish3

* Moving back publish block to below.

* Upgrade to rasky lemmy-arm v0.2.0

* Version 0.19.2-rc.3.publish4

* Test bump for upgraded woodpecker.

* Revert "Test bump for upgraded woodpecker."

This reverts commit e2a5a99f33.

* Adding link to raskys repo.
2024-01-09 11:48:29 -05:00
Dessalines
2f09ad8e5b Version 0.19.2-rc.4 2024-01-09 09:25:52 -05:00
Nutomic
e3b715002b
Handle federated reports from Mastodon, Kbin (#4323)
* Test Kbin/Mbin federation

* Handle reports from Mastodon/Kbin (fixes #4217)

* prettier

* revert

* add mastodon activity

* ci

* revert

* ci
2024-01-05 11:03:13 -05:00
Nutomic
4ca63c5641
Move apub context to join-lemmy.org (fixes #4234) (#4302)
* Move apub context to join-lemmy.org (fixes #4234)

* Dont store federation context in sent_activity table

* include basic activitypub context

* update lib

* ci
2024-01-05 15:42:46 +01:00
Felix Ableitner
346ff11795 Version 0.19.2-rc.2 2024-01-05 12:33:20 +01:00
Nutomic
747ab89e87
Upgrade docker rust version to 1.75 (#4353) 2024-01-05 12:32:06 +01:00
Felix Ableitner
d3efebfa4e Version 0.19.2-rc.1 2024-01-05 11:33:37 +01:00
Dessalines
1856e7c0ca
Increasing max items for user settings import. (#4352)
- Fixes #4307
2024-01-05 10:43:30 +01:00
phiresky
0e6669f617
no endless loop if queue too recent (#4349) 2024-01-04 13:28:26 -05:00
Dessalines
4a740ee80a Version 0.19.1 2024-01-04 12:32:26 -05:00
Nutomic
7d9b59c467
Reduce default db pool size to 30, remove db timeout (ref #4282) (#4301)
* Reduce default db pool size to 30 (ref #4282)

* remove db timeout
2024-01-04 12:16:51 -05:00
Nutomic
38e64825e6
Add macro assert_length!() for tests (#4348)
* Add macro assert_length!() for tests

* fix

* number
2024-01-04 11:51:55 -05:00
Nutomic
3cad3b2119
Dont overwrite cache-control header in session middleware (#4337) 2024-01-04 11:44:36 -05:00
Nutomic
023c9f4fcd
Fix fetching of community posts (fixes #4283) (#4293)
* Fix fetching of community posts (fixes #4283)

Also use spawn_try_task to fetch community outbox, mods etc to avoid
delay/timeout when fetching new community.

* prettier

* fix test

* fix api test

* prettier

* add delay

* Update run-federation-test.sh

* fix test
2024-01-04 11:42:18 -05:00
Nutomic
952c162dac
Fix CI cache (#4276)
* Minor CI improvements (second attempt)

* test slow check condition

* remove rebuild cache condition for testing

* trigger quick ci check

* mkdir

* ls, remove steps

* exclude cargo home from prettier

* ci

* increase timeout

* more timeout

* even higher just for testing

* disable compression

* also increase restore timeout

* cleanup

* try again

* Update .woodpecker.yml

* Update .woodpecker.yml

* cleanup

* rerun ci

* cleanup
2024-01-04 11:40:41 -05:00
ALEX11BR
a5289dd4cf
Use pretty_assertions for assertions (#4347)
* Use `pretty_assertions` for assertions

* fixed ordering of `use`s

* ci

---------

Co-authored-by: Felix Ableitner <me@nutomic.com>
2024-01-04 04:47:18 -05:00
Dessalines
009a45dffb
Adding /post/like/list and /comment/like/list for admins. (#4332)
- Allows admins to view likes, sorted by downvotes first,
  for a given comment or post.
- Fixes #4088
2024-01-03 13:39:21 -05:00
phiresky
024ab7d530
Fix federate loop (#4330)
* make activity channel infallible

* clippy

* federate: make cancellabletask loop itself
2024-01-03 13:30:06 -05:00
Nutomic
53147596b4
Drop unique constraint for site.name (fixes #4329) (#4342)
* Drop unique constraint for site.name (fixes #4329)

* working down migration
2024-01-03 12:47:39 -05:00
Nutomic
4163e0465e
Dont ignore errors during login (fixes #4319) (#4321)
* Dont ignore errors during login (fixes #4319)

* fix test

* fmt
2024-01-03 10:34:03 -05:00
Nutomic
abe8b18ea8
Also send reports to user's home instance (fixes #4286) (#4305) 2024-01-03 10:31:51 -05:00
Sander Saarend
35db0dc8e7
Reduce initial federation retry delay (#4346) 2024-01-03 10:31:23 +01:00
Dessalines
5f603985c0
Allow reports to be resolved, even if the community is deleted / removed. (#4345)
Fixes #4344
2024-01-03 10:31:03 +01:00
Ikko Eltociear Ashimine
2f3a7abe6b
Update READMEs (#4325)
* Update README.ja.md

fix image path

* Update README.es.md

* Update README.ru.md

* Update README.zh.hans.md

* Update README.zh.hant.md

* prettier

---------

Co-authored-by: Felix Ableitner <me@nutomic.com>
2023-12-26 05:23:03 -05:00
Andrew Voynov
f223eb94d5
Fixed matrix_user_id regex (#4312)
* fix(matrix_user_id): fixed regex

* test(matrix_user_id): added test with underscore
2023-12-21 12:13:24 +01:00
SleeplessOne1917
4ef00e068f
Prevent crates that shouldn't be published from being published (#4309)
* Prevent crates that shouldn't be published from being published

* Make dependent crates publishable

* Make dependent crates publishable

* fix toml formatting

* Removing publish=true

* Removing versioned deps.

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
Co-authored-by: Dessalines <tyhou13@gmx.com>
2023-12-20 09:11:58 -05:00
Nutomic
2b9d89057d
Reenable crates.io releases (fixes #4291) (#4294)
* Reenable crates.io releases (fixes #4291)

* disable docker release for now

* fixes

* fix indent

* move to top

* install libssl

* -y

* apt update

* debian version

* install cargo, git

* use cargo install

* move to orig position
2023-12-20 08:18:54 -05:00
Felix Ableitner
08b7e0f03d Version 0.19.1-rc.2 2023-12-20 10:32:51 +01:00
Dessalines
7f780376bc
Add end software patents bagde (#4296)
* Adding End software patents badge.

Context: https://github.com/LemmyNet/joinlemmy-site/issues/283

* Smaller badge.

* Smaller badge 2.

* Smaller badge 3.
2023-12-19 19:59:48 -05:00
Nutomic
a507a39336
Add missing test cleanup (#4289)
* Add missing test cleanup

* cleanup
2023-12-19 05:26:00 -05:00
Dessalines
2899ba0131
Fixing broken post_read logic. Fixes #4290 (#4297) 2023-12-19 10:46:41 +01:00
Dessalines
8583a85607 Version 0.19.1-rc.1 2023-12-18 14:20:42 -05:00
phiresky
6790b54d4d
make activity channel infallible (#4295) 2023-12-18 13:17:10 -05:00
dullbananas
dcb89f52d6
Don't update comment_aggregates if updating path fails (#4281) 2023-12-18 10:31:39 +01:00
Dessalines
bc32b408b5
Fixing private message reports. (#4279) 2023-12-18 10:25:05 +01:00
Dessalines
aab3ca4eb0 Version 0.19.0 2023-12-15 06:53:26 -05:00
dullbananas
a7ba5c9dd3
Sort by post id in post view (#4270)
* Sort by post id in post view

* Remove tie_breaker

---------

Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
Co-authored-by: Nutomic <me@nutomic.com>
2023-12-15 06:42:28 -05:00
Nutomic
7a182c982b
Debug random test failure (#4275) 2023-12-15 12:28:08 +01:00
Dessalines
719b76a6e7
Create comment in transaction. Fixes #3741 (#4265)
* Create comment in transaction. Fixes #3741

* Removing if let on comment create.
2023-12-15 11:36:58 +01:00
Dessalines
246e38a45b
Making login case-insensitive. Fixes #4272 (#4273)
* Making login case-insensitive. Fixes #4272

* Removing redundant lowercase.
2023-12-15 11:34:17 +01:00
Dessalines
e4b97ad8a9
Revert "Minor CI improvements (#4266)" (#4271)
This reverts commit af4d008ce4.
2023-12-14 18:36:33 -05:00
Dessalines
70530a8ad2
Optimizing sql format check. (#4268)
* Optimizing sql format check.

* Fixing format testing.

* Fixing format testing 2.
2023-12-14 11:35:52 -05:00
Nutomic
af4d008ce4
Minor CI improvements (#4266)
* Speed up SQL formatting in CI with parallel processing

* mess up formatting

* check permissions

* make repo world writable

* need git

* use debian image

* apt update

* perl image for faster install

* fix format

* run restore cache in parallel to format

* add drone-cache exit code

* revert sql format changes
2023-12-14 11:18:15 -05:00
Dessalines
ac209d58b4
Adding a retry on a few fetches. (#4267) 2023-12-14 16:26:42 +01:00
Dessalines
59eef85bb7 Version 0.19.0-rc.16 2023-12-14 07:59:27 -05:00
Dessalines
442ec0b9f8
Upgrading lemmy-js-client to 0.19.0 (#4260) 2023-12-14 07:26:01 -05:00
Nutomic
4a55d4f871
Quick fix for Peertube federation (fixes #4261) (#4264)
* Quick fix for Peertube federation (fixes #4261)

* prettier

* apub assets should be included in slow check paths
2023-12-14 07:25:47 -05:00
phiresky
7353be5b68
federate-less-noisy (#4263) 2023-12-14 07:25:04 -05:00
dullbananas
32afc32bc0
Correctly combine sorts in post view cursor-based pagination (#4247)
* Update post_view.rs

* Update post_view.rs

* Update Cargo.toml

* Update post_view.rs

* fix

* Update post_view.rs

---------

Co-authored-by: SleeplessOne1917 <abias1122@gmail.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2023-12-14 07:10:01 -05:00
Nutomic
01aa17f38e
Reenable API tests that were accidentally skipped (#4259)
* Reenable API tests that were accidentally skipped

* log get comment parent id failure
2023-12-13 09:29:10 -05:00
Dessalines
0b2df3980f Version 0.19.0-rc.15 2023-12-13 09:24:24 -05:00
Nutomic
6626d35b98
Second attempt to make command line options more consistent (#4249)
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2023-12-13 09:14:59 -05:00
Nutomic
60ffa2a599
Upgrade pictrs to 0.5.0-rc.2, remove cargo unstable flags (#4258) 2023-12-13 07:05:13 -05:00
dullbananas
5e589004a5
Add test for fixed deleted post filter (#4256)
* Add test for fixed deleted post filter

* fmt

* Fmt

* Update post_view.rs
2023-12-13 11:09:10 +01:00
dullbananas
a0ef56b9b7
Remove moderator_view field from PostQuery (#4255) 2023-12-13 10:52:28 +01:00
Dessalines
93d123b46e
Fixing metadata endpoint. (#4257) 2023-12-13 10:50:51 +01:00
Dessalines
5ad881f3df Version 0.19.0-rc.14 2023-12-12 14:58:17 -05:00
dullbananas
3e2393993e
Update mod.rs (#4240)
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
2023-12-12 13:06:17 -05:00
SleeplessOne1917
8d52c7e7c7
See if different SQL query fixes performance regression (#4246)
* See if different SQL query fixes performance regression

* Fix formatting

* Hopefully fix failing federation test

* Hopefully solve federation test problem

* Use already-existing coalesce function

* Update person_view.rs

---------

Co-authored-by: SleeplessOne1917 <insomnia-void@protonmail.com>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
Co-authored-by: Nutomic <me@nutomic.com>
Co-authored-by: Dessalines <tyhou13@gmx.com>
2023-12-12 12:09:52 -05:00
Nutomic
30d58865b8
Speed up GET /api/v3/site endpoint (#4245)
* Make db queries for GET /api/v3/site in parallel (ref #4244)

* Cache site response

* machete

* Use try_join_with_pool macro

* machete

* taplo

* ttl 1s
2023-12-12 11:56:39 -05:00
Nutomic
e0e74e50ae
Dont use test.png image for api tests (#4254) 2023-12-12 10:58:22 -05:00
Nutomic
c7ee53026f
Upgrade dependencies (#4248)
* Upgrade dependencies

* clippy
2023-12-12 08:56:33 -05:00
Nutomic
b2ae69f96c
Set explicit limit for getPosts/getComments in tests (#4250)
* Set explicit limit for getPosts/getComments in tests

* pretier
2023-12-12 08:54:54 -05:00
Nutomic
95130370f0
Add CI check for ignored files (#4252)
* Add CI check for ignored files

* remove ignored files
2023-12-12 08:54:06 -05:00
Dessalines
f764996745
Show federated posts on user profile. Fixes #4228 (#4229)
* Show federated posts on user profile. Fixes #4228

* Make sure posts are hidden if no listing_type is given.
2023-12-11 14:36:12 -05:00
Nutomic
e317947b75
Dont allow blocking local instance (fixes #4241) (#4242)
* Dont allow blocking local instance (fixes #4241)

* use localuserview
2023-12-11 14:35:53 -05:00
Nutomic
cef3f220a2
Make command line options consistent (#4243) 2023-12-11 09:39:18 -05:00
Benjamin Barbeau
60849355db
Add voters to active users (#4235)
* Add voters to active users

* Edit formatting

* Edit formatting

* Edit formatting in down.sql

* Fix person table inner joins

* Remove post read from calculations
2023-12-11 09:38:17 -05:00
Kroese
2d4037ba61
Extend sitemap span (#4231)
* Extend sitemap span

* Keep cargo_fmt happy

* Add FETCH_LIMIT_SITEMAP

* Use FETCH_LIMIT_SITEMAP

* Keep cargo_fmt happy

* Update utils.rs

* Use SITEMAP_DAYS

* Keep cargo_fmt happy

* Sitemap

* Keep cargo_fmt happy

* Sitemap

* Sitemap

* Increase to 31 days
2023-12-11 11:24:51 +01:00
Dessalines
c85e680aba Version 0.19.0-rc.13 2023-12-06 18:20:52 -05:00
Kroese
203ca9d617
Print version to log (#4226)
* Print version to log

* Keep cargofmt happy

* Keep cargo_fmt happy

* Keep Clippy happy
2023-12-05 11:35:59 -05:00
Nutomic
a790a24c4d
Revert debug auth (#4232)
* Revert "Some changes to help debug auth problems on lemmy.ml (#4220)"

This reverts commit 16ac893e15.

* Rename auth cookie back to jwt
2023-12-05 11:22:08 -05:00
Nutomic
de85e51fac
Dont set duplicate context for activities (#4233) 2023-12-05 11:19:12 -05:00
Nutomic
a5386187e3
Enable missing code for prometheus actix-web stats (#4230)
* Enable missing code for prometheus actix-web stats

* enable middleware conditionally
2023-12-04 09:53:53 -05:00
Dessalines
3f79eacb53 Version 0.19.0-rc.12 2023-12-01 09:29:44 -05:00
Nutomic
16ac893e15
Some changes to help debug auth problems on lemmy.ml (#4220)
* Some changes to help debug auth problems on lemmy.ml

* fix

* clippy
2023-12-01 09:18:29 -05:00
Nutomic
d7376d9541
Fix cors_origin wildcard (fixes #4214) (#4221) 2023-12-01 09:16:55 -05:00
Nutomic
809fc05cb3
Only allow distinguishing own comments (fixes #4216) (#4222) 2023-12-01 09:16:22 -05:00
Dessalines
70003407a7 Version 0.19.0-rc.11 2023-11-30 05:02:18 -05:00
Dessalines
170b3ec45f
Removing cookie secure check. (#4213) 2023-11-30 04:44:18 -05:00
Dessalines
7ef6476520 Version 0.19.0-rc.10 2023-11-29 10:04:42 -05:00
Dessalines
e84f8f55a2
Remove httpOnly requirement. (#4212) 2023-11-29 09:58:35 -05:00
Dessalines
86990d5138
Registrations and Reports should sort by New when viewing unresolved / unread. (#4207)
- Fixes #4206
2023-11-29 11:08:23 +01:00
Dessalines
23b266ec12
Upgrade deps, fix issue with wrong diesel-async in Cargo.lock (#4211)
Co-authored-by: Nutomic <me@nutomic.com>
2023-11-29 11:06:34 +01:00
Dessalines
a986db1a00
Fixing rust-analyzer suggestions on rss feeds. (#4210)
* Fixing rust-analyzer suggestions on rss feeds.

* Get rid of other pointless builders.

* More cleanup.
2023-11-29 11:00:06 +01:00
Bhoomtawath Plinsut
6fa3b59d25
Fix:#4197: validate post title must be shorter than 200 letters (#4198)
* validate post title must be shorter than 200 letters

* use range contains
2023-11-27 10:46:03 +01:00
dullbananas
d95df3a46f
Update diesel-async (#4203)
* Update diesel-async

* Fix
2023-11-27 10:31:19 +01:00
Dessalines
7972dd0fcf Version 0.19.0-rc.8 2023-11-26 22:26:57 -05:00
Kroese
fc07ba2d3b
Fix entrypoint in Dockerfile (#4202)
* Fix entrypoint

* Delete docker/builders/lemmy-builder-arm64/docker-build.sh

* Delete docker/builders directory

* Remove exception for builder

* Remove publish_builder_arm64
2023-11-26 16:50:31 -05:00
Enzo Nocera
8a05c8f8be
fix: Cross-compilation to ARM64 (#4142)
* feat(docker/docs): explain how building lemmy works

Signed-off-by: Enzo NOCERA <enzo@nocera.eu>

* feat: add arm build

* review: rename script & fix typo

* feat(ci): allow cross platform compilation

* feat(ci): prettier

* fix(docker): fix base image name

* fix: add dockerfile in CI path

Signed-off-by: Enzo Nocera <enzo@nocera.eu>

* fix(docker): fix runner name

* fix(docker): fix builder base image

* fix(docker): fix builder base image platform

* fix(docker): avoid using the wrapper adduser/addgroup

* feat: avoid adding the whole docker directory in the build context

---------

Signed-off-by: Enzo NOCERA <enzo@nocera.eu>
Signed-off-by: Enzo Nocera <enzo@nocera.eu>
Co-authored-by: Dessalines <dessalines@users.noreply.github.com>
Co-authored-by: Nutomic <me@nutomic.com>
2023-11-24 12:52:19 -05:00
Nutomic
934fe7d1cb
Remove pictrs config section old_db (#4199)
* Remove pictrs config section `old_db` (ref #4194)

* ci

* remove cargo config comment

* ci

* Revert "remove cargo config comment"

This reverts commit 9389e93649.

* rename .cargo to .cargo_home
2023-11-24 11:26:21 -05:00
Nutomic
7d489f1e3f
Upgrade pictrs to 0.5.0-beta.2 (#4194)
* Upgrade pictrs to 0.5.0-beta.2

* Update src/main.rs

Co-authored-by: asonix <asonix@asonix.dog>

* add comment

---------

Co-authored-by: asonix <asonix@asonix.dog>
2023-11-24 07:44:17 -05:00
Dessalines
2b5a31b411
Upgrading deps. (#4196)
* Upgrading deps.

* Try to use native node-fetch function.

* Fixing image upload tests.
2023-11-24 10:29:41 +01:00
535 changed files with 29502 additions and 16112 deletions

View file

@ -1,2 +0,0 @@
[build]
rustflags = ["--cfg", "tokio_unstable"]

4
.github/CODEOWNERS vendored
View file

@ -1,3 +1,3 @@
* @Nutomic @dessalines @phiresky
* @Nutomic @dessalines @phiresky @dullbananas @SleeplessOne1917
crates/apub/ @Nutomic
migrations/ @dessalines @phiresky
migrations/ @dessalines @phiresky @dullbananas

View file

@ -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

View file

@ -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:

1
.gitignore vendored
View file

@ -20,7 +20,6 @@ query_testing/**/reports/*.json
api_tests/node_modules
api_tests/.yalc
api_tests/yalc.lock
api_tests/test.png
api_tests/pict-rs
# pictrs data

View file

@ -3,3 +3,5 @@ edition = "2021"
imports_layout = "HorizontalVertical"
imports_granularity = "Crate"
group_imports = "One"
wrap_comments = true
comment_width = 100

View file

@ -2,32 +2,34 @@
# See https://github.com/woodpecker-ci/woodpecker/issues/1677
variables:
- &rust_image "rust:1.74.0"
- &rust_image "rust:1.81"
- &rust_nightly_image "rustlang/rust:nightly"
- &install_pnpm "corepack enable pnpm"
- &install_binstall "wget -O- https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz | tar -xvz -C /usr/local/cargo/bin"
- install_diesel_cli: &install_diesel_cli
- apt-get update && apt-get install -y postgresql-client
- cargo install diesel_cli --no-default-features --features postgres
- export PATH="$CARGO_HOME/bin:$PATH"
- &slow_check_paths
- path:
- event: pull_request
path:
include: [
# rust source code
- "**/*.rs"
- "**/Cargo.toml"
- "Cargo.lock"
"crates/**",
"src/**",
"**/Cargo.toml",
"Cargo.lock",
# database migrations
- "migrations/**"
"migrations/**",
# typescript tests
- "api_tests/**"
"api_tests/**",
# config files and scripts used by ci
- ".woodpecker.yml"
- ".rustfmt.toml"
- "scripts/update_config_defaults.sh"
- "diesel.toml"
- ".gitmodules"
# Broken for cron jobs currently, see
# https://github.com/woodpecker-ci/woodpecker/issues/1716
# clone:
# git:
# image: woodpeckerci/plugin-git
# settings:
# recursive: true
# submodule_update_remote: true
".woodpecker.yml",
".rustfmt.toml",
"scripts/update_config_defaults.sh",
"diesel.toml",
".gitmodules",
]
steps:
prepare_repo:
@ -36,74 +38,64 @@ steps:
- apk add git
- git submodule init
- git submodule update
when:
- event: [pull_request, tag]
prettier_check:
group: format
image: tmknom/prettier:3.0.0
commands:
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations'
- prettier -c . '!**/volumes' '!**/dist' '!target' '!**/translations' '!api_tests/pnpm-lock.yaml'
when:
- event: pull_request
toml_fmt:
group: format
image: tamasfe/taplo:0.8.1
commands:
- taplo format --check
when:
- event: pull_request
sql_fmt:
group: format
image: backplane/pgformatter:latest
image: backplane/pgformatter
commands:
- ./scripts/sql_format_check.sh
when:
- event: pull_request
cargo_fmt:
group: format
image: rustlang/rust:nightly
image: *rust_nightly_image
environment:
# store cargo data in repo folder so that it gets cached between steps
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
commands:
# need make existing toolchain available
- rustup component add rustfmt
- cargo +nightly fmt -- --check
when:
- event: pull_request
cargo_machete:
group: format
image: rustlang/rust:nightly
image: *rust_nightly_image
commands:
- wget https://github.com/cargo-bins/cargo-binstall/releases/latest/download/cargo-binstall-x86_64-unknown-linux-musl.tgz
- tar -xvf cargo-binstall-x86_64-unknown-linux-musl.tgz
- cp cargo-binstall /usr/local/cargo/bin
- *install_binstall
- cargo binstall -y cargo-machete
- cargo machete
when:
- event: pull_request
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
mount:
- ".cargo"
- "target"
- "api_tests/node_modules"
secrets:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
when: *slow_check_paths
ignored_files:
image: alpine:3
commands:
- apk add git
- IGNORED=$(git ls-files --cached -i --exclude-standard)
- if [[ "$IGNORED" ]]; then echo "Ignored files present:\n$IGNORED\n"; exit 1; fi
when:
- event: pull_request
# make sure api builds with default features (used by other crates relying on lemmy api)
check_api_common_default_features:
image: *rust_image
environment:
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
commands:
- cargo check --package lemmy_api_common
when: *slow_check_paths
@ -111,7 +103,7 @@ steps:
lemmy_api_common_doesnt_depend_on_diesel:
image: *rust_image
environment:
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
commands:
- "! cargo tree -p lemmy_api_common --no-default-features -i diesel"
when: *slow_check_paths
@ -119,7 +111,7 @@ steps:
lemmy_api_common_works_with_wasm:
image: *rust_image
environment:
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
commands:
- "rustup target add wasm32-unknown-unknown"
- "cargo check --target wasm32-unknown-unknown -p lemmy_api_common"
@ -128,7 +120,7 @@ steps:
check_defaults_hjson_updated:
image: *rust_image
environment:
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
commands:
- export LEMMY_CONFIG_LOCATION=./config/config.hjson
- ./scripts/update_config_defaults.sh config/defaults_current.hjson
@ -136,124 +128,160 @@ steps:
when: *slow_check_paths
check_diesel_schema:
image: willsquire/diesel-cli
image: *rust_image
environment:
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
commands:
- <<: *install_diesel_cli
- diesel migration run
- diesel print-schema --config-file=diesel.toml > tmp.schema
- diff tmp.schema crates/db_schema/src/schema.rs
when: *slow_check_paths
check_diesel_migration_revertable:
image: willsquire/diesel-cli
check_db_perf_tool:
image: *rust_image
environment:
CARGO_HOME: .cargo
DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
RUST_BACKTRACE: "1"
CARGO_HOME: .cargo_home
commands:
- diesel migration run
- diesel migration redo
# same as scripts/db_perf.sh but without creating a new database server
- export LEMMY_CONFIG_LOCATION=config/config.hjson
- cargo run --package lemmy_db_perf -- --posts 10 --read-post-pages 1
when: *slow_check_paths
cargo_clippy:
image: *rust_image
environment:
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
commands:
# when adding new clippy lints, make sure to also add them in scripts/lint.sh
- rustup component add clippy
- cargo clippy --workspace --tests --all-targets --features console -- -D warnings
- cargo clippy --workspace --tests --all-targets -- -D warnings
when: *slow_check_paths
cargo_build:
image: *rust_image
environment:
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
commands:
- cargo build
- mv target/debug/lemmy_server target/lemmy_server
when: *slow_check_paths
cargo_test:
group: tests
image: *rust_image
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
RUST_BACKTRACE: "1"
CARGO_HOME: .cargo
CARGO_HOME: .cargo_home
LEMMY_TEST_FAST_FEDERATION: "1"
commands:
- export LEMMY_CONFIG_LOCATION=../../config/config.hjson
- cargo test --workspace --no-fail-fast
when: *slow_check_paths
check_diesel_migration:
# TODO: use willsquire/diesel-cli image when shared libraries become optional in lemmy_server
image: *rust_image
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
RUST_BACKTRACE: "1"
CARGO_HOME: .cargo_home
DATABASE_URL: postgres://lemmy:password@database:5432/lemmy
PGUSER: lemmy
PGPASSWORD: password
PGHOST: database
PGDATABASE: lemmy
commands:
# Install diesel_cli
- <<: *install_diesel_cli
# Run all migrations
- diesel migration run
- psql -c "DROP SCHEMA IF EXISTS r CASCADE;"
- pg_dump --no-owner --no-privileges --no-table-access-method --schema-only --no-sync -f before.sqldump
# Make sure that the newest migration is revertable without the `r` schema
- diesel migration redo
# Run schema setup twice, which fails on the 2nd time if `DROP SCHEMA IF EXISTS r CASCADE` drops the wrong things
- alias lemmy_schema_setup="target/lemmy_server --disable-scheduled-tasks --disable-http-server --disable-activity-sending"
- lemmy_schema_setup
- lemmy_schema_setup
# Make sure that the newest migration is revertable with the `r` schema
- diesel migration redo
# Check for changes in the schema, which would be caused by an incorrect migration
- psql -c "DROP SCHEMA IF EXISTS r CASCADE;"
- pg_dump --no-owner --no-privileges --no-table-access-method --schema-only --no-sync -f after.sqldump
- diff before.sqldump after.sqldump
when: *slow_check_paths
run_federation_tests:
group: tests
image: node:20-bookworm-slim
environment:
LEMMY_DATABASE_URL: postgres://lemmy:password@database:5432
DO_WRITE_HOSTS_FILE: "1"
commands:
- apt update && apt install -y bash curl postgresql-client
- *install_pnpm
- apt-get update && apt-get install -y bash curl postgresql-client
- bash api_tests/prepare-drone-federation-test.sh
- cd api_tests/
- yarn
- yarn api-test
- pnpm i
- pnpm api-test
when: *slow_check_paths
rebuild-cache:
image: meltwater/drone-cache:v1
pull: true
settings:
rebuild: true
endpoint:
from_secret: MINIO_ENDPOINT
access-key:
from_secret: MINIO_WRITE_USER
secret-key:
from_secret: MINIO_WRITE_PASSWORD
bucket:
from_secret: MINIO_BUCKET
cache_key: "rust-cache"
region: us-east-1
path-style: true
mount:
- ".cargo"
- "target"
- "api_tests/node_modules"
secrets:
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
federation_tests_server_output:
image: alpine:3
commands:
# `|| true` prevents this step from appearing to fail if the server output files don't exist
- cat target/log/lemmy_*.out || true
- "# If you can't see all output, then use the download button"
when:
- event: push
branch: main
- event: pull_request
status: failure
publish_release_docker:
image: woodpeckerci/plugin-docker-buildx
secrets: [docker_username, docker_password]
settings:
repo: dessalines/lemmy
dockerfile: docker/Dockerfile
# TODO fix arm build: see: https://woodpecker.join-lemmy.org/repos/129/pipeline/2888/20
# platforms: linux/amd64,linux/arm64
platforms: linux/amd64
username:
from_secret: docker_username
password:
from_secret: docker_password
platforms: linux/amd64, linux/arm64
build_args:
- RUST_RELEASE_MODE=release
tag: ${CI_COMMIT_TAG}
when:
event: tag
- event: tag
nightly_build:
image: woodpeckerci/plugin-docker-buildx
secrets: [docker_username, docker_password]
settings:
repo: dessalines/lemmy
dockerfile: docker/Dockerfile
username:
from_secret: docker_username
password:
from_secret: docker_password
platforms: linux/amd64,linux/arm64
build_args:
- RUST_RELEASE_MODE=release
tag: dev
when:
event: cron
- event: cron
# using https://github.com/pksunkara/cargo-workspaces
publish_to_crates_io:
image: *rust_image
commands:
- *install_binstall
# Install cargo-workspaces
- cargo binstall -y cargo-workspaces
- cp -r migrations crates/db_schema/
- cargo workspaces publish --token "$CARGO_API_TOKEN" --from-git --allow-dirty --no-verify --allow-branch "${CI_COMMIT_TAG}" --yes custom "${CI_COMMIT_TAG}"
secrets: [cargo_api_token]
when:
- event: tag
notify_on_failure:
image: alpine:3
@ -261,7 +289,8 @@ steps:
- apk add curl
- "curl -d'Lemmy CI build failed: ${CI_PIPELINE_URL}' ntfy.sh/lemmy_drone_ci"
when:
status: [failure]
- event: [pull_request, tag]
status: failure
notify_on_tag_deploy:
image: alpine:3
@ -269,11 +298,12 @@ steps:
- apk add curl
- "curl -d'lemmy:${CI_COMMIT_TAG} deployed' ntfy.sh/lemmy_drone_ci"
when:
event: tag
- event: tag
services:
database:
image: postgres:15.2-alpine
# 15-alpine image necessary because of diesel tests
image: pgautoupgrade/pgautoupgrade:15-alpine
environment:
POSTGRES_USER: lemmy
POSTGRES_PASSWORD: password

View file

@ -1,3 +0,0 @@
# Contributing
See [here](https://join-lemmy.org/docs/en/contributors/01-overview.html) for contributing Instructions.

4576
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
[workspace.package]
version = "0.19.0-rc.7"
version = "0.19.6-beta.7"
edition = "2021"
description = "A link aggregator for the fediverse"
license = "AGPL-3.0"
@ -16,6 +16,7 @@ license.workspace = true
homepage.workspace = true
documentation.workspace = true
repository.workspace = true
publish = false
[lib]
doctest = false
@ -35,14 +36,6 @@ opt-level = "z" # Optimize for size.
debug = 0
[features]
embed-pictrs = ["pict-rs"]
console = [
"console-subscriber",
"opentelemetry",
"opentelemetry-otlp",
"tracing-opentelemetry",
"reqwest-tracing/opentelemetry_0_16",
]
json-log = ["tracing-subscriber/json"]
default = []
@ -53,6 +46,7 @@ members = [
"crates/api_common",
"crates/apub",
"crates/utils",
"crates/db_perf",
"crates/db_schema",
"crates/db_views",
"crates/db_views_actor",
@ -63,8 +57,8 @@ members = [
[workspace.lints.clippy]
cast_lossless = "deny"
complexity = "deny"
correctness = "deny"
complexity = { level = "deny", priority = -1 }
correctness = { level = "deny", priority = -1 }
dbg_macro = "deny"
explicit_into_iter_loop = "deny"
explicit_iter_loop = "deny"
@ -75,85 +69,94 @@ inefficient_to_string = "deny"
items-after-statements = "deny"
manual_string_new = "deny"
needless_collect = "deny"
perf = "deny"
perf = { level = "deny", priority = -1 }
redundant_closure_for_method_calls = "deny"
style = "deny"
suspicious = "deny"
style = { level = "deny", priority = -1 }
suspicious = { level = "deny", priority = -1 }
uninlined_format_args = "allow"
unused_self = "deny"
unwrap_used = "deny"
unimplemented = "deny"
[workspace.dependencies]
lemmy_api = { version = "=0.19.0-rc.7", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.0-rc.7", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.0-rc.7", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.0-rc.7", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.0-rc.7", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.0-rc.7", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.0-rc.7", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.0-rc.7", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.0-rc.7", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.0-rc.7", path = "./crates/db_views_moderator" }
activitypub_federation = { version = "0.5.0-beta.5", default-features = false, features = [
lemmy_api = { version = "=0.19.6-beta.7", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.6-beta.7", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.6-beta.7", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.6-beta.7", path = "./crates/utils", default-features = false }
lemmy_db_schema = { version = "=0.19.6-beta.7", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.6-beta.7", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.6-beta.7", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.6-beta.7", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.6-beta.7", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.6-beta.7", path = "./crates/db_views_moderator" }
lemmy_federate = { version = "=0.19.6-beta.7", path = "./crates/federate" }
activitypub_federation = { version = "0.6.0-alpha1", default-features = false, features = [
"actix-web",
] }
diesel = "2.1.3"
diesel = "2.1.6"
diesel_migrations = "2.1.0"
diesel-async = "0.3.2"
serde = { version = "1.0.189", features = ["derive"] }
serde_with = "3.4.0"
actix-web = { version = "4.4.0", default-features = false, features = [
diesel-async = "0.4.1"
serde = { version = "1.0.204", features = ["derive"] }
serde_with = "3.9.0"
actix-web = { version = "4.9.0", default-features = false, features = [
"macros",
"rustls",
"rustls-0_23",
"compress-brotli",
"compress-gzip",
"compress-zstd",
"cookies",
] }
tracing = "0.1.40"
tracing-actix-web = { version = "0.7.8", default-features = false }
tracing-error = "0.2.0"
tracing-log = "0.1.4"
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
url = { version = "2.4.1", features = ["serde"] }
reqwest = { version = "0.11.22", features = ["json", "blocking", "gzip"] }
reqwest-middleware = "0.2.4"
reqwest-tracing = "0.4.6"
tracing-actix-web = { version = "0.7.10", default-features = false }
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
url = { version = "2.5.2", features = ["serde"] }
reqwest = { version = "0.12.7", default-features = false, features = [
"json",
"blocking",
"gzip",
"rustls-tls",
] }
reqwest-middleware = "0.3.3"
reqwest-tracing = "0.5.3"
clokwerk = "0.4.0"
doku = { version = "0.21.1", features = ["url-2"] }
bcrypt = "0.15.0"
chrono = { version = "0.4.31", features = ["serde"], default-features = false }
serde_json = { version = "1.0.107", features = ["preserve_order"] }
base64 = "0.21.5"
uuid = { version = "1.5.0", features = ["serde", "v4"] }
async-trait = "0.1.74"
bcrypt = "0.15.1"
chrono = { version = "0.4.38", features = ["serde"], default-features = false }
serde_json = { version = "1.0.121", features = ["preserve_order"] }
base64 = "0.22.1"
uuid = { version = "1.10.0", features = ["serde", "v4"] }
async-trait = "0.1.81"
captcha = "0.0.9"
anyhow = { version = "1.0.75", features = [
anyhow = { version = "1.0.86", features = [
"backtrace",
] } # backtrace is on by default on nightly, but not stable rust
diesel_ltree = "0.3.0"
typed-builder = "0.15.2"
serial_test = "2.0.0"
tokio = { version = "1.33.0", features = ["full"] }
regex = "1.10.2"
once_cell = "1.18.0"
diesel-derive-newtype = "2.1.0"
diesel_ltree = "0.3.1"
serial_test = "3.1.1"
tokio = { version = "1.39.2", features = ["full"] }
regex = "1.10.5"
diesel-derive-newtype = "2.1.2"
diesel-derive-enum = { version = "2.1.0", features = ["postgres"] }
strum = "0.25.0"
strum_macros = "0.25.3"
itertools = "0.11.0"
futures = "0.3.28"
http = "0.2.9"
percent-encoding = "2.3.0"
strum = { version = "0.26.3", features = ["derive"] }
itertools = "0.13.0"
futures = "0.3.30"
http = "1.1"
rosetta-i18n = "0.1.3"
opentelemetry = { version = "0.19.0", features = ["rt-tokio"] }
tracing-opentelemetry = { version = "0.19.0" }
ts-rs = { version = "7.0.0", features = ["serde-compat", "chrono-impl"] }
rustls = { version = "0.21.8", features = ["dangerous_configuration"] }
futures-util = "0.3.28"
tokio-postgres = "0.7.10"
tokio-postgres-rustls = "0.10.0"
ts-rs = { version = "7.1.1", features = [
"serde-compat",
"chrono-impl",
"no-serde-warnings",
] }
rustls = { version = "0.23.12", features = ["ring"] }
futures-util = "0.3.30"
tokio-postgres = "0.7.11"
tokio-postgres-rustls = "0.12.0"
urlencoding = "2.1.3"
enum-map = "2.7"
moka = { version = "0.12.8", features = ["future"] }
i-love-jesus = { version = "0.1.0" }
clap = { version = "4.5.13", features = ["derive", "env"] }
pretty_assertions = "1.4.0"
derive-new = "0.7.0"
[dependencies]
lemmy_api = { workspace = true }
@ -163,15 +166,13 @@ lemmy_utils = { workspace = true }
lemmy_db_schema = { workspace = true }
lemmy_api_common = { workspace = true }
lemmy_routes = { workspace = true }
lemmy_federate = { version = "0.19.0-rc.7", path = "crates/federate" }
lemmy_federate = { workspace = true }
activitypub_federation = { workspace = true }
diesel = { workspace = true }
diesel-async = { workspace = true }
actix-web = { workspace = true }
tracing = { workspace = true }
tracing-actix-web = { workspace = true }
tracing-error = { workspace = true }
tracing-log = { workspace = true }
tracing-subscriber = { workspace = true }
url = { workspace = true }
reqwest = { workspace = true }
@ -179,15 +180,15 @@ reqwest-middleware = { workspace = true }
reqwest-tracing = { workspace = true }
clokwerk = { workspace = true }
serde_json = { workspace = true }
tracing-opentelemetry = { workspace = true, optional = true }
opentelemetry = { workspace = true, optional = true }
console-subscriber = { version = "0.1.10", optional = true }
opentelemetry-otlp = { version = "0.12.0", optional = true }
pict-rs = { version = "0.4.5", optional = true }
rustls = { workspace = true }
tokio.workspace = true
actix-cors = "0.6.4"
actix-cors = "0.7.0"
futures-util = { workspace = true }
chrono = { workspace = true }
prometheus = { version = "0.13.3", features = ["process"] }
prometheus = { version = "0.13.4", features = ["process"] }
serial_test = { workspace = true }
clap = { version = "4.4.7", features = ["derive"] }
clap = { workspace = true }
actix-web-prom = "0.8.0"
[dev-dependencies]
pretty_assertions = { workspace = true }

View file

@ -7,7 +7,7 @@
[![Translation status](http://weblate.join-lemmy.org/widgets/lemmy/-/lemmy/svg-badge.svg)](http://weblate.join-lemmy.org/engage/lemmy/)
[![License](https://img.shields.io/github/license/LemmyNet/lemmy.svg)](LICENSE)
![GitHub stars](https://img.shields.io/github/stars/LemmyNet/lemmy?style=social)
[![Delightful Humane Tech](https://codeberg.org/teaserbot-labs/delightful-humane-design/raw/branch/main/humane-tech-badge.svg)](https://codeberg.org/teaserbot-labs/delightful-humane-design)
<a href="https://endsoftwarepatents.org/innovating-without-patents"><img style="height: 20px;" src="https://static.fsf.org/nosvn/esp/logos/patent-free.svg"></a>
</div>
@ -107,7 +107,6 @@ Each Lemmy server can set its own moderation policy; appointing site-wide admins
- NSFW post / community support.
- High performance.
- Server is written in rust.
- Front end is `~80kB` gzipped.
- Supports arm64 / Raspberry Pi.
## Installation
@ -122,6 +121,8 @@ Each Lemmy server can set its own moderation policy; appointing site-wide admins
Lemmy is free, open-source software, meaning no advertising, monetizing, or venture capital, ever. Your donations directly support full-time development of the project.
Lemmy is made possible by a generous grant from the [NLnet foundation](https://nlnet.nl/).
- [Support on Liberapay](https://liberapay.com/Lemmy).
- [Support on Patreon](https://www.patreon.com/dessalines).
- [Support on OpenCollective](https://opencollective.com/lemmy).
@ -132,21 +133,25 @@ Lemmy is free, open-source software, meaning no advertising, monetizing, or vent
- bitcoin: `1Hefs7miXS5ff5Ck5xvmjKjXf5242KzRtK`
- ethereum: `0x400c96c96acbC6E7B3B43B1dc1BB446540a88A01`
- monero: `41taVyY6e1xApqKyMVDRVxJ76sPkfZhALLTjRvVKpaAh2pBd4wv9RgYj1tSPrx8wc6iE1uWUfjtQdTmTy2FGMeChGVKPQuV`
- cardano: `addr1q858t89l2ym6xmrugjs0af9cslfwvnvsh2xxp6x4dcez7pf5tushkp4wl7zxfhm2djp6gq60dk4cmc7seaza5p3slx0sakjutm`
## Contributing
Read the following documentation to setup the development environment and start coding:
- [Contributing instructions](https://join-lemmy.org/docs/contributors/01-overview.html)
- [Docker Development](https://join-lemmy.org/docs/contributors/03-docker-development.html)
- [Local Development](https://join-lemmy.org/docs/contributors/02-local-development.html)
When working on an issue or pull request, you can comment with any questions you may have so that maintainers can answer them. You can also join the [Matrix Development Chat](https://matrix.to/#/#lemmydev:matrix.org) for general assistance.
### Translations
- If you want to help with translating, take a look at [Weblate](https://weblate.join-lemmy.org/projects/lemmy/). You can also help by [translating the documentation](https://github.com/LemmyNet/lemmy-docs#adding-a-new-language).
## Contact
## Community
- [Mastodon](https://mastodon.social/@LemmyDev)
- [Matrix Space](https://matrix.to/#/#lemmy-space:matrix.org)
- [Lemmy Forum](https://lemmy.ml/c/lemmy)
- [Lemmy Support Forum](https://lemmy.ml/c/lemmy_support)
## Code Mirrors

View file

@ -1,41 +0,0 @@
{
"root": true,
"env": {
"browser": true
},
"plugins": ["@typescript-eslint"],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json",
"warnOnUnsupportedTypeScriptVersion": false
},
"rules": {
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"arrow-body-style": 0,
"curly": 0,
"eol-last": 0,
"eqeqeq": 0,
"func-style": 0,
"import/no-duplicates": 0,
"max-statements": 0,
"max-params": 0,
"new-cap": 0,
"no-console": 0,
"no-duplicate-imports": 0,
"no-extra-parens": 0,
"no-return-assign": 0,
"no-throw-literal": 0,
"no-trailing-spaces": 0,
"no-unused-expressions": 0,
"no-useless-constructor": 0,
"no-useless-escape": 0,
"no-var": 0,
"prefer-const": 0,
"prefer-rest-params": 0,
"quote-props": 0,
"unicorn/filename-case": 0
}
}

1
api_tests/.npmrc Normal file
View file

@ -0,0 +1 @@
package-manager-strict=false

View file

@ -0,0 +1,56 @@
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
export default [
pluginJs.configs.recommended,
...tseslint.configs.recommended,
{
languageOptions: {
parser: tseslint.parser,
},
},
// For some reason this has to be in its own block
{
ignores: [
"putTypesInIndex.js",
"dist/*",
"docs/*",
".yalc",
"jest.config.js",
],
},
{
files: ["src/**/*"],
rules: {
"@typescript-eslint/no-empty-interface": 0,
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/ban-ts-comment": 0,
"@typescript-eslint/no-explicit-any": 0,
"@typescript-eslint/explicit-module-boundary-types": 0,
"@typescript-eslint/no-var-requires": 0,
"arrow-body-style": 0,
curly: 0,
"eol-last": 0,
eqeqeq: 0,
"func-style": 0,
"import/no-duplicates": 0,
"max-statements": 0,
"max-params": 0,
"new-cap": 0,
"no-console": 0,
"no-duplicate-imports": 0,
"no-extra-parens": 0,
"no-return-assign": 0,
"no-throw-literal": 0,
"no-trailing-spaces": 0,
"no-unused-expressions": 0,
"no-useless-constructor": 0,
"no-useless-escape": 0,
"no-var": 0,
"prefer-const": 0,
"prefer-rest-params": 0,
"quote-props": 0,
"unicorn/filename-case": 0,
},
},
];

View file

@ -6,10 +6,11 @@
"repository": "https://github.com/LemmyNet/lemmy",
"author": "Dessalines",
"license": "AGPL-3.0",
"packageManager": "pnpm@9.9.0",
"scripts": {
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check 'src/**/*.ts'",
"lint": "tsc --noEmit && eslint --report-unused-disable-directives && prettier --check 'src/**/*.ts'",
"fix": "prettier --write src && eslint --fix src",
"api-test": "jest -i follow.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts && jest -i private_message.spec.ts && jest -i user.spec.ts && jest -i community.spec.ts && jest -i image.spec.ts",
"api-test": "jest -i follow.spec.ts && jest -i image.spec.ts && jest -i user.spec.ts && jest -i private_message.spec.ts && jest -i community.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts ",
"api-test-follow": "jest -i follow.spec.ts",
"api-test-comment": "jest -i comment.spec.ts",
"api-test-post": "jest -i post.spec.ts",
@ -19,17 +20,17 @@
"api-test-image": "jest -i image.spec.ts"
},
"devDependencies": {
"@types/jest": "^29.5.10",
"@types/node": "^20.9.4",
"@typescript-eslint/eslint-plugin": "^6.12.0",
"@typescript-eslint/parser": "^6.12.0",
"download-file-sync": "^1.0.4",
"eslint": "^8.54.0",
"eslint-plugin-prettier": "^5.0.1",
"@types/jest": "^29.5.12",
"@types/node": "^22.3.0",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"eslint": "^9.9.0",
"eslint-plugin-prettier": "^5.1.3",
"jest": "^29.5.0",
"lemmy-js-client": "0.19.0-alpha.18",
"prettier": "^3.1.0",
"lemmy-js-client": "0.20.0-alpha.11",
"prettier": "^3.2.5",
"ts-jest": "^29.1.0",
"typescript": "^5.3.2"
"typescript": "^5.5.4",
"typescript-eslint": "^8.1.0"
}
}

Binary file not shown.

3496
api_tests/pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

@ -3,14 +3,19 @@
# it is expected that this script is called by run-federation-test.sh script.
set -e
if [ -z "$LEMMY_LOG_LEVEL" ];
then
LEMMY_LOG_LEVEL=info
fi
export RUST_BACKTRACE=1
export RUST_LOG="warn,lemmy_server=debug,lemmy_federate=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"
export RUST_LOG="warn,lemmy_server=$LEMMY_LOG_LEVEL,lemmy_federate=$LEMMY_LOG_LEVEL,lemmy_api=$LEMMY_LOG_LEVEL,lemmy_api_common=$LEMMY_LOG_LEVEL,lemmy_api_crud=$LEMMY_LOG_LEVEL,lemmy_apub=$LEMMY_LOG_LEVEL,lemmy_db_schema=$LEMMY_LOG_LEVEL,lemmy_db_views=$LEMMY_LOG_LEVEL,lemmy_db_views_actor=$LEMMY_LOG_LEVEL,lemmy_db_views_moderator=$LEMMY_LOG_LEVEL,lemmy_routes=$LEMMY_LOG_LEVEL,lemmy_utils=$LEMMY_LOG_LEVEL,lemmy_websocket=$LEMMY_LOG_LEVEL"
export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queue has delays in the scale of 30s-5min
# pictrs setup
if ! [ -f "pict-rs" ]; then
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.0-beta.2/pict-rs-linux-amd64" -o api_tests/pict-rs
if [ ! -f "api_tests/pict-rs" ]; then
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.16/pict-rs-linux-amd64" -o api_tests/pict-rs
chmod +x api_tests/pict-rs
fi
./api_tests/pict-rs \
@ -46,32 +51,35 @@ fi
echo "$PWD"
LOG_DIR=target/log
mkdir -p $LOG_DIR
echo "start alpha"
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_alpha.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_alpha" \
target/lemmy_server >/tmp/lemmy_alpha.out 2>&1 &
target/lemmy_server >$LOG_DIR/lemmy_alpha.out 2>&1 &
echo "start beta"
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_beta.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_beta" \
target/lemmy_server >/tmp/lemmy_beta.out 2>&1 &
target/lemmy_server >$LOG_DIR/lemmy_beta.out 2>&1 &
echo "start gamma"
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_gamma.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_gamma" \
target/lemmy_server >/tmp/lemmy_gamma.out 2>&1 &
target/lemmy_server >$LOG_DIR/lemmy_gamma.out 2>&1 &
echo "start delta"
# An instance with only an allowlist for beta
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_delta.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_delta" \
target/lemmy_server >/tmp/lemmy_delta.out 2>&1 &
target/lemmy_server >$LOG_DIR/lemmy_delta.out 2>&1 &
echo "start epsilon"
# An instance who has a blocklist, with lemmy-alpha blocked
LEMMY_CONFIG_LOCATION=./docker/federation/lemmy_epsilon.hjson \
LEMMY_DATABASE_URL="${LEMMY_DATABASE_URL}/lemmy_epsilon" \
target/lemmy_server >/tmp/lemmy_epsilon.out 2>&1 &
target/lemmy_server >$LOG_DIR/lemmy_epsilon.out 2>&1 &
echo "wait for all instances to start"
while [[ "$(curl -s -o /dev/null -w '%{http_code}' 'lemmy-alpha:8541/api/v3/site')" != "200" ]]; do sleep 1; done

View file

@ -10,8 +10,8 @@ killall -s1 lemmy_server || true
./api_tests/prepare-drone-federation-test.sh
popd
yarn
yarn api-test || true
pnpm i
pnpm api-test || true
killall -s1 lemmy_server || true
killall -s1 pict-rs || true

View file

@ -37,16 +37,15 @@ import {
followCommunity,
blockCommunity,
delay,
saveUserSettings,
} from "./shared";
import { CommentView, CommunityView } from "lemmy-js-client";
import { LemmyHttp } from "lemmy-js-client";
import { CommentView, CommunityView, SaveUserSettings } from "lemmy-js-client";
let betaCommunity: CommunityView | undefined;
let postOnAlphaRes: PostResponse;
beforeAll(async () => {
await setupLogins();
await unfollows();
await Promise.all([followBeta(alpha), followBeta(gamma)]);
betaCommunity = (await resolveBetaCommunity(alpha)).community;
if (betaCommunity) {
@ -54,9 +53,7 @@ beforeAll(async () => {
}
});
afterAll(() => {
unfollows();
});
afterAll(unfollows);
function assertCommentFederation(
commentOne?: CommentView,
@ -95,7 +92,7 @@ test("Create a comment", async () => {
test("Create a comment in a non-existent post", async () => {
await expect(createComment(alpha, -1)).rejects.toStrictEqual(
Error("couldnt_find_post"),
Error("not_found"),
);
});
@ -129,8 +126,9 @@ test("Update a comment", async () => {
});
test("Delete a comment", async () => {
let post = await createPost(alpha, betaCommunity!.community.id);
// creating a comment on alpha (remote from home of community)
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
let commentRes = await createComment(alpha, post.post_view.post.id);
// Find the comment on beta (home of community)
let betaComment = (
@ -145,7 +143,7 @@ test("Delete a comment", async () => {
await waitUntil(
() =>
resolveComment(gamma, commentRes.comment_view.comment).catch(e => e),
r => r.message !== "couldnt_find_object",
r => r.message !== "not_found",
)
).comment;
if (!gammaComment) {
@ -158,17 +156,18 @@ test("Delete a comment", async () => {
commentRes.comment_view.comment.id,
);
expect(deleteCommentRes.comment_view.comment.deleted).toBe(true);
expect(deleteCommentRes.comment_view.comment.content).toBe("");
// Make sure that comment is undefined on beta
await waitUntil(
() => resolveComment(beta, commentRes.comment_view.comment).catch(e => e),
e => e.message == "couldnt_find_object",
e => e.message == "not_found",
);
// Make sure that comment is undefined on gamma after delete
await waitUntil(
() => resolveComment(gamma, commentRes.comment_view.comment).catch(e => e),
e => e.message === "couldnt_find_object",
e => e.message === "not_found",
);
// Test undeleting the comment
@ -183,7 +182,7 @@ test("Delete a comment", async () => {
let betaComment2 = (
await waitUntil(
() => resolveComment(beta, commentRes.comment_view.comment).catch(e => e),
e => e.message !== "couldnt_find_object",
e => e.message !== "not_found",
)
).comment;
expect(betaComment2?.comment.deleted).toBe(false);
@ -256,6 +255,16 @@ test("Remove a comment from admin and community on different instance", async ()
betaComment.comment.id,
);
expect(removeCommentRes.comment_view.comment.removed).toBe(true);
expect(removeCommentRes.comment_view.comment.content).toBe("");
// Comment text is also hidden from list
let listComments = await getComments(
beta,
removeCommentRes.comment_view.post.id,
);
expect(listComments.comments.length).toBe(1);
expect(listComments.comments[0].comment.removed).toBe(true);
expect(listComments.comments[0].comment.content).toBe("");
// Make sure its not removed on alpha
let refetchedPostComments = await getComments(
@ -345,17 +354,26 @@ test("Federated comment like", async () => {
test("Reply to a comment from another instance, get notification", async () => {
await alpha.markAllAsRead();
let betaCommunity = (await resolveBetaCommunity(alpha)).community;
let betaCommunity = (
await waitUntil(
() => resolveBetaCommunity(alpha),
c => !!c.community?.community.instance_id,
)
).community;
if (!betaCommunity) {
throw "Missing beta community";
}
const postOnAlphaRes = await createPost(alpha, betaCommunity.community.id);
// Create a root-level trunk-branch comment on alpha
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
// find that comment id on beta
let betaComment = (
await resolveComment(beta, commentRes.comment_view.comment)
await waitUntil(
() => resolveComment(beta, commentRes.comment_view.comment),
c => c.comment?.counts.score === 1,
)
).comment;
if (!betaComment) {
@ -406,7 +424,10 @@ test("Reply to a comment from another instance, get notification", async () => {
expect(alphaUnreadCountRes.replies).toBeGreaterThanOrEqual(1);
// check inbox of replies on alpha, fetching read/unread both
let alphaRepliesRes = await getReplies(alpha);
let alphaRepliesRes = await waitUntil(
() => getReplies(alpha),
r => r.replies.length > 0,
);
const alphaReply = alphaRepliesRes.replies.find(
r => r.comment.id === alphaComment.comment.id,
);
@ -423,6 +444,59 @@ test("Reply to a comment from another instance, get notification", async () => {
assertCommentFederation(alphaReply, replyRes.comment_view);
});
test("Bot reply notifications are filtered when bots are hidden", async () => {
const newAlphaBot = await registerUser(alpha, alphaUrl);
let form: SaveUserSettings = {
bot_account: true,
};
await saveUserSettings(newAlphaBot, form);
const alphaCommunity = (
await resolveCommunity(alpha, "!main@lemmy-alpha:8541")
).community;
if (!alphaCommunity) {
throw "Missing alpha community";
}
await alpha.markAllAsRead();
form = {
show_bot_accounts: false,
};
await saveUserSettings(alpha, form);
const postOnAlphaRes = await createPost(alpha, alphaCommunity.community.id);
// Bot reply to alpha's post
let commentRes = await createComment(
newAlphaBot,
postOnAlphaRes.post_view.post.id,
);
expect(commentRes).toBeDefined();
let alphaUnreadCountRes = await getUnreadCount(alpha);
expect(alphaUnreadCountRes.replies).toBe(0);
let alphaUnreadRepliesRes = await getReplies(alpha, true);
expect(alphaUnreadRepliesRes.replies.length).toBe(0);
// This both restores the original state that may be expected by other tests
// implicitly and is used by the next steps to ensure replies are still
// returned when a user later decides to show bot accounts again.
form = {
show_bot_accounts: true,
};
await saveUserSettings(alpha, form);
alphaUnreadCountRes = await getUnreadCount(alpha);
expect(alphaUnreadCountRes.replies).toBe(1);
alphaUnreadRepliesRes = await getReplies(alpha, true);
expect(alphaUnreadRepliesRes.replies.length).toBe(1);
expect(alphaUnreadRepliesRes.replies[0].comment.id).toBe(
commentRes.comment_view.comment.id,
);
});
test("Mention beta from alpha", async () => {
if (!betaCommunity) throw Error("no community");
const postOnAlphaRes = await createPost(alpha, betaCommunity.community.id);

View file

@ -1,5 +1,6 @@
jest.setTimeout(120000);
import { AddModToCommunity } from "lemmy-js-client/dist/types/AddModToCommunity";
import { CommunityView } from "lemmy-js-client/dist/types/CommunityView";
import {
alpha,
@ -9,6 +10,7 @@ import {
resolveCommunity,
createCommunity,
deleteCommunity,
delay,
removeCommunity,
getCommunity,
followCommunity,
@ -29,12 +31,14 @@ import {
delta,
betaAllowedInstances,
searchPostLocal,
resolveBetaCommunity,
longDelay,
editCommunity,
unfollows,
} from "./shared";
import { EditSite, LemmyHttp } from "lemmy-js-client";
import { EditCommunity, EditSite } from "lemmy-js-client";
beforeAll(setupLogins);
afterAll(unfollows);
function assertCommunityFederation(
communityOne?: CommunityView,
@ -240,7 +244,7 @@ test("Admin actions in remote community are not federated to origin", async () =
);
expect(banRes.banned).toBe(true);
// ban doesnt federate to community's origin instance alpha
// ban doesn't federate to community's origin instance alpha
let alphaPost = (await resolvePost(alpha, gammaPost.post)).post;
expect(alphaPost?.creator_banned_from_community).toBe(false);
@ -377,7 +381,9 @@ test("User blocks instance, communities are hidden", async () => {
test("Community follower count is federated", async () => {
// Follow the beta community from alpha
let resolved = await resolveBetaCommunity(alpha);
let community = await createCommunity(beta);
let communityActorId = community.community_view.community.actor_id;
let resolved = await resolveCommunity(alpha, communityActorId);
if (!resolved.community) {
throw "Missing beta community";
}
@ -385,7 +391,7 @@ test("Community follower count is federated", async () => {
await followCommunity(alpha, true, resolved.community.community.id);
let followed = (
await waitUntil(
() => resolveBetaCommunity(alpha),
() => resolveCommunity(alpha, communityActorId),
c => c.community?.subscribed === "Subscribed",
)
).community;
@ -394,7 +400,7 @@ test("Community follower count is federated", async () => {
expect(followed?.counts.subscribers).toBe(1);
// Follow the community from gamma
resolved = await resolveBetaCommunity(gamma);
resolved = await resolveCommunity(gamma, communityActorId);
if (!resolved.community) {
throw "Missing beta community";
}
@ -402,7 +408,7 @@ test("Community follower count is federated", async () => {
await followCommunity(gamma, true, resolved.community.community.id);
followed = (
await waitUntil(
() => resolveBetaCommunity(gamma),
() => resolveCommunity(gamma, communityActorId),
c => c.community?.subscribed === "Subscribed",
)
).community;
@ -411,7 +417,7 @@ test("Community follower count is federated", async () => {
expect(followed?.counts?.subscribers).toBe(2);
// Follow the community from delta
resolved = await resolveBetaCommunity(delta);
resolved = await resolveCommunity(delta, communityActorId);
if (!resolved.community) {
throw "Missing beta community";
}
@ -419,7 +425,7 @@ test("Community follower count is federated", async () => {
await followCommunity(delta, true, resolved.community.community.id);
followed = (
await waitUntil(
() => resolveBetaCommunity(delta),
() => resolveCommunity(delta, communityActorId),
c => c.community?.subscribed === "Subscribed",
)
).community;
@ -448,7 +454,7 @@ test("Dont receive community activities after unsubscribe", async () => {
);
expect(communityRes1.community_view.counts.subscribers).toBe(2);
// temporarily block alpha, so that it doesnt know about unfollow
// temporarily block alpha, so that it doesn't know about unfollow
let editSiteForm: EditSite = {};
editSiteForm.allowed_instances = ["lemmy-epsilon"];
await beta.editSite(editSiteForm);
@ -480,3 +486,90 @@ test("Dont receive community activities after unsubscribe", async () => {
let postResBeta = searchPostLocal(beta, postRes.post_view.post);
expect((await postResBeta).posts.length).toBe(0);
});
test("Fetch community, includes posts", async () => {
let communityRes = await createCommunity(alpha);
expect(communityRes.community_view.community.name).toBeDefined();
expect(communityRes.community_view.counts.subscribers).toBe(1);
let postRes = await createPost(
alpha,
communityRes.community_view.community.id,
);
expect(postRes.post_view.post).toBeDefined();
let resolvedCommunity = await waitUntil(
() =>
resolveCommunity(beta, communityRes.community_view.community.actor_id),
c => c.community?.community.id != undefined,
);
let betaCommunity = resolvedCommunity.community;
expect(betaCommunity?.community.actor_id).toBe(
communityRes.community_view.community.actor_id,
);
await longDelay();
let post_listing = await getPosts(beta, "All", betaCommunity?.community.id);
expect(post_listing.posts.length).toBe(1);
expect(post_listing.posts[0].post.ap_id).toBe(postRes.post_view.post.ap_id);
});
test("Content in local-only community doesn't federate", async () => {
// create a community and set it local-only
let communityRes = (await createCommunity(alpha)).community_view.community;
let form: EditCommunity = {
community_id: communityRes.id,
visibility: "LocalOnly",
};
await editCommunity(alpha, form);
// cant resolve the community from another instance
await expect(
resolveCommunity(beta, communityRes.actor_id),
).rejects.toStrictEqual(Error("not_found"));
// create a post, also cant resolve it
let postRes = await createPost(alpha, communityRes.id);
await expect(resolvePost(beta, postRes.post_view.post)).rejects.toStrictEqual(
Error("not_found"),
);
});
test("Remote mods can edit communities", async () => {
let communityRes = await createCommunity(alpha);
let betaCommunity = await resolveCommunity(
beta,
communityRes.community_view.community.actor_id,
);
if (!betaCommunity.community) {
throw "Missing beta community";
}
let betaOnAlpha = await resolvePerson(alpha, "lemmy_beta@lemmy-beta:8551");
let form: AddModToCommunity = {
community_id: communityRes.community_view.community.id,
person_id: betaOnAlpha.person?.person.id as number,
added: true,
};
alpha.addModToCommunity(form);
let form2: EditCommunity = {
community_id: betaCommunity.community?.community.id as number,
description: "Example description",
};
await editCommunity(beta, form2);
// give alpha time to get and process the edit
await delay(1000);
let alphaCommunity = await getCommunity(
alpha,
communityRes.community_view.community.id,
);
await expect(alphaCommunity.community_view.community.description).toBe(
"Example description",
);
});

View file

@ -5,46 +5,65 @@ import {
setupLogins,
resolveBetaCommunity,
followCommunity,
unfollowRemotes,
getSite,
waitUntil,
beta,
betaUrl,
registerUser,
unfollows,
delay,
} from "./shared";
beforeAll(setupLogins);
afterAll(() => {
unfollowRemotes(alpha);
});
afterAll(unfollows);
test("Follow local community", async () => {
let user = await registerUser(beta, betaUrl);
let community = (await resolveBetaCommunity(user)).community!;
expect(community.counts.subscribers).toBe(1);
let follow = await followCommunity(user, true, community.community.id);
// Make sure the follow response went through
expect(follow.community_view.community.local).toBe(true);
expect(follow.community_view.subscribed).toBe("Subscribed");
expect(follow.community_view.counts.subscribers).toBe(2);
expect(follow.community_view.counts.subscribers).toBe(
community.counts.subscribers + 1,
);
expect(follow.community_view.counts.subscribers_local).toBe(
community.counts.subscribers_local + 1,
);
// Test an unfollow
let unfollow = await followCommunity(user, false, community.community.id);
expect(unfollow.community_view.subscribed).toBe("NotSubscribed");
expect(unfollow.community_view.counts.subscribers).toBe(1);
expect(unfollow.community_view.counts.subscribers).toBe(
community.counts.subscribers,
);
expect(unfollow.community_view.counts.subscribers_local).toBe(
community.counts.subscribers_local,
);
});
test("Follow federated community", async () => {
let betaCommunity = (await resolveBetaCommunity(alpha)).community;
if (!betaCommunity) {
// It takes about 1 second for the community aggregates to federate
await delay(2000); // if this is the second test run, we don't have a way to wait for the correct number of subscribers
const betaCommunityInitial = (
await waitUntil(
() => resolveBetaCommunity(alpha),
c => !!c.community && c.community?.counts.subscribers >= 1,
)
).community;
if (!betaCommunityInitial) {
throw "Missing beta community";
}
let follow = await followCommunity(alpha, true, betaCommunity.community.id);
let follow = await followCommunity(
alpha,
true,
betaCommunityInitial.community.id,
);
expect(follow.community_view.subscribed).toBe("Pending");
betaCommunity = (
const betaCommunity = (
await waitUntil(
() => resolveBetaCommunity(alpha),
c => c.community?.subscribed === "Subscribed",
@ -55,18 +74,24 @@ test("Follow federated community", async () => {
expect(betaCommunity?.community.local).toBe(false);
expect(betaCommunity?.community.name).toBe("main");
expect(betaCommunity?.subscribed).toBe("Subscribed");
expect(betaCommunity?.counts.subscribers_local).toBe(
betaCommunityInitial.counts.subscribers_local + 1,
);
// check that unfollow was federated
let communityOnBeta1 = await resolveBetaCommunity(beta);
expect(communityOnBeta1.community?.counts.subscribers).toBe(2);
expect(communityOnBeta1.community?.counts.subscribers).toBe(
betaCommunityInitial.counts.subscribers + 1,
);
// Check it from local
let site = await getSite(alpha);
let remoteCommunityId = site.my_user?.follows.find(
c => c.community.local == false,
c =>
c.community.local == false &&
c.community.id === betaCommunityInitial.community.id,
)?.community.id;
expect(remoteCommunityId).toBeDefined();
expect(site.my_user?.follows.length).toBe(2);
if (!remoteCommunityId) {
throw "Missing remote community id";
@ -78,9 +103,21 @@ test("Follow federated community", async () => {
// Make sure you are unsubbed locally
let siteUnfollowCheck = await getSite(alpha);
expect(siteUnfollowCheck.my_user?.follows.length).toBe(1);
expect(
siteUnfollowCheck.my_user?.follows.find(
c => c.community.id === betaCommunityInitial.community.id,
),
).toBe(undefined);
// check that unfollow was federated
let communityOnBeta2 = await resolveBetaCommunity(beta);
expect(communityOnBeta2.community?.counts.subscribers).toBe(1);
let communityOnBeta2 = await waitUntil(
() => resolveBetaCommunity(beta),
c =>
c.community?.counts.subscribers ===
betaCommunityInitial.counts.subscribers,
);
expect(communityOnBeta2.community?.counts.subscribers).toBe(
betaCommunityInitial.counts.subscribers,
);
expect(communityOnBeta2.community?.counts.subscribers_local).toBe(1);
});

View file

@ -8,61 +8,111 @@ import {
} from "lemmy-js-client";
import {
alpha,
alphaImage,
alphaUrl,
beta,
betaUrl,
createCommunity,
createPost,
deleteAllImages,
epsilon,
followCommunity,
gamma,
getSite,
imageFetchLimit,
registerUser,
resolveBetaCommunity,
resolveCommunity,
resolvePost,
setupLogins,
unfollowRemotes,
waitForPost,
unfollows,
getPost,
waitUntil,
createPostWithThumbnail,
sampleImage,
sampleSite,
} from "./shared";
import fs = require("fs");
const downloadFileSync = require("download-file-sync");
beforeAll(setupLogins);
afterAll(() => {
unfollowRemotes(alpha);
afterAll(async () => {
await Promise.all([unfollows(), deleteAllImages(alpha)]);
});
test("Upload image and delete it", async () => {
// upload test image
const upload_image = fs.readFileSync("test.png");
// Before running this test, you need to delete all previous images in the DB
await deleteAllImages(alpha);
// Upload test image. We use a simple string buffer as pictrs doesn't require an actual image
// in testing mode.
const upload_form: UploadImage = {
image: upload_image,
image: Buffer.from("test"),
};
const upload = await alpha.uploadImage(upload_form);
const upload = await alphaImage.uploadImage(upload_form);
expect(upload.files![0].file).toBeDefined();
expect(upload.files![0].delete_token).toBeDefined();
expect(upload.url).toBeDefined();
expect(upload.delete_url).toBeDefined();
// ensure that image download is working. theres probably a better way to do this
const content = downloadFileSync(upload.url);
const response = await fetch(upload.url ?? "");
const content = await response.text();
expect(content.length).toBeGreaterThan(0);
// Ensure that it comes back with the list_media endpoint
const listMediaRes = await alphaImage.listMedia();
expect(listMediaRes.images.length).toBe(1);
// Ensure that it also comes back with the admin all images
const listAllMediaRes = await alphaImage.listAllMedia({
limit: imageFetchLimit,
});
// This number comes from all the previous thumbnails fetched in other tests.
const previousThumbnails = 1;
expect(listAllMediaRes.images.length).toBe(previousThumbnails);
// The deleteUrl is a combination of the endpoint, delete token, and alias
let firstImage = listMediaRes.images[0];
let deleteUrl = `${alphaUrl}/pictrs/image/delete/${firstImage.local_image.pictrs_delete_token}/${firstImage.local_image.pictrs_alias}`;
expect(deleteUrl).toBe(upload.delete_url);
// Make sure the uploader is correct
expect(firstImage.person.actor_id).toBe(
`http://lemmy-alpha:8541/u/lemmy_alpha`,
);
// delete image
const delete_form: DeleteImage = {
token: upload.files![0].delete_token,
filename: upload.files![0].file,
};
const delete_ = await alpha.deleteImage(delete_form);
const delete_ = await alphaImage.deleteImage(delete_form);
expect(delete_).toBe(true);
// ensure that image is deleted
const content2 = downloadFileSync(upload.url);
const response2 = await fetch(upload.url ?? "");
const content2 = await response2.text();
expect(content2).toBe("");
// Ensure that it shows the image is deleted
const deletedListMediaRes = await alphaImage.listMedia();
expect(deletedListMediaRes.images.length).toBe(0);
// Ensure that the admin shows its deleted
const deletedListAllMediaRes = await alphaImage.listAllMedia({
limit: imageFetchLimit,
});
expect(deletedListAllMediaRes.images.length).toBe(previousThumbnails - 1);
});
test("Purge user, uploaded image removed", async () => {
let user = await registerUser(alpha, alphaUrl);
let user = await registerUser(alphaImage, alphaUrl);
// upload test image
const upload_image = fs.readFileSync("test.png");
const upload_form: UploadImage = {
image: upload_image,
image: Buffer.from("test"),
};
const upload = await user.uploadImage(upload_form);
expect(upload.files![0].file).toBeDefined();
@ -71,19 +121,21 @@ test("Purge user, uploaded image removed", async () => {
expect(upload.delete_url).toBeDefined();
// ensure that image download is working. theres probably a better way to do this
const content = downloadFileSync(upload.url);
const response = await fetch(upload.url ?? "");
const content = await response.text();
expect(content.length).toBeGreaterThan(0);
// purge user
let site = await getSite(user);
const purge_form: PurgePerson = {
const purgeForm: PurgePerson = {
person_id: site.my_user!.local_user_view.person.id,
};
const delete_ = await alpha.purgePerson(purge_form);
const delete_ = await alphaImage.purgePerson(purgeForm);
expect(delete_.success).toBe(true);
// ensure that image is deleted
const content2 = downloadFileSync(upload.url);
const response2 = await fetch(upload.url ?? "");
const content2 = await response2.text();
expect(content2).toBe("");
});
@ -91,9 +143,8 @@ test("Purge post, linked image removed", async () => {
let user = await registerUser(beta, betaUrl);
// upload test image
const upload_image = fs.readFileSync("test.png");
const upload_form: UploadImage = {
image: upload_image,
image: Buffer.from("test"),
};
const upload = await user.uploadImage(upload_form);
expect(upload.files![0].file).toBeDefined();
@ -102,7 +153,8 @@ test("Purge post, linked image removed", async () => {
expect(upload.delete_url).toBeDefined();
// ensure that image download is working. theres probably a better way to do this
const content = downloadFileSync(upload.url);
const response = await fetch(upload.url ?? "");
const content = await response.text();
expect(content.length).toBeGreaterThan(0);
let community = await resolveBetaCommunity(user);
@ -112,15 +164,209 @@ test("Purge post, linked image removed", async () => {
upload.url,
);
expect(post.post_view.post.url).toBe(upload.url);
expect(post.post_view.image_details).toBeDefined();
// purge post
const purge_form: PurgePost = {
const purgeForm: PurgePost = {
post_id: post.post_view.post.id,
};
const delete_ = await beta.purgePost(purge_form);
const delete_ = await beta.purgePost(purgeForm);
expect(delete_.success).toBe(true);
// ensure that image is deleted
const content2 = downloadFileSync(upload.url);
const response2 = await fetch(upload.url ?? "");
const content2 = await response2.text();
expect(content2).toBe("");
});
test("Images in remote image post are proxied if setting enabled", async () => {
let community = await createCommunity(gamma);
let postRes = await createPost(
gamma,
community.community_view.community.id,
sampleImage,
`![](${sampleImage})`,
);
const post = postRes.post_view.post;
expect(post).toBeDefined();
// Make sure it fetched the image details
expect(postRes.post_view.image_details).toBeDefined();
// remote image gets proxied after upload
expect(
post.thumbnail_url?.startsWith(
"http://lemmy-gamma:8561/api/v3/image_proxy?url",
),
).toBeTruthy();
expect(
post.body?.startsWith("![](http://lemmy-gamma:8561/api/v3/image_proxy?url"),
).toBeTruthy();
// Make sure that it ends with jpg, to be sure its an image
expect(post.thumbnail_url?.endsWith(".jpg")).toBeTruthy();
let epsilonPostRes = await resolvePost(epsilon, postRes.post_view.post);
expect(epsilonPostRes.post).toBeDefined();
// Fetch the post again, the metadata should be backgrounded now
// Wait for the metadata to get fetched, since this is backgrounded now
let epsilonPostRes2 = await waitUntil(
() => getPost(epsilon, epsilonPostRes.post!.post.id),
p => p.post_view.post.thumbnail_url != undefined,
);
const epsilonPost = epsilonPostRes2.post_view.post;
expect(
epsilonPost.thumbnail_url?.startsWith(
"http://lemmy-epsilon:8581/api/v3/image_proxy?url",
),
).toBeTruthy();
expect(
epsilonPost.body?.startsWith(
"![](http://lemmy-epsilon:8581/api/v3/image_proxy?url",
),
).toBeTruthy();
// Make sure that it ends with jpg, to be sure its an image
expect(epsilonPost.thumbnail_url?.endsWith(".jpg")).toBeTruthy();
});
test("Thumbnail of remote image link is proxied if setting enabled", async () => {
let community = await createCommunity(gamma);
let postRes = await createPost(
gamma,
community.community_view.community.id,
// The sample site metadata thumbnail ends in png
sampleSite,
);
const post = postRes.post_view.post;
expect(post).toBeDefined();
// remote image gets proxied after upload
expect(
post.thumbnail_url?.startsWith(
"http://lemmy-gamma:8561/api/v3/image_proxy?url",
),
).toBeTruthy();
// Make sure that it ends with png, to be sure its an image
expect(post.thumbnail_url?.endsWith(".png")).toBeTruthy();
let epsilonPostRes = await resolvePost(epsilon, postRes.post_view.post);
expect(epsilonPostRes.post).toBeDefined();
let epsilonPostRes2 = await waitUntil(
() => getPost(epsilon, epsilonPostRes.post!.post.id),
p => p.post_view.post.thumbnail_url != undefined,
);
const epsilonPost = epsilonPostRes2.post_view.post;
expect(
epsilonPost.thumbnail_url?.startsWith(
"http://lemmy-epsilon:8581/api/v3/image_proxy?url",
),
).toBeTruthy();
// Make sure that it ends with png, to be sure its an image
expect(epsilonPost.thumbnail_url?.endsWith(".png")).toBeTruthy();
});
test("No image proxying if setting is disabled", async () => {
let user = await registerUser(beta, betaUrl);
let community = await createCommunity(alpha);
let betaCommunity = await resolveCommunity(
beta,
community.community_view.community.actor_id,
);
await followCommunity(beta, true, betaCommunity.community!.community.id);
const upload_form: UploadImage = {
image: Buffer.from("test"),
};
const upload = await user.uploadImage(upload_form);
let post = await createPost(
alpha,
community.community_view.community.id,
upload.url,
`![](${sampleImage})`,
);
expect(post.post_view.post).toBeDefined();
// remote image doesn't get proxied after upload
expect(
post.post_view.post.url?.startsWith("http://127.0.0.1:8551/pictrs/image/"),
).toBeTruthy();
expect(post.post_view.post.body).toBe(`![](${sampleImage})`);
let betaPost = await waitForPost(
beta,
post.post_view.post,
res => res?.post.alt_text != null,
);
expect(betaPost.post).toBeDefined();
// remote image doesn't get proxied after federation
expect(
betaPost.post.url?.startsWith("http://127.0.0.1:8551/pictrs/image/"),
).toBeTruthy();
expect(betaPost.post.body).toBe(`![](${sampleImage})`);
// Make sure the alt text got federated
expect(post.post_view.post.alt_text).toBe(betaPost.post.alt_text);
});
test("Make regular post, and give it a custom thumbnail", async () => {
const uploadForm1: UploadImage = {
image: Buffer.from("testRegular1"),
};
const upload1 = await alphaImage.uploadImage(uploadForm1);
const community = await createCommunity(alphaImage);
// Use wikipedia since it has an opengraph image
const wikipediaUrl = "https://wikipedia.org/";
let post = await createPostWithThumbnail(
alphaImage,
community.community_view.community.id,
wikipediaUrl,
upload1.url!,
);
// Wait for the metadata to get fetched, since this is backgrounded now
post = await waitUntil(
() => getPost(alphaImage, post.post_view.post.id),
p => p.post_view.post.thumbnail_url != undefined,
);
expect(post.post_view.post.url).toBe(wikipediaUrl);
// Make sure it uses custom thumbnail
expect(post.post_view.post.thumbnail_url).toBe(upload1.url);
});
test("Create an image post, and make sure a custom thumbnail doesn't overwrite it", async () => {
const uploadForm1: UploadImage = {
image: Buffer.from("test1"),
};
const upload1 = await alphaImage.uploadImage(uploadForm1);
const uploadForm2: UploadImage = {
image: Buffer.from("test2"),
};
const upload2 = await alphaImage.uploadImage(uploadForm2);
const community = await createCommunity(alphaImage);
let post = await createPostWithThumbnail(
alphaImage,
community.community_view.community.id,
upload1.url!,
upload2.url!,
);
post = await waitUntil(
() => getPost(alphaImage, post.post_view.post.id),
p => p.post_view.post.thumbnail_url != undefined,
);
expect(post.post_view.post.url).toBe(upload1.url);
// Make sure the custom thumbnail is ignored
expect(post.post_view.post.thumbnail_url == upload2.url).toBe(false);
});

View file

@ -18,12 +18,12 @@ import {
resolveBetaCommunity,
createComment,
deletePost,
delay,
removePost,
getPost,
unfollowRemotes,
resolvePerson,
banPersonFromSite,
searchPostLocal,
followCommunity,
banPersonFromCommunity,
reportPost,
@ -37,9 +37,10 @@ import {
waitForPost,
alphaUrl,
loginUser,
createCommunity,
} from "./shared";
import { PostView } from "lemmy-js-client/dist/types/PostView";
import { LemmyHttp, ResolveObject } from "lemmy-js-client";
import { EditSite, ResolveObject } from "lemmy-js-client";
let betaCommunity: CommunityView | undefined;
@ -47,14 +48,28 @@ beforeAll(async () => {
await setupLogins();
betaCommunity = (await resolveBetaCommunity(alpha)).community;
expect(betaCommunity).toBeDefined();
await unfollows();
});
afterAll(() => {
unfollows();
});
afterAll(unfollows);
async function assertPostFederation(
postOne: PostView,
postTwo: PostView,
waitForMeta = true,
) {
// Link metadata is generated in background task and may not be ready yet at this time,
// so wait for it explicitly. For removed posts we cant refetch anything.
if (waitForMeta) {
postOne = await waitForPost(beta, postOne.post, res => {
return res === null || !!res?.post.embed_title;
});
postTwo = await waitForPost(
beta,
postTwo.post,
res => res === null || !!res?.post.embed_title,
);
}
function assertPostFederation(postOne?: PostView, postTwo?: PostView) {
expect(postOne?.post.ap_id).toBe(postTwo?.post.ap_id);
expect(postOne?.post.name).toBe(postTwo?.post.name);
expect(postOne?.post.body).toBe(postTwo?.post.body);
@ -72,11 +87,23 @@ function assertPostFederation(postOne?: PostView, postTwo?: PostView) {
}
test("Create a post", async () => {
// Setup some allowlists and blocklists
const editSiteForm: EditSite = {};
editSiteForm.allowed_instances = [];
editSiteForm.blocked_instances = ["lemmy-alpha"];
await epsilon.editSite(editSiteForm);
if (!betaCommunity) {
throw "Missing beta community";
}
let postRes = await createPost(alpha, betaCommunity.community.id);
let postRes = await createPost(
alpha,
betaCommunity.community.id,
"https://example.com/",
"აშშ ითხოვს ირანს დაუყოვნებლივ გაანთავისუფლოს დაკავებული ნავთობის ტანკერი",
);
expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false);
expect(postRes.post_view.creator.local).toBe(true);
@ -93,23 +120,27 @@ test("Create a post", async () => {
expect(betaPost?.community.local).toBe(true);
expect(betaPost?.creator.local).toBe(false);
expect(betaPost?.counts.score).toBe(1);
assertPostFederation(betaPost, postRes.post_view);
await assertPostFederation(betaPost, postRes.post_view);
// Delta only follows beta, so it should not see an alpha ap_id
await expect(
resolvePost(delta, postRes.post_view.post),
).rejects.toStrictEqual(Error("couldnt_find_object"));
).rejects.toStrictEqual(Error("not_found"));
// Epsilon has alpha blocked, it should not see the alpha post
await expect(
resolvePost(epsilon, postRes.post_view.post),
).rejects.toStrictEqual(Error("couldnt_find_object"));
).rejects.toStrictEqual(Error("not_found"));
// remove added allow/blocklists
editSiteForm.allowed_instances = [];
editSiteForm.blocked_instances = [];
await delta.editSite(editSiteForm);
await epsilon.editSite(editSiteForm);
});
test("Create a post in a non-existent community", async () => {
await expect(createPost(alpha, -2)).rejects.toStrictEqual(
Error("couldnt_find_community"),
);
await expect(createPost(alpha, -2)).rejects.toStrictEqual(Error("not_found"));
});
test("Unlike a post", async () => {
@ -135,7 +166,7 @@ test("Unlike a post", async () => {
expect(betaPost?.community.local).toBe(true);
expect(betaPost?.creator.local).toBe(false);
expect(betaPost?.counts.score).toBe(0);
assertPostFederation(betaPost, postRes.post_view);
await assertPostFederation(betaPost, postRes.post_view);
});
test("Update a post", async () => {
@ -156,7 +187,7 @@ test("Update a post", async () => {
expect(betaPost.community.local).toBe(true);
expect(betaPost.creator.local).toBe(false);
expect(betaPost.post.name).toBe(updatedName);
assertPostFederation(betaPost, updatedPost.post_view);
await assertPostFederation(betaPost, updatedPost.post_view);
// Make sure lemmy beta cannot update the post
await expect(editPost(beta, betaPost.post)).rejects.toStrictEqual(
@ -198,12 +229,35 @@ test("Sticky a post", async () => {
if (!gammaPost) {
throw "Missing gamma post";
}
let gammaTrySticky = await featurePost(gamma, true, gammaPost.post);
// This has been failing occasionally
await featurePost(gamma, true, gammaPost.post);
let betaPost3 = (await resolvePost(beta, postRes.post_view.post)).post;
expect(gammaTrySticky.post_view.post.featured_community).toBe(true);
// expect(gammaTrySticky.post_view.post.featured_community).toBe(true);
expect(betaPost3?.post.featured_community).toBe(false);
});
test("Collection of featured posts gets federated", async () => {
// create a new community and feature a post
let community = await createCommunity(alpha);
let post = await createPost(alpha, community.community_view.community.id);
let featuredPost = await featurePost(alpha, true, post.post_view.post);
expect(featuredPost.post_view.post.featured_community).toBe(true);
// fetch the community, ensure that post is also fetched and marked as featured
let betaCommunity = await resolveCommunity(
beta,
community.community_view.community.actor_id,
);
expect(betaCommunity).toBeDefined();
const betaPost = await waitForPost(
beta,
post.post_view.post,
post => post?.post.featured_community === true,
);
expect(betaPost).toBeDefined();
});
test("Lock a post", async () => {
if (!betaCommunity) {
throw "Missing beta community";
@ -227,8 +281,10 @@ test("Lock a post", async () => {
post => !!post && post.post.locked,
);
// Try to make a new comment there, on alpha
await expect(createComment(alpha, alphaPost1.post.id)).rejects.toStrictEqual(
// Try to make a new comment there, on alpha. For this we need to create a normal
// user account because admins/mods can comment in locked posts.
let user = await registerUser(alpha, alphaUrl);
await expect(createComment(user, alphaPost1.post.id)).rejects.toStrictEqual(
Error("locked"),
);
@ -247,7 +303,7 @@ test("Lock a post", async () => {
expect(alphaPost2.post.locked).toBe(false);
// Try to create a new comment, on alpha
let commentAlpha = await createComment(alpha, alphaPost1.post.id);
let commentAlpha = await createComment(user, alphaPost1.post.id);
expect(commentAlpha).toBeDefined();
});
@ -282,7 +338,7 @@ test("Delete a post", async () => {
throw "Missing beta post 2";
}
expect(betaPost2.post.deleted).toBe(false);
assertPostFederation(betaPost2, undeletedPost.post_view);
await assertPostFederation(betaPost2, undeletedPost.post_view);
// Make sure lemmy beta cannot delete the post
await expect(deletePost(beta, true, betaPost2.post)).rejects.toStrictEqual(
@ -325,7 +381,7 @@ test("Remove a post from admin and community on different instance", async () =>
// Make sure lemmy beta sees post is undeleted
let betaPost2 = (await resolvePost(beta, postRes.post_view.post)).post;
expect(betaPost2?.post.removed).toBe(false);
assertPostFederation(betaPost2, undeletedPost.post_view);
await assertPostFederation(betaPost2!, undeletedPost.post_view);
});
test("Remove a post from admin and community on same instance", async () => {
@ -356,7 +412,11 @@ test("Remove a post from admin and community on same instance", async () => {
p => p?.post_view.post.removed ?? false,
);
expect(alphaPost?.post_view.post.removed).toBe(true);
assertPostFederation(alphaPost.post_view, removePostRes.post_view);
await assertPostFederation(
alphaPost.post_view,
removePostRes.post_view,
false,
);
// Undelete
let undeletedPost = await removePost(beta, false, betaPost.post);
@ -369,7 +429,7 @@ test("Remove a post from admin and community on same instance", async () => {
p => !!p && !p.post.removed,
);
expect(alphaPost2.post.removed).toBe(false);
assertPostFederation(alphaPost2, undeletedPost.post_view);
await assertPostFederation(alphaPost2, undeletedPost.post_view);
await unfollowRemotes(alpha);
});
@ -385,30 +445,34 @@ test("Search for a post", async () => {
expect(betaPost?.post.name).toBeDefined();
});
test("Enforce site ban for federated user", async () => {
test("Enforce site ban federation for local user", async () => {
if (!betaCommunity) {
throw "Missing beta community";
}
// create a test user
let alpha_user = await registerUser(alpha, alphaUrl);
let alphaUserPerson = (await getSite(alpha_user)).my_user?.local_user_view
let alphaUserHttp = await registerUser(alpha, alphaUrl);
let alphaUserPerson = (await getSite(alphaUserHttp)).my_user?.local_user_view
.person;
let alphaUserActorId = alphaUserPerson?.actor_id;
if (!alphaUserActorId) {
throw "Missing alpha user actor id";
}
expect(alphaUserActorId).toBeDefined();
let alphaPerson = (await resolvePerson(alpha_user, alphaUserActorId!)).person;
await followBeta(alphaUserHttp);
let alphaPerson = (await resolvePerson(alphaUserHttp, alphaUserActorId!))
.person;
if (!alphaPerson) {
throw "Missing alpha person";
}
expect(alphaPerson).toBeDefined();
// alpha makes post in beta community, it federates to beta instance
let postRes1 = await createPost(alpha_user, betaCommunity.community.id);
let postRes1 = await createPost(alphaUserHttp, betaCommunity.community.id);
let searchBeta1 = await waitForPost(beta, postRes1.post_view.post);
// ban alpha from its instance
// ban alpha from its own instance
let banAlpha = await banPersonFromSite(
alpha,
alphaPerson.person.id,
@ -425,40 +489,111 @@ test("Enforce site ban for federated user", async () => {
expect(alphaUserOnBeta1.person?.person.banned).toBe(true);
// existing alpha post should be removed on beta
await waitUntil(
let betaBanRes = await waitUntil(
() => getPost(beta, searchBeta1.post.id),
s => s.post_view.post.removed,
);
expect(betaBanRes.post_view.post.removed).toBe(true);
// Unban alpha
let unBanAlpha = await banPersonFromSite(
alpha,
alphaPerson.person.id,
false,
false,
true,
);
expect(unBanAlpha.banned).toBe(false);
// existing alpha post should be restored on beta
betaBanRes = await waitUntil(
() => getPost(beta, searchBeta1.post.id),
s => !s.post_view.post.removed,
);
expect(betaBanRes.post_view.post.removed).toBe(false);
// Login gets invalidated by ban, need to login again
if (!alphaUserPerson) {
throw "Missing alpha person";
}
let newAlphaUserJwt = await loginUser(alpha, alphaUserPerson.name);
alpha_user.setHeaders({
Authorization: "Bearer " + newAlphaUserJwt.jwt ?? "",
alphaUserHttp.setHeaders({
Authorization: "Bearer " + newAlphaUserJwt.jwt,
});
// alpha makes new post in beta community, it federates
let postRes2 = await createPost(alpha_user, betaCommunity!.community.id);
let postRes2 = await createPost(alphaUserHttp, betaCommunity!.community.id);
await waitForPost(beta, postRes2.post_view.post);
let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId!);
expect(alphaUserOnBeta2.person?.person.banned).toBe(false);
await unfollowRemotes(alpha);
});
test.skip("Enforce community ban for federated user", async () => {
test("Enforce site ban federation for federated user", async () => {
if (!betaCommunity) {
throw "Missing beta community";
}
// create a test user
let alphaUserHttp = await registerUser(alpha, alphaUrl);
let alphaUserPerson = (await getSite(alphaUserHttp)).my_user?.local_user_view
.person;
let alphaUserActorId = alphaUserPerson?.actor_id;
if (!alphaUserActorId) {
throw "Missing alpha user actor id";
}
expect(alphaUserActorId).toBeDefined();
await followBeta(alphaUserHttp);
let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId!);
expect(alphaUserOnBeta2.person?.person.banned).toBe(false);
if (!alphaUserOnBeta2.person) {
throw "Missing alpha person";
}
// alpha makes post in beta community, it federates to beta instance
let postRes1 = await createPost(alphaUserHttp, betaCommunity.community.id);
let searchBeta1 = await waitForPost(beta, postRes1.post_view.post);
expect(searchBeta1.post).toBeDefined();
// Now ban and remove their data from beta
let banAlphaOnBeta = await banPersonFromSite(
beta,
alphaUserOnBeta2.person.person.id,
true,
true,
);
expect(banAlphaOnBeta.banned).toBe(true);
// The beta site ban should NOT be federated to alpha
let alphaPerson2 = (await getSite(alphaUserHttp)).my_user!.local_user_view
.person;
expect(alphaPerson2.banned).toBe(false);
// existing alpha post should be removed on beta
let betaBanRes = await waitUntil(
() => getPost(beta, searchBeta1.post.id),
s => s.post_view.post.removed,
);
expect(betaBanRes.post_view.post.removed).toBe(true);
// existing alpha's post to the beta community should be removed on alpha
let alphaPostAfterRemoveOnBeta = await waitUntil(
() => getPost(alpha, postRes1.post_view.post.id),
s => s.post_view.post.removed,
);
expect(betaBanRes.post_view.post.removed).toBe(true);
expect(alphaPostAfterRemoveOnBeta.post_view.post.removed).toBe(true);
expect(
alphaPostAfterRemoveOnBeta.post_view.creator_banned_from_community,
).toBe(true);
await unfollowRemotes(alpha);
});
test("Enforce community ban for federated user", async () => {
if (!betaCommunity) {
throw "Missing beta community";
}
await followBeta(alpha);
let alphaShortname = `@lemmy_alpha@lemmy-alpha:8541`;
let alphaPerson = (await resolvePerson(beta, alphaShortname)).person;
if (!alphaPerson) {
@ -468,38 +603,46 @@ test.skip("Enforce community ban for federated user", async () => {
// make a post in beta, it goes through
let postRes1 = await createPost(alpha, betaCommunity.community.id);
let searchBeta1 = await searchPostLocal(beta, postRes1.post_view.post);
expect(searchBeta1.posts[0]).toBeDefined();
let searchBeta1 = await waitForPost(beta, postRes1.post_view.post);
expect(searchBeta1.post).toBeDefined();
// ban alpha from beta community
let banAlpha = await banPersonFromCommunity(
beta,
alphaPerson.person.id,
2,
searchBeta1.community.id,
true,
true,
);
expect(banAlpha.banned).toBe(true);
// ensure that the post by alpha got removed
await expect(getPost(alpha, searchBeta1.posts[0].post.id)).rejects.toBe(
Error("unknown"),
let removePostRes = await waitUntil(
() => getPost(alpha, postRes1.post_view.post.id),
s => s.post_view.post.removed,
);
expect(removePostRes.post_view.post.removed).toBe(true);
expect(removePostRes.post_view.creator_banned_from_community).toBe(true);
expect(removePostRes.community_view.banned_from_community).toBe(true);
// Alpha tries to make post on beta, but it fails because of ban
await expect(createPost(alpha, betaCommunity.community.id)).rejects.toBe(
Error("banned_from_community"),
);
await expect(
createPost(alpha, betaCommunity.community.id),
).rejects.toStrictEqual(Error("person_is_banned_from_community"));
// Unban alpha
let unBanAlpha = await banPersonFromCommunity(
beta,
alphaPerson.person.id,
2,
searchBeta1.community.id,
false,
false,
);
expect(unBanAlpha.banned).toBe(false);
// Need to re-follow the community
await followBeta(alpha);
let postRes3 = await createPost(alpha, betaCommunity.community.id);
expect(postRes3.post_view.post).toBeDefined();
expect(postRes3.post_view.community.local).toBe(false);
@ -507,57 +650,86 @@ test.skip("Enforce community ban for federated user", async () => {
expect(postRes3.post_view.counts.score).toBe(1);
// Make sure that post makes it to beta community
let searchBeta2 = await searchPostLocal(beta, postRes3.post_view.post);
expect(searchBeta2.posts[0]).toBeDefined();
let postRes4 = await waitForPost(beta, postRes3.post_view.post);
expect(postRes4.post).toBeDefined();
expect(postRes4.creator_banned_from_community).toBe(false);
await unfollowRemotes(alpha);
});
test("A and G subscribe to B (center) A posts, it gets announced to G", async () => {
if (!betaCommunity) {
throw "Missing beta community";
}
await followBeta(alpha);
let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined();
let betaPost = (await resolvePost(gamma, postRes.post_view.post)).post;
expect(betaPost?.post.name).toBeDefined();
await unfollowRemotes(alpha);
});
test("Report a post", async () => {
// Note, this is a different one from the setup
let betaCommunity = (await resolveBetaCommunity(beta)).community;
if (!betaCommunity) {
throw "Missing beta community";
}
let postRes = await createPost(beta, betaCommunity.community.id);
// Create post from alpha
let alphaCommunity = (await resolveBetaCommunity(alpha)).community!;
await followBeta(alpha);
let postRes = await createPost(alpha, alphaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined();
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post;
if (!alphaPost) {
throw "Missing alpha post";
}
let alphaReport = (
await reportPost(alpha, alphaPost.post.id, randomString(10))
).post_report_view.post_report;
// Send report from gamma
let gammaPost = (await resolvePost(gamma, alphaPost.post)).post!;
let gammaReport = (
await reportPost(gamma, gammaPost.post.id, randomString(10))
).post_report_view.post_report;
expect(gammaReport).toBeDefined();
// Report was federated to community instance
let betaReport = (await waitUntil(
() =>
listPostReports(beta).then(p =>
p.post_reports.find(
r =>
r.post_report.original_post_name === alphaReport.original_post_name,
r.post_report.original_post_name === gammaReport.original_post_name,
),
),
res => !!res,
))!.post_report;
expect(betaReport).toBeDefined();
expect(betaReport.resolved).toBe(false);
expect(betaReport.original_post_name).toBe(alphaReport.original_post_name);
expect(betaReport.original_post_url).toBe(alphaReport.original_post_url);
expect(betaReport.original_post_body).toBe(alphaReport.original_post_body);
expect(betaReport.reason).toBe(alphaReport.reason);
expect(betaReport.original_post_name).toBe(gammaReport.original_post_name);
//expect(betaReport.original_post_url).toBe(gammaReport.original_post_url);
expect(betaReport.original_post_body).toBe(gammaReport.original_post_body);
expect(betaReport.reason).toBe(gammaReport.reason);
await unfollowRemotes(alpha);
// Report was federated to poster's instance
let alphaReport = (await waitUntil(
() =>
listPostReports(alpha).then(p =>
p.post_reports.find(
r =>
r.post_report.original_post_name === gammaReport.original_post_name,
),
),
res => !!res,
))!.post_report;
expect(alphaReport).toBeDefined();
expect(alphaReport.resolved).toBe(false);
expect(alphaReport.original_post_name).toBe(gammaReport.original_post_name);
//expect(alphaReport.original_post_url).toBe(gammaReport.original_post_url);
expect(alphaReport.original_post_body).toBe(gammaReport.original_post_body);
expect(alphaReport.reason).toBe(gammaReport.reason);
});
test("Fetch post via redirect", async () => {
await followBeta(alpha);
let alphaPost = await createPost(alpha, betaCommunity!.community.id);
expect(alphaPost.post_view.post).toBeDefined();
// Make sure that post is liked on beta
@ -578,4 +750,47 @@ test("Fetch post via redirect", async () => {
let gammaPost = await gamma.resolveObject(form);
expect(gammaPost).toBeDefined();
expect(gammaPost.post?.post.ap_id).toBe(alphaPost.post_view.post.ap_id);
await unfollowRemotes(alpha);
});
test("Block post that contains banned URL", async () => {
let editSiteForm: EditSite = {
blocked_urls: ["https://evil.com/"],
};
await epsilon.editSite(editSiteForm);
await delay();
if (!betaCommunity) {
throw "Missing beta community";
}
expect(
createPost(epsilon, betaCommunity.community.id, "https://evil.com"),
).rejects.toStrictEqual(Error("blocked_url"));
// Later tests need this to be empty
editSiteForm.blocked_urls = [];
await epsilon.editSite(editSiteForm);
});
test("Fetch post with redirect", async () => {
let alphaPost = await createPost(alpha, betaCommunity!.community.id);
expect(alphaPost.post_view.post).toBeDefined();
// beta fetches from alpha as usual
let betaPost = await resolvePost(beta, alphaPost.post_view.post);
expect(betaPost.post).toBeDefined();
// gamma fetches from beta, and gets redirected to alpha
let gammaPost = await resolvePost(gamma, betaPost.post!.post);
expect(gammaPost.post).toBeDefined();
// fetch remote object from local url, which redirects to the original url
let form: ResolveObject = {
q: `http://lemmy-gamma:8561/post/${gammaPost.post!.post.id}`,
};
let gammaPost2 = await gamma.resolveObject(form);
expect(gammaPost2.post).toBeDefined();
});

View file

@ -8,8 +8,9 @@ import {
editPrivateMessage,
listPrivateMessages,
deletePrivateMessage,
unfollowRemotes,
waitUntil,
reportPrivateMessage,
unfollows,
} from "./shared";
let recipient_id: number;
@ -20,9 +21,7 @@ beforeAll(async () => {
recipient_id = 3;
});
afterAll(() => {
unfollowRemotes(alpha);
});
afterAll(unfollows);
test("Create a private message", async () => {
let pmRes = await createPrivateMessage(alpha, recipient_id);
@ -109,3 +108,42 @@ test("Delete a private message", async () => {
betaPms1.private_messages.length,
);
});
test("Create a private message report", async () => {
let pmRes = await createPrivateMessage(alpha, recipient_id);
let betaPms1 = await waitUntil(
() => listPrivateMessages(beta),
m =>
!!m.private_messages.find(
e =>
e.private_message.ap_id ===
pmRes.private_message_view.private_message.ap_id,
),
);
let betaPm = betaPms1.private_messages[0];
expect(betaPm).toBeDefined();
// Make sure that only the recipient can report it, so this should fail
await expect(
reportPrivateMessage(
alpha,
pmRes.private_message_view.private_message.id,
"a reason",
),
).rejects.toStrictEqual(Error("couldnt_create_report"));
// This one should pass
let reason = "another reason";
let report = await reportPrivateMessage(
beta,
betaPm.private_message.id,
reason,
);
expect(report.private_message_report_view.private_message.id).toBe(
betaPm.private_message.id,
);
expect(report.private_message_report_view.private_message_report.reason).toBe(
reason,
);
});

View file

@ -4,12 +4,16 @@ import {
BlockInstance,
BlockInstanceResponse,
CommunityId,
CreatePrivateMessageReport,
DeleteImage,
EditCommunity,
GetReplies,
GetRepliesResponse,
GetUnreadCountResponse,
InstanceId,
LemmyHttp,
PostView,
PrivateMessageReportResponse,
SuccessResponse,
} from "lemmy-js-client";
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
@ -75,19 +79,26 @@ import { GetPersonDetailsResponse } from "lemmy-js-client/dist/types/GetPersonDe
import { GetPersonDetails } from "lemmy-js-client/dist/types/GetPersonDetails";
import { ListingType } from "lemmy-js-client/dist/types/ListingType";
export let alphaUrl = "http://127.0.0.1:8541";
export let betaUrl = "http://127.0.0.1:8551";
export let gammaUrl = "http://127.0.0.1:8561";
export let deltaUrl = "http://127.0.0.1:8571";
export let epsilonUrl = "http://127.0.0.1:8581";
export const fetchFunction = fetch;
export const imageFetchLimit = 50;
export const sampleImage =
"https://i.pinimg.com/originals/df/5f/5b/df5f5b1b174a2b4b6026cc6c8f9395c1.jpg";
export const sampleSite = "https://yahoo.com";
export let alpha = new LemmyHttp(alphaUrl);
export let beta = new LemmyHttp(betaUrl);
export let gamma = new LemmyHttp(gammaUrl);
export let delta = new LemmyHttp(deltaUrl);
export let epsilon = new LemmyHttp(epsilonUrl);
export const alphaUrl = "http://127.0.0.1:8541";
export const betaUrl = "http://127.0.0.1:8551";
export const gammaUrl = "http://127.0.0.1:8561";
export const deltaUrl = "http://127.0.0.1:8571";
export const epsilonUrl = "http://127.0.0.1:8581";
export let betaAllowedInstances = [
export const alpha = new LemmyHttp(alphaUrl, { fetchFunction });
export const alphaImage = new LemmyHttp(alphaUrl);
export const beta = new LemmyHttp(betaUrl, { fetchFunction });
export const gamma = new LemmyHttp(gammaUrl, { fetchFunction });
export const delta = new LemmyHttp(deltaUrl, { fetchFunction });
export const epsilon = new LemmyHttp(epsilonUrl, { fetchFunction });
export const betaAllowedInstances = [
"lemmy-alpha",
"lemmy-gamma",
"lemmy-delta",
@ -135,6 +146,7 @@ export async function setupLogins() {
resEpsilon,
]);
alpha.setHeaders({ Authorization: `Bearer ${res[0].jwt ?? ""}` });
alphaImage.setHeaders({ Authorization: `Bearer ${res[0].jwt ?? ""}` });
beta.setHeaders({ Authorization: `Bearer ${res[1].jwt ?? ""}` });
gamma.setHeaders({ Authorization: `Bearer ${res[2].jwt ?? ""}` });
delta.setHeaders({ Authorization: `Bearer ${res[3].jwt ?? ""}` });
@ -171,13 +183,10 @@ export async function setupLogins() {
];
await gamma.editSite(editSiteForm);
// Setup delta allowed instance
editSiteForm.allowed_instances = ["lemmy-beta"];
await delta.editSite(editSiteForm);
editSiteForm.allowed_instances = [];
editSiteForm.blocked_instances = ["lemmy-alpha"];
await epsilon.editSite(editSiteForm);
// Create the main alpha/beta communities
// Ignore thrown errors of duplicates
try {
@ -188,7 +197,7 @@ export async function setupLogins() {
// (because last_successful_id is set to current id when federation to an instance is first started)
// only needed the first time so do in this try
await delay(10_000);
} catch (_) {
} catch {
console.log("Communities already exist");
}
}
@ -196,16 +205,20 @@ export async function setupLogins() {
export async function createPost(
api: LemmyHttp,
community_id: number,
// use example.com for consistent title and embed description
url: string = "https://example.com/",
body = randomString(10),
// use example.com for consistent title and embed description
name: string = randomString(5),
alt_text = randomString(10),
custom_thumbnail: string | undefined = undefined,
): Promise<PostResponse> {
let name = randomString(5);
let body = randomString(10);
let form: CreatePost = {
name,
url,
body,
alt_text,
community_id,
custom_thumbnail,
};
return api.createPost(form);
}
@ -222,6 +235,21 @@ export async function editPost(
return api.editPost(form);
}
export async function createPostWithThumbnail(
api: LemmyHttp,
community_id: number,
url: string,
custom_thumbnail: string,
): Promise<PostResponse> {
let form: CreatePost = {
name: randomString(10),
url,
community_id,
custom_thumbnail,
};
return api.createPost(form);
}
export async function deletePost(
api: LemmyHttp,
deleted: boolean,
@ -325,6 +353,7 @@ export async function getComments(
post_id: post_id,
type_: listingType,
sort: "New",
limit: 50,
};
return api.getComments(form);
}
@ -335,10 +364,13 @@ export async function getUnreadCount(
return api.getUnreadCount();
}
export async function getReplies(api: LemmyHttp): Promise<GetRepliesResponse> {
export async function getReplies(
api: LemmyHttp,
unread_only: boolean = false,
): Promise<GetRepliesResponse> {
let form: GetReplies = {
sort: "New",
unread_only: false,
unread_only,
};
return api.getReplies(form);
}
@ -387,13 +419,13 @@ export async function banPersonFromSite(
api: LemmyHttp,
person_id: number,
ban: boolean,
remove_data: boolean,
remove_or_restore_data: boolean,
): Promise<BanPersonResponse> {
// Make sure lemmy-beta/c/main is cached on lemmy_alpha
let form: BanPerson = {
person_id,
ban,
remove_data: remove_data,
remove_or_restore_data,
};
return api.banPerson(form);
}
@ -402,13 +434,13 @@ export async function banPersonFromCommunity(
api: LemmyHttp,
person_id: number,
community_id: number,
remove_data: boolean,
remove_or_restore_data: boolean,
ban: boolean,
): Promise<BanFromCommunityResponse> {
let form: BanFromCommunity = {
person_id,
community_id,
remove_data: remove_data,
remove_or_restore_data,
ban,
};
return api.banFromCommunity(form);
@ -521,7 +553,7 @@ export async function likeComment(
export async function createCommunity(
api: LemmyHttp,
name_: string = randomString(5),
name_: string = randomString(10),
): Promise<CommunityResponse> {
let description = "a sample description";
let form: CreateCommunity = {
@ -532,6 +564,13 @@ export async function createCommunity(
return api.createCommunity(form);
}
export async function editCommunity(
api: LemmyHttp,
form: EditCommunity,
): Promise<CommunityResponse> {
return api.editCommunity(form);
}
export async function getCommunity(
api: LemmyHttp,
id: number,
@ -651,7 +690,7 @@ export async function saveUserSettingsBio(
blur_nsfw: false,
auto_expand: true,
theme: "darkly",
default_sort_type: "Active",
default_post_sort_type: "Active",
default_listing_type: "All",
interface_language: "en",
show_avatars: true,
@ -664,14 +703,14 @@ export async function saveUserSettingsBio(
export async function saveUserSettingsFederated(
api: LemmyHttp,
): Promise<SuccessResponse> {
let avatar = "https://image.flaticon.com/icons/png/512/35/35896.png";
let banner = "https://image.flaticon.com/icons/png/512/36/35896.png";
let avatar = sampleImage;
let banner = sampleImage;
let bio = "a changed bio";
let form: SaveUserSettings = {
show_nsfw: false,
blur_nsfw: true,
auto_expand: false,
default_sort_type: "Hot",
default_post_sort_type: "Hot",
default_listing_type: "All",
interface_language: "",
avatar,
@ -731,6 +770,7 @@ export async function unfollowRemotes(
await Promise.all(
remoteFollowed.map(cu => followCommunity(api, false, cu.community.id)),
);
let siteRes = await getSite(api);
return siteRes;
}
@ -776,6 +816,18 @@ export async function reportComment(
return api.createCommentReport(form);
}
export async function reportPrivateMessage(
api: LemmyHttp,
private_message_id: number,
reason: string,
): Promise<PrivateMessageReportResponse> {
let form: CreatePrivateMessageReport = {
private_message_id,
reason,
};
return api.createPrivateMessageReport(form);
}
export async function listCommentReports(
api: LemmyHttp,
): Promise<ListCommentReportsResponse> {
@ -786,9 +838,12 @@ export async function listCommentReports(
export function getPosts(
api: LemmyHttp,
listingType?: ListingType,
community_id?: number,
): Promise<GetPostsResponse> {
let form: GetPosts = {
type_: listingType,
limit: 50,
community_id,
};
return api.getPosts(form);
}
@ -840,13 +895,49 @@ export function randomString(length: number): string {
return result;
}
export async function deleteAllImages(api: LemmyHttp) {
const imagesRes = await api.listAllMedia({
limit: imageFetchLimit,
});
Promise.all(
imagesRes.images
.map(image => {
const form: DeleteImage = {
token: image.local_image.pictrs_delete_token,
filename: image.local_image.pictrs_alias,
};
return form;
})
.map(form => api.deleteImage(form)),
);
}
export async function unfollows() {
await Promise.all([
unfollowRemotes(alpha),
unfollowRemotes(beta),
unfollowRemotes(gamma),
unfollowRemotes(delta),
unfollowRemotes(epsilon),
]);
await Promise.all([
purgeAllPosts(alpha),
purgeAllPosts(beta),
purgeAllPosts(gamma),
purgeAllPosts(delta),
purgeAllPosts(epsilon),
]);
}
export async function purgeAllPosts(api: LemmyHttp) {
// The best way to get all federated items, is to find the posts
let res = await api.getPosts({ type_: "All", limit: 50 });
await Promise.all(
Array.from(new Set(res.posts.map(p => p.post.id)))
.map(post_id => api.purgePost({ post_id }))
// Ignore errors
.map(p => p.catch(e => e)),
);
}
export function getCommentParentId(comment: Comment): number | undefined {
@ -857,6 +948,7 @@ export function getCommentParentId(comment: Comment): number | undefined {
if (split.length > 1) {
return Number(split[split.length - 2]);
} else {
console.log(`Failed to extract comment parent id from ${comment.path}`);
return undefined;
}
}

View file

@ -18,11 +18,16 @@ import {
saveUserSettings,
getPost,
getComments,
fetchFunction,
alphaImage,
unfollows,
saveUserSettingsBio,
} from "./shared";
import { LemmyHttp, SaveUserSettings } from "lemmy-js-client";
import { LemmyHttp, SaveUserSettings, UploadImage } from "lemmy-js-client";
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
beforeAll(setupLogins);
afterAll(unfollows);
let apShortname: string;
@ -44,7 +49,7 @@ test("Create user", async () => {
if (!site.my_user) {
throw "Missing site user";
}
apShortname = `@${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
});
test("Set some user settings, check that they are federated", async () => {
@ -67,7 +72,7 @@ test("Delete user", async () => {
let user = await registerUser(alpha, alphaUrl);
// make a local post and comment
let alphaCommunity = (await resolveCommunity(user, "!main@lemmy-alpha:8541"))
let alphaCommunity = (await resolveCommunity(user, "main@lemmy-alpha:8541"))
.community;
if (!alphaCommunity) {
throw "Missing alpha community";
@ -114,6 +119,7 @@ test("Delete user", async () => {
test("Requests with invalid auth should be treated as unauthenticated", async () => {
let invalid_auth = new LemmyHttp(alphaUrl, {
headers: { Authorization: "Bearer foobar" },
fetchFunction,
});
let site = await getSite(invalid_auth);
expect(site.my_user).toBeUndefined();
@ -125,15 +131,86 @@ test("Requests with invalid auth should be treated as unauthenticated", async ()
});
test("Create user with Arabic name", async () => {
let user = await registerUser(alpha, alphaUrl, "تجريب");
let user = await registerUser(
alpha,
alphaUrl,
"تجريب" + Math.random().toString().slice(2, 10), // less than actor_name_max_length
);
let site = await getSite(user);
expect(site.my_user).toBeDefined();
if (!site.my_user) {
throw "Missing site user";
}
apShortname = `@${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
apShortname = `${site.my_user.local_user_view.person.name}@lemmy-alpha:8541`;
let alphaPerson = (await resolvePerson(alpha, apShortname)).person;
expect(alphaPerson).toBeDefined();
});
test("Create user with accept-language", async () => {
let lemmy_http = new LemmyHttp(alphaUrl, {
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Accept-Language#syntax
headers: { "Accept-Language": "fr-CH, en;q=0.8, de;q=0.7, *;q=0.5" },
});
let user = await registerUser(lemmy_http, alphaUrl);
let site = await getSite(user);
expect(site.my_user).toBeDefined();
expect(site.my_user?.local_user_view.local_user.interface_language).toBe(
"fr",
);
let langs = site.all_languages
.filter(a => site.my_user?.discussion_languages.includes(a.id))
.map(l => l.code);
// should have languages from accept header, as well as "undetermined"
// which is automatically enabled by backend
expect(langs).toStrictEqual(["und", "de", "en", "fr"]);
});
test("Set a new avatar, old avatar is deleted", async () => {
const listMediaRes = await alphaImage.listMedia();
expect(listMediaRes.images.length).toBe(0);
const upload_form1: UploadImage = {
image: Buffer.from("test1"),
};
const upload1 = await alphaImage.uploadImage(upload_form1);
expect(upload1.url).toBeDefined();
let form1 = {
avatar: upload1.url,
};
await saveUserSettings(alpha, form1);
const listMediaRes1 = await alphaImage.listMedia();
expect(listMediaRes1.images.length).toBe(1);
const upload_form2: UploadImage = {
image: Buffer.from("test2"),
};
const upload2 = await alphaImage.uploadImage(upload_form2);
expect(upload2.url).toBeDefined();
let form2 = {
avatar: upload2.url,
};
await saveUserSettings(alpha, form2);
// make sure only the new avatar is kept
const listMediaRes2 = await alphaImage.listMedia();
expect(listMediaRes2.images.length).toBe(1);
// Upload that same form2 avatar, make sure it isn't replaced / deleted
await saveUserSettings(alpha, form2);
// make sure only the new avatar is kept
const listMediaRes3 = await alphaImage.listMedia();
expect(listMediaRes3.images.length).toBe(1);
// Now try to save a user settings, with the icon missing,
// and make sure it doesn't clear the data, or delete the image
await saveUserSettingsBio(alpha);
let site = await getSite(alpha);
expect(site.my_user?.local_user_view.person.avatar).toBe(upload2.url);
// make sure only the new avatar is kept
const listMediaRes4 = await alphaImage.listMedia();
expect(listMediaRes4.images.length).toBe(1);
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

File diff suppressed because it is too large Load diff

89
cliff.toml Normal file
View file

@ -0,0 +1,89 @@
# 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 -%}
{% 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 -%}
{%- endif -%}
{% 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 = """
<!-- generated by git-cliff -->
"""
# 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 = "" },
]
commit_parsers = [{ field = "author.name", pattern = "renovate", skip = true }]
# 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"

View file

@ -34,24 +34,45 @@
# Name of the postgres database for lemmy
database: "string"
# Maximum number of active sql connections
pool_size: 95
pool_size: 30
}
# Settings related to activitypub federation
# Pictrs image server configuration.
pictrs: {
# Address where pictrs is available (for image hosting)
url: "http://localhost:8080/"
# Set a custom pictrs API key. ( Required for deleting images )
api_key: "string"
# By default the thumbnails for external links are stored in pict-rs. This ensures that they
# can be reliably retrieved and can be resized using pict-rs APIs. However it also increases
# storage usage. In case this is disabled, the Opengraph image is directly returned as
# thumbnail.
# Backwards compatibility with 0.18.1. False is equivalent to `image_mode: None`, true is
# equivalent to `image_mode: StoreLinkPreviews`.
#
# In some countries it is forbidden to copy preview images from newspaper articles and only
# hotlinking is allowed. If that is the case for your instance, make sure that this setting is
# disabled.
# To be removed in 0.20
cache_external_link_previews: true
# Specifies how to handle remote images, so that users don't have to connect directly to remote
# servers.
image_mode:
# Leave images unchanged, don't generate any local thumbnails for post urls. Instead the
# Opengraph image is directly returned as thumbnail
"None"
# or
# Generate thumbnails for external post urls and store them persistently in pict-rs. This
# ensures that they can be reliably retrieved and can be resized using pict-rs APIs. However
# it also increases storage usage.
#
# This is the default behaviour, and also matches Lemmy 0.18.
"StoreLinkPreviews"
# or
# If enabled, all images from remote domains are rewritten to pass through
# `/api/v3/image_proxy`, including embedded images in markdown. Images are stored temporarily
# in pict-rs for caching. This improves privacy as users don't expose their IP to untrusted
# servers, and decreases load on other servers. However it increases bandwidth use for the
# local server.
#
# Requires pict-rs 0.5
"ProxyAllImages"
# Timeout for uploading images to pictrs (in seconds)
upload_timeout: 30
}
@ -87,10 +108,12 @@
port: 8536
# Whether the site is available over TLS. Needs to be true for federation to work.
tls_enabled: true
# The number of activitypub federation workers that can be in-flight concurrently
worker_count: 0
# The number of activitypub federation retry workers that can be in-flight concurrently
retry_count: 0
federation: {
# Limit to the number of concurrent outgoing federation requests per target instance.
# Set this to a higher value than 1 (e.g. 6) only if you have a huge instance (>10 activities
# per second) and if a receiving instance is not keeping up.
concurrent_sends_per_instance: 1
}
prometheus: {
bind: "127.0.0.1"
port: 10002

View file

@ -1,5 +1,6 @@
[package]
name = "lemmy_api"
publish = false
version.workspace = true
edition.workspace = true
description.workspace = true
@ -32,12 +33,14 @@ anyhow = { workspace = true }
tracing = { workspace = true }
chrono = { workspace = true }
url = { workspace = true }
wav = "1.0.0"
sitemap-rs = "0.2.0"
totp-rs = { version = "5.4.0", features = ["gen_secret", "otpauth"] }
actix-web-httpauth = "0.8.1"
hound = "3.5.1"
sitemap-rs = "0.2.1"
totp-rs = { version = "5.6.0", features = ["gen_secret", "otpauth"] }
actix-web-httpauth = "0.8.2"
[dev-dependencies]
serial_test = { workspace = true }
tokio = { workspace = true }
elementtree = "1.2.3"
pretty_assertions = { workspace = true }
lemmy_api_crud = { workspace = true }

View file

@ -9,15 +9,20 @@ use lemmy_db_schema::{
traits::Crud,
};
use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn distinguish_comment(
data: Json<DistinguishComment>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<CommentResponse>, LemmyError> {
let orig_comment = CommentView::read(&mut context.pool(), data.comment_id, None).await?;
) -> LemmyResult<Json<CommentResponse>> {
let orig_comment = CommentView::read(
&mut context.pool(),
data.comment_id,
Some(&local_user_view.local_user),
)
.await?;
check_community_user_action(
&local_user_view.person,
@ -26,6 +31,11 @@ pub async fn distinguish_comment(
)
.await?;
// Verify that only the creator can distinguish
if local_user_view.person.id != orig_comment.creator.id {
Err(LemmyErrorType::NoCommentEditAllowed)?
}
// Verify that only a mod or admin can distinguish a comment
check_community_mod_action(
&local_user_view.person,
@ -47,7 +57,7 @@ pub async fn distinguish_comment(
let comment_view = CommentView::read(
&mut context.pool(),
data.comment_id,
Some(local_user_view.person.id),
Some(&local_user_view.local_user),
)
.await?;

View file

@ -17,7 +17,7 @@ use lemmy_db_schema::{
traits::Likeable,
};
use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use std::ops::Deref;
#[tracing::instrument(skip(context))]
@ -25,7 +25,7 @@ pub async fn like_comment(
data: Json<CreateCommentLike>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<CommentResponse>, LemmyError> {
) -> LemmyResult<Json<CommentResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let mut recipient_ids = Vec::<LocalUserId>::new();
@ -35,7 +35,12 @@ pub async fn like_comment(
check_bot_account(&local_user_view.person)?;
let comment_id = data.comment_id;
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
let orig_comment = CommentView::read(
&mut context.pool(),
comment_id,
Some(&local_user_view.local_user),
)
.await?;
check_community_user_action(
&local_user_view.person,
@ -46,7 +51,7 @@ pub async fn like_comment(
// Add parent poster or commenter to recipients
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
if let Ok(reply) = comment_reply {
if let Ok(Some(reply)) = comment_reply {
let recipient_id = reply.recipient_id;
if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await
{
@ -75,12 +80,12 @@ pub async fn like_comment(
}
ActivityChannel::submit_activity(
SendActivityData::LikePostOrComment(
orig_comment.comment.ap_id,
local_user_view.person.clone(),
orig_comment.community,
data.score,
),
SendActivityData::LikePostOrComment {
object_id: orig_comment.comment.ap_id,
actor: local_user_view.person.clone(),
community: orig_comment.community,
score: data.score,
},
&context,
)
.await?;

View file

@ -0,0 +1,35 @@
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
comment::{ListCommentLikes, ListCommentLikesResponse},
context::LemmyContext,
utils::is_mod_or_admin,
};
use lemmy_db_views::structs::{CommentView, LocalUserView, VoteView};
use lemmy_utils::error::LemmyResult;
/// Lists likes for a comment
#[tracing::instrument(skip(context))]
pub async fn list_comment_likes(
data: Query<ListCommentLikes>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<ListCommentLikesResponse>> {
let comment_view = CommentView::read(
&mut context.pool(),
data.comment_id,
Some(&local_user_view.local_user),
)
.await?;
is_mod_or_admin(
&mut context.pool(),
&local_user_view.person,
comment_view.community.id,
)
.await?;
let comment_likes =
VoteView::list_for_comment(&mut context.pool(), data.comment_id, data.page, data.limit).await?;
Ok(Json(ListCommentLikesResponse { comment_likes }))
}

View file

@ -1,3 +1,4 @@
pub mod distinguish;
pub mod like;
pub mod list_comment_likes;
pub mod save;

View file

@ -8,14 +8,14 @@ use lemmy_db_schema::{
traits::Saveable,
};
use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn save_comment(
data: Json<SaveComment>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<CommentResponse>, LemmyError> {
) -> LemmyResult<Json<CommentResponse>> {
let comment_saved_form = CommentSavedForm {
comment_id: data.comment_id,
person_id: local_user_view.person.id,
@ -32,8 +32,12 @@ pub async fn save_comment(
}
let comment_id = data.comment_id;
let person_id = local_user_view.person.id;
let comment_view = CommentView::read(&mut context.pool(), comment_id, Some(person_id)).await?;
let comment_view = CommentView::read(
&mut context.pool(),
comment_id,
Some(&local_user_view.local_user),
)
.await?;
Ok(Json(CommentResponse {
comment_view,

View file

@ -5,7 +5,11 @@ use lemmy_api_common::{
comment::{CommentReportResponse, CreateCommentReport},
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_user_action, send_new_report_email_to_admins},
utils::{
check_comment_deleted_or_removed,
check_community_user_action,
send_new_report_email_to_admins,
},
};
use lemmy_db_schema::{
source::{
@ -15,7 +19,7 @@ use lemmy_db_schema::{
traits::Reportable,
};
use lemmy_db_views::structs::{CommentReportView, CommentView, LocalUserView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
/// Creates a comment report and notifies the moderators of the community
#[tracing::instrument(skip(context))]
@ -23,7 +27,7 @@ pub async fn create_comment_report(
data: Json<CreateCommentReport>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<CommentReportResponse>, LemmyError> {
) -> LemmyResult<Json<CommentReportResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let reason = data.reason.trim().to_string();
@ -31,7 +35,12 @@ pub async fn create_comment_report(
let person_id = local_user_view.person.id;
let comment_id = data.comment_id;
let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?;
let comment_view = CommentView::read(
&mut context.pool(),
comment_id,
Some(&local_user_view.local_user),
)
.await?;
check_community_user_action(
&local_user_view.person,
@ -40,6 +49,9 @@ pub async fn create_comment_report(
)
.await?;
// Don't allow creating reports for removed / deleted comments
check_comment_deleted_or_removed(&comment_view.comment)?;
let report_form = CommentReportForm {
creator_id: person_id,
comment_id,
@ -66,12 +78,12 @@ pub async fn create_comment_report(
}
ActivityChannel::submit_activity(
SendActivityData::CreateReport(
comment_view.comment.ap_id.inner().clone(),
local_user_view.person,
comment_view.community,
data.reason.clone(),
),
SendActivityData::CreateReport {
object_id: comment_view.comment.ap_id.inner().clone(),
actor: local_user_view.person,
community: comment_view.community,
reason: data.reason.clone(),
},
&context,
)
.await?;

View file

@ -5,7 +5,7 @@ use lemmy_api_common::{
utils::check_community_mod_of_any_or_admin_action,
};
use lemmy_db_views::{comment_report_view::CommentReportQuery, structs::LocalUserView};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
/// Lists comment reports for a community if an id is supplied
/// or returns all comment reports for communities a user moderates
@ -14,8 +14,9 @@ pub async fn list_comment_reports(
data: Query<ListCommentReports>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<ListCommentReportsResponse>, LemmyError> {
) -> LemmyResult<Json<ListCommentReportsResponse>> {
let community_id = data.community_id;
let comment_id = data.comment_id;
let unresolved_only = data.unresolved_only.unwrap_or_default();
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
@ -24,6 +25,7 @@ pub async fn list_comment_reports(
let limit = data.limit;
let comment_reports = CommentReportQuery {
community_id,
comment_id,
unresolved_only,
page,
limit,

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{source::comment_report::CommentReport, traits::Reportable};
use lemmy_db_views::structs::{CommentReportView, LocalUserView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
/// Resolves or unresolves a comment report and notifies the moderators of the community
#[tracing::instrument(skip(context))]
@ -14,7 +14,7 @@ pub async fn resolve_comment_report(
data: Json<ResolveCommentReport>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<CommentReportResponse>, LemmyError> {
) -> LemmyResult<Json<CommentReportResponse>> {
let report_id = data.report_id;
let person_id = local_user_view.person.id;
let report = CommentReportView::read(&mut context.pool(), report_id, person_id).await?;
@ -23,7 +23,7 @@ pub async fn resolve_comment_report(
check_community_mod_action(
&local_user_view.person,
report.community.id,
false,
true,
&mut context.pool(),
)
.await?;

View file

@ -9,20 +9,21 @@ use lemmy_api_common::{
use lemmy_db_schema::{
source::{
community::{Community, CommunityModerator, CommunityModeratorForm},
local_user::LocalUser,
moderator::{ModAddCommunity, ModAddCommunityForm},
},
traits::{Crud, Joinable},
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn add_mod_to_community(
data: Json<AddModToCommunity>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<AddModToCommunityResponse>, LemmyError> {
) -> LemmyResult<Json<AddModToCommunityResponse>> {
let community_id = data.community_id;
// Verify that only mods or admins can add mod
@ -33,9 +34,30 @@ pub async fn add_mod_to_community(
&mut context.pool(),
)
.await?;
// If its a mod removal, also check that you're a higher mod.
if !data.added {
LocalUser::is_higher_mod_or_admin_check(
&mut context.pool(),
community_id,
local_user_view.person.id,
vec![data.person_id],
)
.await?;
}
let community = Community::read(&mut context.pool(), community_id).await?;
// If user is admin and community is remote, explicitly check that he is a
// moderator. This is necessary because otherwise the action would be rejected
// by the community's home instance.
if local_user_view.local_user.admin && !community.local {
Err(LemmyErrorType::NotAModerator)?
CommunityModeratorView::check_is_community_moderator(
&mut context.pool(),
community.id,
local_user_view.person.id,
)
.await?;
}
// Update in local database
@ -69,12 +91,12 @@ pub async fn add_mod_to_community(
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
ActivityChannel::submit_activity(
SendActivityData::AddModToCommunity(
local_user_view.person,
data.community_id,
data.person_id,
data.added,
),
SendActivityData::AddModToCommunity {
moderator: local_user_view.person,
community_id: data.community_id,
target: data.person_id,
added: data.added,
},
&context,
)
.await?;

View file

@ -4,7 +4,11 @@ use lemmy_api_common::{
community::{BanFromCommunity, BanFromCommunityResponse},
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_mod_action, check_expire_time, remove_user_data_in_community},
utils::{
check_community_mod_action,
check_expire_time,
remove_or_restore_user_data_in_community,
},
};
use lemmy_db_schema::{
source::{
@ -14,6 +18,7 @@ use lemmy_db_schema::{
CommunityPersonBan,
CommunityPersonBanForm,
},
local_user::LocalUser,
moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
},
traits::{Bannable, Crud, Followable},
@ -21,7 +26,7 @@ use lemmy_db_schema::{
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::validation::is_valid_body_field,
};
@ -30,9 +35,8 @@ pub async fn ban_from_community(
data: Json<BanFromCommunity>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<BanFromCommunityResponse>, LemmyError> {
) -> LemmyResult<Json<BanFromCommunityResponse>> {
let banned_person_id = data.person_id;
let remove_data = data.remove_data.unwrap_or(false);
let expires = check_expire_time(data.expires)?;
// Verify that only mods or admins can ban
@ -43,7 +47,18 @@ pub async fn ban_from_community(
&mut context.pool(),
)
.await?;
is_valid_body_field(&data.reason, false)?;
LocalUser::is_higher_mod_or_admin_check(
&mut context.pool(),
data.community_id,
local_user_view.person.id,
vec![data.person_id],
)
.await?;
if let Some(reason) = &data.reason {
is_valid_body_field(reason, false)?;
}
let community_user_ban_form = CommunityPersonBanForm {
community_id: data.community_id,
@ -73,9 +88,16 @@ pub async fn ban_from_community(
}
// Remove/Restore their data if that's desired
if remove_data {
remove_user_data_in_community(data.community_id, banned_person_id, &mut context.pool()).await?;
}
if data.remove_or_restore_data.unwrap_or(false) {
let remove_data = data.ban;
remove_or_restore_user_data_in_community(
data.community_id,
banned_person_id,
remove_data,
&mut context.pool(),
)
.await?;
};
// Mod tables
let form = ModBanFromCommunityForm {
@ -92,12 +114,12 @@ pub async fn ban_from_community(
let person_view = PersonView::read(&mut context.pool(), data.person_id).await?;
ActivityChannel::submit_activity(
SendActivityData::BanFromCommunity(
local_user_view.person,
data.community_id,
person_view.person.clone(),
data.0.clone(),
),
SendActivityData::BanFromCommunity {
moderator: local_user_view.person,
community_id: data.community_id,
target: person_view.person.clone(),
data: data.0.clone(),
},
&context,
)
.await?;

View file

@ -14,14 +14,14 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn block_community(
data: Json<BlockCommunity>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<BlockCommunityResponse>, LemmyError> {
) -> LemmyResult<Json<BlockCommunityResponse>> {
let community_id = data.community_id;
let person_id = local_user_view.person.id;
let community_block_form = CommunityBlockForm {
@ -50,8 +50,13 @@ pub async fn block_community(
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
}
let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), false).await?;
let community_view = CommunityView::read(
&mut context.pool(),
community_id,
Some(&local_user_view.local_user),
false,
)
.await?;
ActivityChannel::submit_activity(
SendActivityData::FollowCommunity(

View file

@ -15,14 +15,14 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn follow_community(
data: Json<FollowCommunity>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<CommunityResponse>, LemmyError> {
) -> LemmyResult<Json<CommunityResponse>> {
let community = Community::read(&mut context.pool(), data.community_id).await?;
let mut community_follower_form = CommunityFollowerForm {
community_id: community.id,
@ -60,9 +60,14 @@ pub async fn follow_community(
}
let community_id = data.community_id;
let person_id = local_user_view.person.id;
let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), false).await?;
let community_view = CommunityView::read(
&mut context.pool(),
community_id,
Some(&local_user_view.local_user),
false,
)
.await?;
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
Ok(Json(CommunityResponse {

View file

@ -15,14 +15,14 @@ use lemmy_db_schema::{
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn hide_community(
data: Json<HideCommunity>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
// Verify its a admin (only admin can hide or unhide it)
is_admin(&local_user_view)?;

View file

@ -15,7 +15,7 @@ use lemmy_db_schema::{
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
location_info,
};
@ -26,7 +26,7 @@ pub async fn transfer_community(
data: Json<TransferCommunity>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<GetCommunityResponse>, LemmyError> {
) -> LemmyResult<Json<GetCommunityResponse>> {
let community_id = data.community_id;
let mut community_mods =
CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
@ -76,16 +76,16 @@ pub async fn transfer_community(
ModTransferCommunity::create(&mut context.pool(), &form).await?;
let community_id = data.community_id;
let person_id = local_user_view.person.id;
let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), false)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
let community_view = CommunityView::read(
&mut context.pool(),
community_id,
Some(&local_user_view.local_user),
false,
)
.await?;
let community_id = data.community_id;
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id)
.await
.with_lemmy_type(LemmyErrorType::CouldntFindCommunity)?;
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
// Return the jwt
Ok(Json(GetCommunityResponse {

View file

@ -1,16 +1,32 @@
use activitypub_federation::config::Data;
use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha;
use lemmy_api_common::{
claims::Claims,
community::BanFromCommunity,
context::LemmyContext,
utils::{check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME},
send_activity::{ActivityChannel, SendActivityData},
utils::{check_expire_time, check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME},
};
use lemmy_db_schema::{
source::{
community::{
CommunityFollower,
CommunityFollowerForm,
CommunityPersonBan,
CommunityPersonBanForm,
},
local_site::LocalSite,
moderator::{ModBanFromCommunity, ModBanFromCommunityForm},
person::Person,
},
traits::{Bannable, Crud, Followable},
};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
error::{LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
utils::slurs::check_slurs,
};
use std::io::Cursor;
@ -28,31 +44,37 @@ pub mod site;
pub mod sitemap;
/// Converts the captcha to a base64 encoded wav audio file
pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result<String, LemmyError> {
pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> LemmyResult<String> {
let letters = captcha.as_wav();
// Decode each wav file, concatenate the samples
let mut concat_samples: Vec<i16> = Vec::new();
let mut any_header: Option<wav::Header> = None;
let mut any_header: Option<hound::WavSpec> = None;
for letter in letters {
let mut cursor = Cursor::new(letter.unwrap_or_default());
let (header, samples) = wav::read(&mut cursor)?;
any_header = Some(header);
if let Some(samples16) = samples.as_sixteen() {
let reader = hound::WavReader::new(&mut cursor)?;
any_header = Some(reader.spec());
let samples16 = reader
.into_samples::<i16>()
.collect::<Result<Vec<_>, _>>()
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
concat_samples.extend(samples16);
} else {
Err(LemmyErrorType::CouldntCreateAudioCaptcha)?
}
}
// Encode the concatenated result as a wav file
let mut output_buffer = Cursor::new(vec![]);
if let Some(header) = any_header {
wav::write(
header,
&wav::BitDepth::Sixteen(concat_samples),
&mut output_buffer,
)
let mut writer = hound::WavWriter::new(&mut output_buffer, header)
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
let mut writer16 = writer.get_i16_writer(concat_samples.len() as u32);
for sample in concat_samples {
writer16.write_sample(sample);
}
writer16
.flush()
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
writer
.finalize()
.with_lemmy_type(LemmyErrorType::CouldntCreateAudioCaptcha)?;
Ok(base64.encode(output_buffer.into_inner()))
@ -62,7 +84,7 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> Result<String, LemmyEr
}
/// Check size of report
pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> {
pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> LemmyResult<()> {
let slur_regex = &local_site_to_slur_regex(local_site);
check_slurs(reason, slur_regex)?;
@ -75,24 +97,15 @@ pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Resul
}
}
pub fn read_auth_token(req: &HttpRequest) -> Result<Option<String>, LemmyError> {
pub fn read_auth_token(req: &HttpRequest) -> LemmyResult<Option<String>> {
// Try reading jwt from auth header
if let Ok(header) = Authorization::<Bearer>::parse(req) {
Ok(Some(header.as_ref().token().to_string()))
}
// If that fails, try to read from cookie
else if let Some(cookie) = &req.cookie(AUTH_COOKIE_NAME) {
// ensure that its marked as httponly and secure
let secure = cookie.secure().unwrap_or_default();
let http_only = cookie.http_only().unwrap_or_default();
let is_debug_mode = cfg!(debug_assertions);
if !is_debug_mode && (!secure || !http_only) {
Err(LemmyError::from(LemmyErrorType::AuthCookieInsecure))
} else {
Ok(Some(cookie.value().to_string()))
}
}
// Otherwise, there's no auth
else {
Ok(None)
@ -128,11 +141,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<TOTP, LemmyError> {
fn build_totp_2fa(hostname: &str, username: &str, secret: &str) -> LemmyResult<TOTP> {
let sec = Secret::Raw(secret.as_bytes().to_vec());
let sec_bytes = sec
.to_bytes()
@ -144,17 +153,108 @@ 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)
}
/// Site bans are only federated for local users.
/// This is a problem, because site-banning non-local users will still leave content
/// they've posted to our local communities, on other servers.
///
/// So when doing a site ban for a non-local user, you need to federate/send a
/// community ban for every local community they've participated in.
/// See https://github.com/LemmyNet/lemmy/issues/4118
#[tracing::instrument(skip_all)]
pub(crate) async fn ban_nonlocal_user_from_local_communities(
local_user_view: &LocalUserView,
target: &Person,
ban: bool,
reason: &Option<String>,
remove_or_restore_data: &Option<bool>,
expires: &Option<i64>,
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
// Only run this code for federated users
if !target.local {
let ids = Person::list_local_community_ids(&mut context.pool(), target.id).await?;
for community_id in ids {
let expires_dt = check_expire_time(*expires)?;
// Ban / unban them from our local communities
let community_user_ban_form = CommunityPersonBanForm {
community_id,
person_id: target.id,
expires: Some(expires_dt),
};
if ban {
// Ignore all errors for these
CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form)
.await
.ok();
// Also unsubscribe them from the community, if they are subscribed
let community_follower_form = CommunityFollowerForm {
community_id,
person_id: target.id,
pending: false,
};
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
.await
.ok();
} else {
CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form)
.await
.ok();
}
// Mod tables
let form = ModBanFromCommunityForm {
mod_person_id: local_user_view.person.id,
other_person_id: target.id,
community_id,
reason: reason.clone(),
banned: Some(ban),
expires: expires_dt,
};
ModBanFromCommunity::create(&mut context.pool(), &form).await?;
// Federate the ban from community
let ban_from_community = BanFromCommunity {
community_id,
person_id: target.id,
ban,
reason: reason.clone(),
remove_or_restore_data: *remove_or_restore_data,
expires: *expires,
};
ActivityChannel::submit_activity(
SendActivityData::BanFromCommunity {
moderator: local_user_view.person.clone(),
community_id,
target: target.clone(),
data: ban_from_community,
},
context,
)
.await?;
}
}
Ok(())
}
#[tracing::instrument(skip_all)]
pub async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> Result<LocalUserView, LemmyError> {
) -> LemmyResult<LocalUserView> {
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
@ -166,15 +266,13 @@ pub async fn local_user_view_from_jwt(
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use super::*;
#[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());
}
}

View file

@ -13,23 +13,33 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn add_admin(
data: Json<AddAdmin>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<AddAdminResponse>, LemmyError> {
) -> LemmyResult<Json<AddAdminResponse>> {
// Make sure user is an admin
is_admin(&local_user_view)?;
// If its an admin removal, also check that you're a higher admin
if !data.added {
LocalUser::is_higher_admin_check(
&mut context.pool(),
local_user_view.person.id,
vec![data.person_id],
)
.await?;
}
// Make sure that the person_id added is local
let added_local_user = LocalUserView::read_person(&mut context.pool(), data.person_id)
.await
.with_lemmy_type(LemmyErrorType::ObjectNotLocal)?;
.map_err(|_| LemmyErrorType::ObjectNotLocal)?;
let added_admin = LocalUser::update(
LocalUser::update(
&mut context.pool(),
added_local_user.local_user.id,
&LocalUserUpdateForm {
@ -43,7 +53,7 @@ pub async fn add_admin(
// Mod tables
let form = ModAddForm {
mod_person_id: local_user_view.person.id,
other_person_id: added_admin.person_id,
other_person_id: added_local_user.person.id,
removed: Some(!data.added),
};

View file

@ -1,13 +1,15 @@
use crate::ban_nonlocal_user_from_local_communities;
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
person::{BanPerson, BanPersonResponse},
send_activity::{ActivityChannel, SendActivityData},
utils::{check_expire_time, is_admin, remove_user_data},
utils::{check_expire_time, is_admin, remove_user_data, restore_user_data},
};
use lemmy_db_schema::{
source::{
local_user::LocalUser,
login_token::LoginToken,
moderator::{ModBan, ModBanForm},
person::{Person, PersonUpdateForm},
@ -17,7 +19,7 @@ use lemmy_db_schema::{
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
error::{LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::validation::is_valid_body_field,
};
@ -26,11 +28,21 @@ pub async fn ban_from_site(
data: Json<BanPerson>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<BanPersonResponse>, LemmyError> {
) -> LemmyResult<Json<BanPersonResponse>> {
// Make sure user is an admin
is_admin(&local_user_view)?;
is_valid_body_field(&data.reason, false)?;
// Also make sure you're a higher admin than the target
LocalUser::is_higher_admin_check(
&mut context.pool(),
local_user_view.person.id,
vec![data.person_id],
)
.await?;
if let Some(reason) = &data.reason {
is_valid_body_field(reason, false)?;
}
let expires = check_expire_time(data.expires)?;
@ -47,21 +59,24 @@ pub async fn ban_from_site(
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
// if its a local user, invalidate logins
let local_user = LocalUserView::read_person(&mut context.pool(), data.person_id).await;
let local_user = LocalUserView::read_person(&mut context.pool(), person.id).await;
if let Ok(local_user) = local_user {
LoginToken::invalidate_all(&mut context.pool(), local_user.local_user.id).await?;
}
// Remove their data if that's desired
let remove_data = data.remove_data.unwrap_or(false);
if remove_data {
if data.remove_or_restore_data.unwrap_or(false) {
if data.ban {
remove_user_data(person.id, &context).await?;
} else {
restore_user_data(person.id, &context).await?;
}
};
// Mod tables
let form = ModBanForm {
mod_person_id: local_user_view.person.id,
other_person_id: data.person_id,
other_person_id: person.id,
reason: data.reason.clone(),
banned: Some(data.ban),
expires,
@ -69,14 +84,28 @@ pub async fn ban_from_site(
ModBan::create(&mut context.pool(), &form).await?;
let person_view = PersonView::read(&mut context.pool(), data.person_id).await?;
let person_view = PersonView::read(&mut context.pool(), person.id).await?;
ban_nonlocal_user_from_local_communities(
&local_user_view,
&person,
data.ban,
&data.reason,
&data.remove_or_restore_data,
&data.expires,
&context,
)
.await?;
ActivityChannel::submit_activity(
SendActivityData::BanFromSite(
local_user_view.person,
person_view.person.clone(),
data.0.clone(),
),
SendActivityData::BanFromSite {
moderator: local_user_view.person,
banned_user: person_view.person.clone(),
reason: data.reason.clone(),
remove_or_restore_data: data.remove_or_restore_data,
ban: data.ban,
expires: data.expires,
},
&context,
)
.await?;

View file

@ -9,14 +9,14 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn block_person(
data: Json<BlockPerson>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<BlockPersonResponse>, LemmyError> {
) -> LemmyResult<Json<BlockPersonResponse>> {
let target_id = data.person_id;
let person_id = local_user_view.person.id;
@ -30,8 +30,11 @@ pub async fn block_person(
target_id,
};
let target_user = LocalUserView::read_person(&mut context.pool(), target_id).await;
if target_user.map(|t| t.local_user.admin) == Ok(true) {
let target_user = LocalUserView::read_person(&mut context.pool(), target_id)
.await
.ok();
if target_user.is_some_and(|t| t.local_user.admin) {
Err(LemmyErrorType::CantBlockAdmin)?
}

View file

@ -11,7 +11,7 @@ use lemmy_api_common::{
};
use lemmy_db_schema::source::{local_user::LocalUser, login_token::LoginToken};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyError, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn change_password(
@ -19,7 +19,7 @@ pub async fn change_password(
req: HttpRequest,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<LoginResponse>, LemmyError> {
) -> LemmyResult<Json<LoginResponse>> {
password_length_check(&data.new_password)?;
// Make sure passwords match
@ -28,11 +28,13 @@ pub async fn change_password(
}
// Check the old password
let valid: bool = verify(
&data.old_password,
&local_user_view.local_user.password_encrypted,
)
.unwrap_or(false);
let valid: bool = if let Some(password_encrypted) = &local_user_view.local_user.password_encrypted
{
verify(&data.old_password, password_encrypted).unwrap_or(false)
} else {
data.old_password.is_empty()
};
if !valid {
Err(LemmyErrorType::IncorrectLogin)?
}

View file

@ -10,18 +10,18 @@ use lemmy_db_schema::source::{
login_token::LoginToken,
password_reset_request::PasswordResetRequest,
};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn change_password_after_reset(
data: Json<PasswordChangeAfterReset>,
context: Data<LemmyContext>,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
// Fetch the user_id from the token
let token = data.token.clone();
let local_user_id = PasswordResetRequest::read_from_token(&mut context.pool(), &token)
.await
.map(|p| p.local_user_id)?;
let local_user_id = PasswordResetRequest::read_and_delete(&mut context.pool(), &token)
.await?
.local_user_id;
password_length_check(&data.password)?;

View file

@ -1,17 +1,13 @@
use crate::{build_totp_2fa, generate_totp_2fa_secret};
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
person::GenerateTotpSecretResponse,
sensitive::Sensitive,
use lemmy_api_common::{context::LemmyContext, person::GenerateTotpSecretResponse};
use lemmy_db_schema::source::{
local_user::{LocalUser, LocalUserUpdateForm},
site::Site,
};
use lemmy_db_schema::{
source::local_user::{LocalUser, LocalUserUpdateForm},
traits::Crud,
};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::{LemmyError, LemmyErrorType};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
/// Generate a new secret for two-factor-authentication. Afterwards you need to call [toggle_totp]
/// to enable it. This can only be called if 2FA is currently disabled.
@ -19,16 +15,15 @@ use lemmy_utils::error::{LemmyError, LemmyErrorType};
pub async fn generate_totp_secret(
local_user_view: LocalUserView,
context: Data<LemmyContext>,
) -> Result<Json<GenerateTotpSecretResponse>, LemmyError> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
) -> LemmyResult<Json<GenerateTotpSecretResponse>> {
let site = Site::read_local(&mut context.pool()).await?;
if local_user_view.local_user.totp_2fa_enabled {
return Err(LemmyErrorType::TotpAlreadyEnabled)?;
}
let secret = generate_totp_2fa_secret();
let secret_url =
build_totp_2fa(&site_view.site.name, &local_user_view.person.name, &secret)?.get_url();
let secret_url = build_totp_2fa(&site.name, &local_user_view.person.name, &secret)?.get_url();
let local_user_form = LocalUserUpdateForm {
totp_2fa_secret: Some(Some(secret)),
@ -42,6 +37,6 @@ pub async fn generate_totp_secret(
.await?;
Ok(Json(GenerateTotpSecretResponse {
totp_secret_url: Sensitive::new(secret_url),
totp_secret_url: secret_url.into(),
}))
}

View file

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

View file

@ -2,12 +2,12 @@ use actix_web::web::{Data, Json};
use lemmy_api_common::{context::LemmyContext, person::BannedPersonsResponse, utils::is_admin};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
pub async fn list_banned_users(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<BannedPersonsResponse>, LemmyError> {
) -> LemmyResult<Json<BannedPersonsResponse>> {
// Make sure user is an admin
is_admin(&local_user_view)?;

View file

@ -1,14 +1,14 @@
use actix_web::web::{Data, Json};
use lemmy_api_common::context::LemmyContext;
use lemmy_api_common::{context::LemmyContext, person::ListLoginsResponse};
use lemmy_db_schema::source::login_token::LoginToken;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
pub async fn list_logins(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<Vec<LoginToken>>, LemmyError> {
) -> LemmyResult<Json<ListLoginsResponse>> {
let logins = LoginToken::list(&mut context.pool(), local_user_view.local_user.id).await?;
Ok(Json(logins))
Ok(Json(ListLoginsResponse { logins }))
}

View file

@ -0,0 +1,25 @@
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
context::LemmyContext,
person::{ListMedia, ListMediaResponse},
};
use lemmy_db_views::structs::{LocalImageView, LocalUserView};
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn list_media(
data: Query<ListMedia>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<ListMediaResponse>> {
let page = data.page;
let limit = data.limit;
let images = LocalImageView::get_all_paged_by_local_user_id(
&mut context.pool(),
local_user_view.local_user.id,
page,
limit,
)
.await?;
Ok(Json(ListMediaResponse { images }))
}

View file

@ -8,57 +8,47 @@ use lemmy_api_common::{
claims::Claims,
context::LemmyContext,
person::{Login, LoginResponse},
utils::check_user_valid,
};
use lemmy_db_schema::{
source::{local_site::LocalSite, registration_application::RegistrationApplication},
utils::DbPool,
RegistrationMode,
utils::{check_email_verified, check_registration_application, check_user_valid},
};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn login(
data: Json<Login>,
req: HttpRequest,
context: Data<LemmyContext>,
) -> Result<Json<LoginResponse>, LemmyError> {
) -> LemmyResult<Json<LoginResponse>> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
// Fetch that username / email
let username_or_email = data.username_or_email.clone();
let local_user_view =
LocalUserView::find_by_email_or_name(&mut context.pool(), &username_or_email)
.await
.with_lemmy_type(LemmyErrorType::IncorrectLogin)?;
LocalUserView::find_by_email_or_name(&mut context.pool(), &username_or_email).await?;
// Verify the password
let valid: bool = verify(
&data.password,
&local_user_view.local_user.password_encrypted,
)
let valid: bool = local_user_view
.local_user
.password_encrypted
.as_ref()
.and_then(|password_encrypted| verify(&data.password, password_encrypted).ok())
.unwrap_or(false);
if !valid {
Err(LemmyErrorType::IncorrectLogin)?
}
check_user_valid(&local_user_view.person)?;
// Check if the user's email is verified if email verification is turned on
// However, skip checking verification if the user is an admin
if !local_user_view.local_user.admin
&& site_view.local_site.require_email_verification
&& !local_user_view.local_user.email_verified
{
Err(LemmyErrorType::EmailNotVerified)?
}
check_email_verified(&local_user_view, &site_view)?;
check_registration_application(&local_user_view, &site_view.local_site, &mut context.pool())
.await?;
// 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?;
@ -69,26 +59,3 @@ pub async fn login(
registration_created: false,
}))
}
async fn check_registration_application(
local_user_view: &LocalUserView,
local_site: &LocalSite,
pool: &mut DbPool<'_>,
) -> Result<(), LemmyError> {
if (local_site.registration_mode == RegistrationMode::RequireApplication
|| local_site.registration_mode == RegistrationMode::Closed)
&& !local_user_view.local_user.accepted_application
&& !local_user_view.local_user.admin
{
// Fetch the registration application. If no admin id is present its still pending. Otherwise it
// was processed (either accepted or denied).
let local_user_id = local_user_view.local_user.id;
let registration = RegistrationApplication::find_by_local_user_id(pool, local_user_id).await?;
if registration.admin_id.is_some() {
Err(LemmyErrorType::RegistrationDenied(registration.deny_reason))?
} else {
Err(LemmyErrorType::RegistrationApplicationIsPending)?
}
}
Ok(())
}

View file

@ -7,6 +7,7 @@ pub mod generate_totp_secret;
pub mod get_captcha;
pub mod list_banned;
pub mod list_logins;
pub mod list_media;
pub mod login;
pub mod logout;
pub mod notifications;

View file

@ -5,14 +5,14 @@ use lemmy_api_common::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::person_mention_view::PersonMentionQuery;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn list_mentions(
data: Query<GetPersonMentions>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<GetPersonMentionsResponse>, LemmyError> {
) -> LemmyResult<Json<GetPersonMentionsResponse>> {
let sort = data.sort;
let page = data.page;
let limit = data.limit;

View file

@ -5,14 +5,14 @@ use lemmy_api_common::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::comment_reply_view::CommentReplyQuery;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn list_replies(
data: Query<GetReplies>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<GetRepliesResponse>, LemmyError> {
) -> LemmyResult<Json<GetRepliesResponse>> {
let sort = data.sort;
let page = data.page;
let limit = data.limit;

View file

@ -6,13 +6,13 @@ use lemmy_db_schema::source::{
private_message::PrivateMessage,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn mark_all_notifications_read(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<GetRepliesResponse>, LemmyError> {
) -> LemmyResult<Json<GetRepliesResponse>> {
let person_id = local_user_view.person.id;
// Mark all comment_replies as read

View file

@ -9,14 +9,14 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::PersonMentionView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn mark_person_mention_as_read(
data: Json<MarkPersonMentionAsRead>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PersonMentionResponse>, LemmyError> {
) -> LemmyResult<Json<PersonMentionResponse>> {
let person_mention_id = data.person_mention_id;
let read_person_mention = PersonMention::read(&mut context.pool(), person_mention_id).await?;

View file

@ -9,14 +9,14 @@ use lemmy_db_schema::{
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommentReplyView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn mark_reply_as_read(
data: Json<MarkCommentReplyAsRead>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<CommentReplyResponse>, LemmyError> {
) -> LemmyResult<Json<CommentReplyResponse>> {
let comment_reply_id = data.comment_reply_id;
let read_comment_reply = CommentReply::read(&mut context.pool(), comment_reply_id).await?;

View file

@ -2,18 +2,21 @@ use actix_web::web::{Data, Json};
use lemmy_api_common::{context::LemmyContext, person::GetUnreadCountResponse};
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
use lemmy_db_views_actor::structs::{CommentReplyView, PersonMentionView};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn unread_count(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<GetUnreadCountResponse>, LemmyError> {
) -> LemmyResult<Json<GetUnreadCountResponse>> {
let person_id = local_user_view.person.id;
let replies = CommentReplyView::get_unread_replies(&mut context.pool(), person_id).await?;
let replies =
CommentReplyView::get_unread_replies(&mut context.pool(), &local_user_view.local_user).await?;
let mentions = PersonMentionView::get_unread_mentions(&mut context.pool(), person_id).await?;
let mentions =
PersonMentionView::get_unread_mentions(&mut context.pool(), &local_user_view.local_user)
.await?;
let private_messages =
PrivateMessageView::get_unread_messages(&mut context.pool(), person_id).await?;

View file

@ -10,14 +10,14 @@ use lemmy_db_views::structs::{
PostReportView,
PrivateMessageReportView,
};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn report_count(
data: Query<GetReportCount>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<GetReportCountResponse>, LemmyError> {
) -> LemmyResult<Json<GetReportCountResponse>> {
let person_id = local_user_view.person.id;
let admin = local_user_view.local_user.admin;
let community_id = data.community_id;

View file

@ -2,12 +2,11 @@ use actix_web::web::{Data, Json};
use lemmy_api_common::{
context::LemmyContext,
person::PasswordReset,
utils::send_password_reset_email,
utils::{check_email_verified, send_password_reset_email},
SuccessResponse,
};
use lemmy_db_schema::source::password_reset_request::PasswordResetRequest;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn reset_password(
@ -18,17 +17,10 @@ pub async fn reset_password(
let email = data.email.to_lowercase();
let local_user_view = LocalUserView::find_by_email(&mut context.pool(), &email)
.await
.with_lemmy_type(LemmyErrorType::IncorrectLogin)?;
.map_err(|_| LemmyErrorType::IncorrectLogin)?;
// Check for too many attempts (to limit potential abuse)
let recent_resets_count = PasswordResetRequest::get_recent_password_resets_count(
&mut context.pool(),
local_user_view.local_user.id,
)
.await?;
if recent_resets_count >= 3 {
Err(LemmyErrorType::PasswordResetLimitReached)?
}
let site_view = SiteView::read_local(&mut context.pool()).await?;
check_email_verified(&local_user_view, &site_view)?;
// Email the pure token to the user.
send_password_reset_email(&local_user_view, &mut context.pool(), context.settings()).await?;

View file

@ -1,48 +1,69 @@
use actix_web::web::{Data, Json};
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
person::SaveUserSettings,
utils::send_verification_email,
request::replace_image,
utils::{
get_url_blocklist,
local_site_to_slur_regex,
process_markdown_opt,
proxy_image_link_opt_api,
send_verification_email,
},
SuccessResponse,
};
use lemmy_db_schema::{
source::{
actor_language::LocalUserLanguage,
local_user::{LocalUser, LocalUserUpdateForm},
local_user_vote_display_mode::{LocalUserVoteDisplayMode, LocalUserVoteDisplayModeUpdateForm},
person::{Person, PersonUpdateForm},
},
traits::Crud,
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url},
utils::{diesel_string_update, diesel_url_update},
};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::{
error::{LemmyError, LemmyErrorType},
error::{LemmyErrorType, LemmyResult},
utils::validation::{is_valid_bio_field, is_valid_display_name, is_valid_matrix_id},
};
use std::ops::Deref;
#[tracing::instrument(skip(context))]
pub async fn save_user_settings(
data: Json<SaveUserSettings>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?;
let bio = diesel_option_overwrite(data.bio.clone());
let display_name = diesel_option_overwrite(data.display_name.clone());
let matrix_user_id = diesel_option_overwrite(data.matrix_user_id.clone());
let slur_regex = local_site_to_slur_regex(&site_view.local_site);
let url_blocklist = get_url_blocklist(&context).await?;
let bio = diesel_string_update(
process_markdown_opt(&data.bio, &slur_regex, &url_blocklist, &context)
.await?
.as_deref(),
);
let avatar = diesel_url_update(data.avatar.as_deref())?;
replace_image(&avatar, &local_user_view.person.avatar, &context).await?;
let avatar = proxy_image_link_opt_api(avatar, &context).await?;
let banner = diesel_url_update(data.banner.as_deref())?;
replace_image(&banner, &local_user_view.person.banner, &context).await?;
let banner = proxy_image_link_opt_api(banner, &context).await?;
let display_name = diesel_string_update(data.display_name.as_deref());
let matrix_user_id = diesel_string_update(data.matrix_user_id.as_deref());
let email_deref = data.email.as_deref().map(str::to_lowercase);
let email = diesel_option_overwrite(email_deref.clone());
let email = diesel_string_update(email_deref.as_deref());
if let Some(Some(email)) = &email {
let previous_email = local_user_view.local_user.email.clone().unwrap_or_default();
// if email was changed, check that it is not taken and send verification mail
if &previous_email != email {
if LocalUser::is_email_taken(&mut context.pool(), email).await? {
return Err(LemmyErrorType::EmailAlreadyExists)?;
}
if previous_email.deref() != email {
LocalUser::check_is_email_taken(&mut context.pool(), email).await?;
send_verification_email(
&local_user_view,
email,
@ -53,7 +74,8 @@ pub async fn save_user_settings(
}
}
// When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value
// When the site requires email, make sure email is not Some(None). IE, an overwrite to a None
// value
if let Some(email) = &email {
if email.is_none() && site_view.local_site.require_email_verification {
Err(LemmyErrorType::EmailRequired)?
@ -78,7 +100,8 @@ pub async fn save_user_settings(
let local_user_id = local_user_view.local_user.id;
let person_id = local_user_view.person.id;
let default_listing_type = data.default_listing_type;
let default_sort_type = data.default_sort_type;
let default_post_sort_type = data.default_post_sort_type;
let default_comment_sort_type = data.default_comment_sort_type;
let person_form = PersonUpdateForm {
display_name,
@ -107,10 +130,9 @@ pub async fn save_user_settings(
send_notifications_to_email: data.send_notifications_to_email,
show_nsfw: data.show_nsfw,
blur_nsfw: data.blur_nsfw,
auto_expand: data.auto_expand,
show_bot_accounts: data.show_bot_accounts,
show_scores: data.show_scores,
default_sort_type,
default_post_sort_type,
default_comment_sort_type,
default_listing_type,
theme: data.theme.clone(),
interface_language: data.interface_language.clone(),
@ -123,11 +145,17 @@ pub async fn save_user_settings(
..Default::default()
};
// Ignore errors, because 'no fields updated' will return an error.
// https://github.com/LemmyNet/lemmy/issues/4076
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form)
.await
.ok();
LocalUser::update(&mut context.pool(), local_user_id, &local_user_form).await?;
// 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()))
}

View file

@ -4,12 +4,9 @@ use lemmy_api_common::{
context::LemmyContext,
person::{UpdateTotp, UpdateTotpResponse},
};
use lemmy_db_schema::{
source::local_user::{LocalUser, LocalUserUpdateForm},
traits::Crud,
};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::LemmyError;
use lemmy_db_schema::source::local_user::{LocalUser, LocalUserUpdateForm};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyResult;
/// Enable or disable two-factor-authentication. The current setting is determined from
/// [LocalUser.totp_2fa_enabled].
@ -24,13 +21,11 @@ pub async fn update_totp(
data: Json<UpdateTotp>,
local_user_view: LocalUserView,
context: Data<LemmyContext>,
) -> Result<Json<UpdateTotpResponse>, LemmyError> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
) -> LemmyResult<Json<UpdateTotpResponse>> {
check_totp_2fa_valid(
&local_user_view,
&Some(data.totp_token.clone()),
&site_view.site.name,
&context.settings().hostname,
)?;
// toggle the 2fa setting

View file

@ -4,7 +4,7 @@ use actix_web::{
HttpRequest,
};
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
use lemmy_utils::error::{LemmyError, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
/// 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.
@ -12,7 +12,7 @@ use lemmy_utils::error::{LemmyError, LemmyErrorType};
pub async fn validate_auth(
req: HttpRequest,
context: Data<LemmyContext>,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
let jwt = read_auth_token(&req)?;
if let Some(jwt) = jwt {
local_user_view_from_jwt(&jwt, &context).await?;

View file

@ -5,17 +5,12 @@ use lemmy_api_common::{
utils::send_new_applicant_email_to_admins,
SuccessResponse,
};
use lemmy_db_schema::{
source::{
use lemmy_db_schema::source::{
email_verification::EmailVerification,
local_user::{LocalUser, LocalUserUpdateForm},
person::Person,
},
traits::Crud,
RegistrationMode,
};
use lemmy_db_views::structs::SiteView;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_utils::error::LemmyResult;
pub async fn verify_email(
data: Json<VerifyEmail>,
@ -23,9 +18,7 @@ pub async fn verify_email(
) -> LemmyResult<Json<SuccessResponse>> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
let token = data.token.clone();
let verification = EmailVerification::read_for_token(&mut context.pool(), &token)
.await
.with_lemmy_type(LemmyErrorType::TokenNotFound)?;
let verification = EmailVerification::read_for_token(&mut context.pool(), &token).await?;
let form = LocalUserUpdateForm {
// necessary in case this is a new signup
@ -36,16 +29,19 @@ pub async fn verify_email(
};
let local_user_id = verification.local_user_id;
let local_user = LocalUser::update(&mut context.pool(), local_user_id, &form).await?;
LocalUser::update(&mut context.pool(), local_user_id, &form).await?;
EmailVerification::delete_old_tokens_for_local_user(&mut context.pool(), local_user_id).await?;
// send out notification about registration application to admins if enabled
if site_view.local_site.registration_mode == RegistrationMode::RequireApplication
&& site_view.local_site.application_email_admins
{
let person = Person::read(&mut context.pool(), local_user.person_id).await?;
send_new_applicant_email_to_admins(&person.name, &mut context.pool(), context.settings())
if site_view.local_site.application_email_admins {
let local_user = LocalUserView::read(&mut context.pool(), local_user_id).await?;
send_new_applicant_email_to_admins(
&local_user.person.name,
&mut context.pool(),
context.settings(),
)
.await?;
}

View file

@ -16,14 +16,14 @@ use lemmy_db_schema::{
PostFeatureType,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn feature_post(
data: Json<FeaturePost>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PostResponse>, LemmyError> {
) -> LemmyResult<Json<PostResponse>> {
let post_id = data.post_id;
let orig_post = Post::read(&mut context.pool(), post_id).await?;
@ -70,11 +70,5 @@ pub async fn feature_post(
)
.await?;
build_post_response(
&context,
orig_post.community_id,
&local_user_view.person,
post_id,
)
.await
build_post_response(&context, orig_post.community_id, local_user_view, post_id).await
}

View file

@ -1,17 +1,25 @@
use actix_web::web::{Data, Json};
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
context::LemmyContext,
post::{GetSiteMetadata, GetSiteMetadataResponse},
request::fetch_site_metadata,
request::fetch_link_metadata,
};
use lemmy_utils::error::LemmyError;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{LemmyErrorExt, LemmyResult},
LemmyErrorType,
};
use url::Url;
#[tracing::instrument(skip(context))]
pub async fn get_link_metadata(
data: Json<GetSiteMetadata>,
data: Query<GetSiteMetadata>,
context: Data<LemmyContext>,
) -> Result<Json<GetSiteMetadataResponse>, LemmyError> {
let metadata = fetch_site_metadata(context.client(), &data.url).await?;
// Require an account for this API
_local_user_view: LocalUserView,
) -> LemmyResult<Json<GetSiteMetadataResponse>> {
let url = Url::parse(&data.url).with_lemmy_type(LemmyErrorType::InvalidUrl)?;
let metadata = fetch_link_metadata(&url, &context).await?;
Ok(Json(GetSiteMetadataResponse { metadata }))
}

View file

@ -0,0 +1,34 @@
use actix_web::web::{Data, Json};
use lemmy_api_common::{context::LemmyContext, post::HidePost, SuccessResponse};
use lemmy_db_schema::source::post::PostHide;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS};
use std::collections::HashSet;
#[tracing::instrument(skip(context))]
pub async fn hide_post(
data: Json<HidePost>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<SuccessResponse>> {
let post_ids = HashSet::from_iter(data.post_ids.clone());
if post_ids.len() > MAX_API_PARAM_ELEMENTS {
Err(LemmyErrorType::TooManyItems)?;
}
let person_id = local_user_view.person.id;
// Mark the post as hidden / unhidden
if data.hide {
PostHide::hide(&mut context.pool(), post_ids, person_id)
.await
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
} else {
PostHide::unhide(&mut context.pool(), post_ids, person_id)
.await
.with_lemmy_type(LemmyErrorType::CouldntHidePost)?;
}
Ok(Json(SuccessResponse::default()))
}

View file

@ -21,7 +21,7 @@ use lemmy_db_schema::{
traits::{Crud, Likeable},
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use std::ops::Deref;
#[tracing::instrument(skip(context))]
@ -29,7 +29,7 @@ pub async fn like_post(
data: Json<CreatePostLike>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PostResponse>, LemmyError> {
) -> LemmyResult<Json<PostResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
// Don't do a downvote if site has downvotes disabled
@ -66,25 +66,20 @@ pub async fn like_post(
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
}
// Mark the post as read
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
let community = Community::read(&mut context.pool(), post.community_id).await?;
ActivityChannel::submit_activity(
SendActivityData::LikePostOrComment(
post.ap_id,
local_user_view.person.clone(),
Community::read(&mut context.pool(), post.community_id).await?,
data.score,
),
SendActivityData::LikePostOrComment {
object_id: post.ap_id,
actor: local_user_view.person.clone(),
community,
score: data.score,
},
&context,
)
.await?;
build_post_response(
context.deref(),
post.community_id,
&local_user_view.person,
post_id,
)
.await
build_post_response(context.deref(), post.community_id, local_user_view, post_id).await
}

View file

@ -0,0 +1,30 @@
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
context::LemmyContext,
post::{ListPostLikes, ListPostLikesResponse},
utils::is_mod_or_admin,
};
use lemmy_db_schema::{source::post::Post, traits::Crud};
use lemmy_db_views::structs::{LocalUserView, VoteView};
use lemmy_utils::error::LemmyResult;
/// Lists likes for a post
#[tracing::instrument(skip(context))]
pub async fn list_post_likes(
data: Query<ListPostLikes>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<ListPostLikesResponse>> {
let post = Post::read(&mut context.pool(), data.post_id).await?;
is_mod_or_admin(
&mut context.pool(),
&local_user_view.person,
post.community_id,
)
.await?;
let post_likes =
VoteView::list_for_post(&mut context.pool(), data.post_id, data.page, data.limit).await?;
Ok(Json(ListPostLikesResponse { post_likes }))
}

View file

@ -15,14 +15,14 @@ use lemmy_db_schema::{
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn lock_post(
data: Json<LockPost>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PostResponse>, LemmyError> {
) -> LemmyResult<Json<PostResponse>> {
let post_id = data.post_id;
let orig_post = Post::read(&mut context.pool(), post_id).await?;
@ -61,11 +61,5 @@ pub async fn lock_post(
)
.await?;
build_post_response(
&context,
orig_post.community_id,
&local_user_view.person,
post_id,
)
.await
build_post_response(&context, orig_post.community_id, local_user_view, post_id).await
}

View file

@ -2,7 +2,7 @@ use actix_web::web::{Data, Json};
use lemmy_api_common::{context::LemmyContext, post::MarkPostAsRead, SuccessResponse};
use lemmy_db_schema::source::post::PostRead;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType, MAX_API_PARAM_ELEMENTS};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult, MAX_API_PARAM_ELEMENTS};
use std::collections::HashSet;
#[tracing::instrument(skip(context))]
@ -10,15 +10,8 @@ pub async fn mark_post_as_read(
data: Json<MarkPostAsRead>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<SuccessResponse>, LemmyError> {
let mut post_ids = HashSet::new();
if let Some(post_ids_) = &data.post_ids {
post_ids.extend(post_ids_.iter().cloned());
}
if let Some(post_id) = data.post_id {
post_ids.insert(post_id);
}
) -> LemmyResult<Json<SuccessResponse>> {
let post_ids = HashSet::from_iter(data.post_ids.clone());
if post_ids.len() > MAX_API_PARAM_ELEMENTS {
Err(LemmyErrorType::TooManyItems)?;

View file

@ -1,6 +1,8 @@
pub mod feature;
pub mod get_link_metadata;
pub mod hide;
pub mod like;
pub mod list_post_likes;
pub mod lock;
pub mod mark_read;
pub mod save;

View file

@ -9,14 +9,14 @@ use lemmy_db_schema::{
traits::Saveable,
};
use lemmy_db_views::structs::{LocalUserView, PostView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn save_post(
data: Json<SavePost>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PostResponse>, LemmyError> {
) -> LemmyResult<Json<PostResponse>> {
let post_saved_form = PostSavedForm {
post_id: data.post_id,
person_id: local_user_view.person.id,
@ -34,9 +34,14 @@ pub async fn save_post(
let post_id = data.post_id;
let person_id = local_user_view.person.id;
let post_view = PostView::read(&mut context.pool(), post_id, Some(person_id), false).await?;
let post_view = PostView::read(
&mut context.pool(),
post_id,
Some(&local_user_view.local_user),
false,
)
.await?;
// Mark the post as read
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
Ok(Json(PostResponse { post_view }))

View file

@ -5,7 +5,11 @@ use lemmy_api_common::{
context::LemmyContext,
post::{CreatePostReport, PostReportResponse},
send_activity::{ActivityChannel, SendActivityData},
utils::{check_community_user_action, send_new_report_email_to_admins},
utils::{
check_community_user_action,
check_post_deleted_or_removed,
send_new_report_email_to_admins,
},
};
use lemmy_db_schema::{
source::{
@ -15,7 +19,7 @@ use lemmy_db_schema::{
traits::Reportable,
};
use lemmy_db_views::structs::{LocalUserView, PostReportView, PostView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
/// Creates a post report and notifies the moderators of the community
#[tracing::instrument(skip(context))]
@ -23,7 +27,7 @@ pub async fn create_post_report(
data: Json<CreatePostReport>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PostReportResponse>, LemmyError> {
) -> LemmyResult<Json<PostReportResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let reason = data.reason.trim().to_string();
@ -40,6 +44,8 @@ pub async fn create_post_report(
)
.await?;
check_post_deleted_or_removed(&post_view.post)?;
let report_form = PostReportForm {
creator_id: person_id,
post_id,
@ -67,12 +73,12 @@ pub async fn create_post_report(
}
ActivityChannel::submit_activity(
SendActivityData::CreateReport(
post_view.post.ap_id.inner().clone(),
local_user_view.person,
post_view.community,
data.reason.clone(),
),
SendActivityData::CreateReport {
object_id: post_view.post.ap_id.inner().clone(),
actor: local_user_view.person,
community: post_view.community,
reason: data.reason.clone(),
},
&context,
)
.await?;

View file

@ -5,7 +5,7 @@ use lemmy_api_common::{
utils::check_community_mod_of_any_or_admin_action,
};
use lemmy_db_views::{post_report_view::PostReportQuery, structs::LocalUserView};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
/// Lists post reports for a community if an id is supplied
/// or returns all post reports for communities a user moderates
@ -14,8 +14,9 @@ pub async fn list_post_reports(
data: Query<ListPostReports>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<ListPostReportsResponse>, LemmyError> {
) -> LemmyResult<Json<ListPostReportsResponse>> {
let community_id = data.community_id;
let post_id = data.post_id;
let unresolved_only = data.unresolved_only.unwrap_or_default();
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
@ -24,6 +25,7 @@ pub async fn list_post_reports(
let limit = data.limit;
let post_reports = PostReportQuery {
community_id,
post_id,
unresolved_only,
page,
limit,

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{source::post_report::PostReport, traits::Reportable};
use lemmy_db_views::structs::{LocalUserView, PostReportView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
/// Resolves or unresolves a post report and notifies the moderators of the community
#[tracing::instrument(skip(context))]
@ -14,7 +14,7 @@ pub async fn resolve_post_report(
data: Json<ResolvePostReport>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PostReportResponse>, LemmyError> {
) -> LemmyResult<Json<PostReportResponse>> {
let report_id = data.report_id;
let person_id = local_user_view.person.id;
let report = PostReportView::read(&mut context.pool(), report_id, person_id).await?;
@ -23,7 +23,7 @@ pub async fn resolve_post_report(
check_community_mod_action(
&local_user_view.person,
report.community.id,
false,
true,
&mut context.pool(),
)
.await?;

View file

@ -8,14 +8,14 @@ use lemmy_db_schema::{
traits::Crud,
};
use lemmy_db_views::structs::{LocalUserView, PrivateMessageView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn mark_pm_as_read(
data: Json<MarkPrivateMessageAsRead>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PrivateMessageResponse>, LemmyError> {
) -> LemmyResult<Json<PrivateMessageResponse>> {
// Checking permissions
let private_message_id = data.private_message_id;
let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;

View file

@ -14,14 +14,14 @@ use lemmy_db_schema::{
traits::{Crud, Reportable},
};
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn create_pm_report(
data: Json<CreatePrivateMessageReport>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PrivateMessageReportResponse>, LemmyError> {
) -> LemmyResult<Json<PrivateMessageReportResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
let reason = data.reason.trim().to_string();
@ -31,6 +31,11 @@ pub async fn create_pm_report(
let private_message_id = data.private_message_id;
let private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;
// Make sure that only the recipient of the private message can create a report
if person_id != private_message.recipient_id {
Err(LemmyErrorType::CouldntCreateReport)?
}
let report_form = PrivateMessageReportForm {
creator_id: person_id,
private_message_id,

View file

@ -8,14 +8,14 @@ use lemmy_db_views::{
private_message_report_view::PrivateMessageReportQuery,
structs::LocalUserView,
};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn list_pm_reports(
data: Query<ListPrivateMessageReports>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<ListPrivateMessageReportsResponse>, LemmyError> {
) -> LemmyResult<Json<ListPrivateMessageReportsResponse>> {
is_admin(&local_user_view)?;
let unresolved_only = data.unresolved_only.unwrap_or_default();

View file

@ -6,14 +6,14 @@ use lemmy_api_common::{
};
use lemmy_db_schema::{source::private_message_report::PrivateMessageReport, traits::Reportable};
use lemmy_db_views::structs::{LocalUserView, PrivateMessageReportView};
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn resolve_pm_report(
data: Json<ResolvePrivateMessageReport>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<PrivateMessageReportResponse>, LemmyError> {
) -> LemmyResult<Json<PrivateMessageReportResponse>> {
is_admin(&local_user_view)?;
let report_id = data.report_id;

View file

@ -9,16 +9,20 @@ use lemmy_db_schema::{
traits::Blockable,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
#[tracing::instrument(skip(context))]
pub async fn block_instance(
data: Json<BlockInstance>,
local_user_view: LocalUserView,
context: Data<LemmyContext>,
) -> Result<Json<BlockInstanceResponse>, LemmyError> {
) -> LemmyResult<Json<BlockInstanceResponse>> {
let instance_id = data.instance_id;
let person_id = local_user_view.person.id;
if local_user_view.person.instance_id == instance_id {
return Err(LemmyErrorType::CantBlockLocalInstance)?;
}
let instance_block_form = InstanceBlockForm {
person_id,
instance_id,

View file

@ -5,12 +5,12 @@ use lemmy_api_common::{
utils::build_federated_instances,
};
use lemmy_db_views::structs::SiteView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn get_federated_instances(
context: Data<LemmyContext>,
) -> Result<Json<GetFederatedInstancesResponse>, LemmyError> {
) -> LemmyResult<Json<GetFederatedInstancesResponse>> {
let site_view = SiteView::read_local(&mut context.pool()).await?;
let federated_instances =
build_federated_instances(&site_view.local_site, &mut context.pool()).await?;

View file

@ -4,24 +4,26 @@ use lemmy_db_schema::{
source::{
actor_language::SiteLanguage,
language::Language,
local_site_url_blocklist::LocalSiteUrlBlocklist,
local_user::{LocalUser, LocalUserUpdateForm},
moderator::{ModAdd, ModAddForm},
oauth_provider::OAuthProvider,
tagline::Tagline,
},
traits::Crud,
};
use lemmy_db_views::structs::{CustomEmojiView, LocalUserView, SiteView};
use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_db_views_actor::structs::PersonView;
use lemmy_utils::{
error::{LemmyError, LemmyErrorType},
version,
error::{LemmyErrorType, LemmyResult},
VERSION,
};
#[tracing::instrument(skip(context))]
pub async fn leave_admin(
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<GetSiteResponse>, LemmyError> {
) -> LemmyResult<Json<GetSiteResponse>> {
is_admin(&local_user_view)?;
// Make sure there isn't just one admin (so if one leaves, there will still be one left)
@ -59,18 +61,22 @@ pub async fn leave_admin(
let all_languages = Language::read_all(&mut context.pool()).await?;
let discussion_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
let taglines = Tagline::get_all(&mut context.pool(), site_view.local_site.id).await?;
let custom_emojis =
CustomEmojiView::get_all(&mut context.pool(), site_view.local_site.id).await?;
let oauth_providers = OAuthProvider::get_all_public(&mut context.pool()).await?;
let blocked_urls = LocalSiteUrlBlocklist::get_all(&mut context.pool()).await?;
let tagline = Tagline::get_random(&mut context.pool()).await?;
Ok(Json(GetSiteResponse {
site_view,
admins,
version: version::VERSION.to_string(),
version: VERSION.to_string(),
my_user: None,
all_languages,
discussion_languages,
taglines,
custom_emojis,
oauth_providers: Some(oauth_providers),
admin_oauth_providers: None,
blocked_urls,
tagline,
taglines: vec![],
custom_emojis: vec![],
}))
}

View file

@ -0,0 +1,23 @@
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
context::LemmyContext,
person::{ListMedia, ListMediaResponse},
utils::is_admin,
};
use lemmy_db_views::structs::{LocalImageView, LocalUserView};
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn list_all_media(
data: Query<ListMedia>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<ListMediaResponse>> {
// Only let admins view all media
is_admin(&local_user_view)?;
let page = data.page;
let limit = data.limit;
let images = LocalImageView::get_all(&mut context.pool(), page, limit).await?;
Ok(Json(ListMediaResponse { images }))
}

View file

@ -1,6 +1,7 @@
pub mod block;
pub mod federated_instances;
pub mod leave_admin;
pub mod list_all_media;
pub mod mod_log;
pub mod purge;
pub mod registration_applications;

View file

@ -24,7 +24,7 @@ use lemmy_db_views_moderator::structs::{
ModTransferCommunityView,
ModlogListParams,
};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
use ModlogActionType::*;
#[tracing::instrument(skip(context))]
@ -32,7 +32,7 @@ pub async fn get_mod_log(
data: Query<GetModlog>,
context: Data<LemmyContext>,
local_user_view: Option<LocalUserView>,
) -> Result<Json<GetModlogResponse>, LemmyError> {
) -> LemmyResult<Json<GetModlogResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
check_private_instance(&local_user_view, &local_site)?;
@ -55,10 +55,15 @@ pub async fn get_mod_log(
data.mod_person_id
};
let other_person_id = data.other_person_id;
let post_id = data.post_id;
let comment_id = data.comment_id;
let params = ModlogListParams {
community_id,
mod_person_id,
other_person_id,
post_id,
comment_id,
page: data.page,
limit: data.limit,
hide_modlog_names,

View file

@ -1,6 +1,8 @@
use actix_web::web::{Data, Json};
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
send_activity::{ActivityChannel, SendActivityData},
site::PurgeComment,
utils::is_admin,
SuccessResponse,
@ -8,28 +10,42 @@ use lemmy_api_common::{
use lemmy_db_schema::{
source::{
comment::Comment,
local_user::LocalUser,
moderator::{AdminPurgeComment, AdminPurgeCommentForm},
},
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_db_views::structs::{CommentView, LocalUserView};
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn purge_comment(
data: Json<PurgeComment>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
// Only let admin purge an item
is_admin(&local_user_view)?;
let comment_id = data.comment_id;
// Read the comment to get the post_id
let comment = Comment::read(&mut context.pool(), comment_id).await?;
// Read the comment to get the post_id and community
let comment_view = CommentView::read(
&mut context.pool(),
comment_id,
Some(&local_user_view.local_user),
)
.await?;
let post_id = comment.post_id;
// Also check that you're a higher admin
LocalUser::is_higher_admin_check(
&mut context.pool(),
local_user_view.person.id,
vec![comment_view.creator.id],
)
.await?;
let post_id = comment_view.comment.post_id;
// TODO read comments for pictrs images and purge them
@ -41,8 +57,18 @@ pub async fn purge_comment(
reason: data.reason.clone(),
post_id,
};
AdminPurgeComment::create(&mut context.pool(), &form).await?;
ActivityChannel::submit_activity(
SendActivityData::RemoveComment {
comment: comment_view.comment,
moderator: local_user_view.person.clone(),
community: comment_view.community,
reason: data.reason.clone(),
},
&context,
)
.await?;
Ok(Json(SuccessResponse::default()))
}

View file

@ -1,54 +1,82 @@
use actix_web::web::{Data, Json};
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
request::purge_image_from_pictrs,
send_activity::{ActivityChannel, SendActivityData},
site::PurgeCommunity,
utils::{is_admin, purge_image_posts_for_community},
SuccessResponse,
};
use lemmy_db_schema::{
newtypes::PersonId,
source::{
community::Community,
local_user::LocalUser,
moderator::{AdminPurgeCommunity, AdminPurgeCommunityForm},
},
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn purge_community(
data: Json<PurgeCommunity>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
// Only let admin purge an item
is_admin(&local_user_view)?;
let community_id = data.community_id;
// Read the community to get its images
let community = Community::read(&mut context.pool(), community_id).await?;
let community = Community::read(&mut context.pool(), data.community_id).await?;
if let Some(banner) = community.banner {
purge_image_from_pictrs(&banner, &context).await.ok();
// Also check that you're a higher admin than all the mods
let community_mod_person_ids =
CommunityModeratorView::for_community(&mut context.pool(), community.id)
.await?
.iter()
.map(|cmv| cmv.moderator.id)
.collect::<Vec<PersonId>>();
LocalUser::is_higher_admin_check(
&mut context.pool(),
local_user_view.person.id,
community_mod_person_ids,
)
.await?;
if let Some(banner) = &community.banner {
purge_image_from_pictrs(banner, &context).await.ok();
}
if let Some(icon) = community.icon {
purge_image_from_pictrs(&icon, &context).await.ok();
if let Some(icon) = &community.icon {
purge_image_from_pictrs(icon, &context).await.ok();
}
purge_image_posts_for_community(community_id, &context).await?;
purge_image_posts_for_community(data.community_id, &context).await?;
Community::delete(&mut context.pool(), community_id).await?;
Community::delete(&mut context.pool(), data.community_id).await?;
// Mod tables
let form = AdminPurgeCommunityForm {
admin_person_id: local_user_view.person.id,
reason: data.reason.clone(),
};
AdminPurgeCommunity::create(&mut context.pool(), &form).await?;
ActivityChannel::submit_activity(
SendActivityData::RemoveCommunity {
moderator: local_user_view.person.clone(),
community,
reason: data.reason.clone(),
removed: true,
},
&context,
)
.await?;
Ok(Json(SuccessResponse::default()))
}

View file

@ -1,51 +1,61 @@
use actix_web::web::{Data, Json};
use crate::ban_nonlocal_user_from_local_communities;
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
request::delete_image_from_pictrs,
send_activity::{ActivityChannel, SendActivityData},
site::PurgePerson,
utils::is_admin,
utils::{is_admin, purge_user_account},
SuccessResponse,
};
use lemmy_db_schema::{
source::{
image_upload::ImageUpload,
local_user::LocalUser,
moderator::{AdminPurgePerson, AdminPurgePersonForm},
person::{Person, PersonUpdateForm},
},
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn purge_person(
data: Json<PurgePerson>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
// Only let admin purge an item
is_admin(&local_user_view)?;
// Read the person to get their images
let person_id = data.person_id;
// Also check that you're a higher admin
LocalUser::is_higher_admin_check(
&mut context.pool(),
local_user_view.person.id,
vec![data.person_id],
)
.await?;
if let Ok(local_user) = LocalUserView::read_person(&mut context.pool(), person_id).await {
let pictrs_uploads =
ImageUpload::get_all_by_local_user_id(&mut context.pool(), &local_user.local_user.id).await?;
let person = Person::read(&mut context.pool(), data.person_id).await?;
for upload in pictrs_uploads {
delete_image_from_pictrs(&upload.pictrs_alias, &upload.pictrs_delete_token, &context)
.await
.ok();
}
}
ban_nonlocal_user_from_local_communities(
&local_user_view,
&person,
true,
&data.reason,
&Some(true),
&None,
&context,
)
.await?;
// Clear profile data.
Person::delete_account(&mut context.pool(), person_id).await?;
purge_user_account(data.person_id, &context).await?;
// Keep person record, but mark as banned to prevent login or refetching from home instance.
Person::update(
let person = Person::update(
&mut context.pool(),
person_id,
data.person_id,
&PersonUpdateForm {
banned: Some(true),
..Default::default()
@ -58,8 +68,20 @@ pub async fn purge_person(
admin_person_id: local_user_view.person.id,
reason: data.reason.clone(),
};
AdminPurgePerson::create(&mut context.pool(), &form).await?;
ActivityChannel::submit_activity(
SendActivityData::BanFromSite {
moderator: local_user_view.person,
banned_user: person,
reason: data.reason.clone(),
remove_or_restore_data: Some(true),
ban: true,
expires: None,
},
&context,
)
.await?;
Ok(Json(SuccessResponse::default()))
}

View file

@ -1,56 +1,73 @@
use actix_web::web::{Data, Json};
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
request::purge_image_from_pictrs,
send_activity::{ActivityChannel, SendActivityData},
site::PurgePost,
utils::is_admin,
SuccessResponse,
};
use lemmy_db_schema::{
source::{
local_user::LocalUser,
moderator::{AdminPurgePost, AdminPurgePostForm},
post::Post,
},
traits::Crud,
};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
#[tracing::instrument(skip(context))]
pub async fn purge_post(
data: Json<PurgePost>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<SuccessResponse>, LemmyError> {
) -> LemmyResult<Json<SuccessResponse>> {
// Only let admin purge an item
is_admin(&local_user_view)?;
let post_id = data.post_id;
// Read the post to get the community_id
let post = Post::read(&mut context.pool(), post_id).await?;
let post = Post::read(&mut context.pool(), data.post_id).await?;
// Also check that you're a higher admin
LocalUser::is_higher_admin_check(
&mut context.pool(),
local_user_view.person.id,
vec![post.creator_id],
)
.await?;
// Purge image
if let Some(url) = post.url {
purge_image_from_pictrs(&url, &context).await.ok();
if let Some(url) = &post.url {
purge_image_from_pictrs(url, &context).await.ok();
}
// Purge thumbnail
if let Some(thumbnail_url) = post.thumbnail_url {
purge_image_from_pictrs(&thumbnail_url, &context).await.ok();
if let Some(thumbnail_url) = &post.thumbnail_url {
purge_image_from_pictrs(thumbnail_url, &context).await.ok();
}
let community_id = post.community_id;
Post::delete(&mut context.pool(), post_id).await?;
Post::delete(&mut context.pool(), data.post_id).await?;
// Mod tables
let form = AdminPurgePostForm {
admin_person_id: local_user_view.person.id,
reason: data.reason.clone(),
community_id,
community_id: post.community_id,
};
AdminPurgePost::create(&mut context.pool(), &form).await?;
ActivityChannel::submit_activity(
SendActivityData::RemovePost {
post,
moderator: local_user_view.person.clone(),
reason: data.reason.clone(),
removed: true,
},
&context,
)
.await?;
Ok(Json(SuccessResponse::default()))
}

View file

@ -1,4 +1,5 @@
use actix_web::web::{Data, Json};
use activitypub_federation::config::Data;
use actix_web::web::Json;
use lemmy_api_common::{
context::LemmyContext,
site::{ApproveRegistrationApplication, RegistrationApplicationResponse},
@ -10,48 +11,60 @@ use lemmy_db_schema::{
registration_application::{RegistrationApplication, RegistrationApplicationUpdateForm},
},
traits::Crud,
utils::diesel_option_overwrite,
utils::{diesel_string_update, get_conn},
};
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::{LemmyError, LemmyResult};
pub async fn approve_registration_application(
data: Json<ApproveRegistrationApplication>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<RegistrationApplicationResponse>, LemmyError> {
) -> LemmyResult<Json<RegistrationApplicationResponse>> {
let app_id = data.id;
// Only let admins do this
is_admin(&local_user_view)?;
let pool = &mut context.pool();
let conn = &mut get_conn(pool).await?;
let tx_data = data.clone();
let approved_user_id = conn
.build_transaction()
.run(|conn| {
Box::pin(async move {
// Update the registration with reason, admin_id
let deny_reason = diesel_option_overwrite(data.deny_reason.clone());
let deny_reason = diesel_string_update(tx_data.deny_reason.as_deref());
let app_form = RegistrationApplicationUpdateForm {
admin_id: Some(Some(local_user_view.person.id)),
deny_reason,
};
let registration_application =
RegistrationApplication::update(&mut context.pool(), app_id, &app_form).await?;
RegistrationApplication::update(&mut conn.into(), app_id, &app_form).await?;
// Update the local_user row
let local_user_form = LocalUserUpdateForm {
accepted_application: Some(data.approve),
accepted_application: Some(tx_data.approve),
..Default::default()
};
let approved_user_id = registration_application.local_user_id;
LocalUser::update(&mut context.pool(), approved_user_id, &local_user_form).await?;
LocalUser::update(&mut conn.into(), approved_user_id, &local_user_form).await?;
Ok::<_, LemmyError>(approved_user_id)
}) as _
})
.await?;
if data.approve {
let approved_local_user_view =
LocalUserView::read(&mut context.pool(), approved_user_id).await?;
if approved_local_user_view.local_user.email.is_some() {
// Email sending may fail, but this won't revert the application approval
send_application_approved_email(&approved_local_user_view, context.settings()).await?;
}
}
};
// Read the view
let registration_application =

View file

@ -0,0 +1,26 @@
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
context::LemmyContext,
site::{GetRegistrationApplication, RegistrationApplicationResponse},
utils::is_admin,
};
use lemmy_db_views::structs::{LocalUserView, RegistrationApplicationView};
use lemmy_utils::error::LemmyResult;
/// Lists registration applications, filterable by undenied only.
pub async fn get_registration_application(
data: Query<GetRegistrationApplication>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> LemmyResult<Json<RegistrationApplicationResponse>> {
// Make sure user is an admin
is_admin(&local_user_view)?;
// Read the view
let registration_application =
RegistrationApplicationView::read_by_person(&mut context.pool(), data.person_id).await?;
Ok(Json(RegistrationApplicationResponse {
registration_application,
}))
}

View file

@ -1,4 +1,5 @@
use actix_web::web::{Data, Json, Query};
use activitypub_federation::config::Data;
use actix_web::web::{Json, Query};
use lemmy_api_common::{
context::LemmyContext,
site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
@ -9,14 +10,14 @@ use lemmy_db_views::{
registration_application_view::RegistrationApplicationQuery,
structs::LocalUserView,
};
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::LemmyResult;
/// Lists registration applications, filterable by undenied only.
pub async fn list_registration_applications(
data: Query<ListRegistrationApplications>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<ListRegistrationApplicationsResponse>, LemmyError> {
) -> LemmyResult<Json<ListRegistrationApplicationsResponse>> {
let local_site = LocalSite::read(&mut context.pool()).await?;
// Make sure user is an admin

Some files were not shown because too many files have changed in this diff Show more