Compare commits

...
Sign in to create a new pull request.

330 commits

Author SHA1 Message Date
Henry Jameson
6165e2f342 Merge branch 'mfm-center' into shigusegubu-new 2026-06-01 22:18:24 +03:00
Henry Jameson
86dd9663fc allow <center> (used by mfm) 2026-06-01 22:06:45 +03:00
lain
aa16f40f09 Merge pull request 'Prepared FE E2E Docker image' (#7884) from docker-e2e-image into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7884
2026-05-25 12:33:27 +00:00
lain
f99ce5b2e6 Merge pull request 'Support paged featured collections' (#7912) from issue-7887-featured-collection into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7912
2026-05-25 12:07:56 +00:00
Lain Soykaf
e023ed7a5a
Seed E2E admin before API readiness 2026-05-25 16:06:21 +04:00
Lain Soykaf
54369c93d7
Use E2E approval config key 2026-05-25 16:05:21 +04:00
Lain Soykaf
8a410bf49f
Quote E2E entrypoint variables 2026-05-25 16:04:53 +04:00
lain
e46365bf2a Merge pull request 'Woodpecker CI: Build armv7 Docker images' (#7882) from ci/build-armv7-docker-images into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7882
2026-05-25 11:34:27 +00:00
lain
33fdc59bc1 Merge branch 'develop' into issue-7887-featured-collection 2026-05-25 11:24:10 +00:00
lain
b1d72e16a3 Merge branch 'develop' into ci/build-armv7-docker-images 2026-05-25 11:21:23 +00:00
lain
c8c1f7d38b Merge pull request 'Fix Websockets streaming API pushing old already federated posts to TL' (#7907) from websockets-necroposts into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7907
2026-05-25 10:25:36 +00:00
Lain Soykaf
cdb0f103a8
Tighten rich media backfill stream test 2026-05-25 14:08:07 +04:00
lain
619db0adca Merge pull request 'Restore streaming WebSocket protocol tokens' (#7908) from issue-3298-websocket-protocol into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7908
2026-05-25 08:06:52 +00:00
Lain Soykaf
4c9fc6287e
Remove unreachable WebSocket protocol match 2026-05-25 09:26:07 +04:00
Phantasm
a5e7145c97 Merge pull request 'Woodpecker CI: Do not trigger on Weblate MRs' (#7903) from phnt/pleroma:weblate-no-ci into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7903
2026-05-24 21:47:18 +00:00
Phantasm
0c0d687330
credo, lint 2026-05-24 23:41:59 +02:00
Phantasm
6ee40cb2eb
RichMedia: Add StatusView backfill streaming tests 2026-05-24 23:41:59 +02:00
Phantasm
ff7927e219
changelog 2026-05-24 23:41:59 +02:00
Phantasm
5f55c9653c
RichMedia: Disable websockets backfill streaming in StatusView 2026-05-24 23:41:59 +02:00
Phantasm
678fe8a064
RichMedia: Add support for disabling wss streaming out on backfill 2026-05-24 23:41:58 +02:00
Phantasm
94a28d1286
RichMedia Backfill: Add cachex positive test 2026-05-24 23:41:58 +02:00
lain
1a83b1d28d Merge pull request 'reorganize mfm attributes in order they're listed in misskey repo, add missing ones' (#7904) from mfm-extended into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7904
2026-05-23 05:54:48 +00:00
hj
cda6738309 Update priv/scrubbers/default.ex 2026-05-22 20:47:52 +00:00
Lain Soykaf
b054c2aa42
Fix paged featured collection fetches 2026-05-22 22:11:37 +04:00
Lain Soykaf
9dd02ecd50
Fix WebSocket protocol token handshakes 2026-05-22 16:30:14 +04:00
Henry Jameson
49486a4e64 Merge branch 'mfm-extended' into shigusegubu-new 2026-05-18 21:08:45 +03:00
Henry Jameson
46a2808690 extraneous commas 2026-05-18 21:08:24 +03:00
Henry Jameson
c428cf43e8 "changelog" 2026-05-18 21:07:12 +03:00
Henry Jameson
1a13ec539b reorganize mfm attributes in order they're listed in misskey repo, add missing ones 2026-05-18 21:05:46 +03:00
Phantasm
7575b7abc2
Woodpecker CI: Do not trigger on Weblate MRs 2026-05-14 12:34:27 +02:00
lain
093b156c65 Merge pull request 'Switch Phoenix back to upstream' (#7803) from gitlab-mr-iid-4426 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7803
2026-05-14 09:21:36 +00:00
lain
c7c453ca21 Merge branch 'develop' into gitlab-mr-iid-4426 2026-05-14 06:56:57 +00:00
lain
0cf221ba13 Merge pull request 'Translations update from Pleroma Weblate' (#7866) from weblate into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7866
2026-05-14 06:47:36 +00:00
lain
c5737898f5 Merge pull request 'Ensure only requests with Host header set to target instance pass through HTTP signatures.' (#7893) from phnt/pleroma:host-verification into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7893
2026-05-14 06:44:56 +00:00
lain
b90ac6b9c7 Merge branch 'develop' into host-verification 2026-05-14 06:01:31 +00:00
lain
5b63307f85 Merge pull request 'Use upstream remote_ip package' (#7900) from issue-3314-remote-ip-hex into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7900
2026-05-14 05:47:32 +00:00
Lain Soykaf
512b0f67a4
Merge remote-tracking branch 'origin/develop' into issue-3314-remote-ip-hex
# Conflicts:
#	mix.exs
2026-05-14 09:27:18 +04:00
Codimp
d7a0d97c36 Translated using Weblate (French)
Currently translated at 10.8% (106 of 974 strings)

Translation: Pleroma/Pleroma Backend (domain config_descriptions)
Translate-URL: https://translate.pleroma.social/projects/pleroma/pleroma-backend-domain-config_descriptions/fr/
2026-05-13 20:08:05 +00:00
Codimp
086c15b5cd Translated using Weblate (French)
Currently translated at 100.0% (80 of 80 strings)

Translation: Pleroma/Pleroma Backend (domain errors)
Translate-URL: https://translate.pleroma.social/projects/pleroma/pleroma-backend-domain-errors/fr/
2026-05-13 20:08:05 +00:00
Neko Nekowazarashi
4fef910303 Added translation using Weblate (Indonesian) 2026-05-13 20:08:05 +00:00
Neko Nekowazarashi
526365364b Added translation using Weblate (Indonesian) 2026-05-13 20:08:05 +00:00
Neko Nekowazarashi
7fff19cbe4 Translated using Weblate (Indonesian)
Currently translated at 77.8% (74 of 95 strings)

Translation: Pleroma/Pleroma Backend (domain errors)
Translate-URL: https://translate.pleroma.social/projects/pleroma/pleroma-backend-domain-errors/id/
2026-05-13 20:08:05 +00:00
Irrlicht
143f426e84 Translated using Weblate (French)
Currently translated at 10.8% (106 of 974 strings)

Translation: Pleroma/Pleroma Backend (domain config_descriptions)
Translate-URL: https://translate.pleroma.social/projects/pleroma/pleroma-backend-domain-config_descriptions/fr/
2026-05-13 20:08:05 +00:00
lain
e4ad3ab322 Merge pull request 'Use pleroma_captcha Hex package' (#7901) from issue-7678-captcha-hex-1-0-3 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7901
2026-05-13 19:31:23 +00:00
Lain Soykaf
9ae1249ccb
Remove captcha dependency shape test 2026-05-13 23:09:29 +04:00
Lain Soykaf
7ab9e2c7ce
Use pleroma_captcha Hex package 2026-05-13 23:05:25 +04:00
Lain Soykaf
c92d233233
Use upstream remote_ip package 2026-05-13 20:04:12 +04:00
lain
2db3a9c04d Merge pull request 'Use Hex releases for majic and oban_plugins_lazarus' (#7899) from issue-7686-majic-1-2-0 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7899
2026-05-13 15:32:09 +00:00
Lain Soykaf
d0c2d04356
Use Hex release for oban_plugins_lazarus 2026-05-13 19:10:15 +04:00
Lain Soykaf
d8e3ea69b1
Use Hex release for majic 1.2.0 2026-05-13 19:00:16 +04:00
Lain Soykaf
9e16332d9d
Update majic to 1.2.0 2026-05-13 18:39:40 +04:00
Phantasm
4810d2536e
ActivityPubController: Use valid signatures in Host header test 2026-05-13 12:16:27 +02:00
lain
47ca427497 Merge pull request 'Better user search' (#7793) from gitlab-mr-iid-4416 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7793
2026-05-13 09:53:52 +00:00
lain
ffff2098f0 Merge pull request 'Signatures: Only true is true.' (#7892) from bump/http-signatures-0.1.3 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7892
2026-05-13 06:09:06 +00:00
lain
e211b72924 Merge pull request 'Reject third-party remote reports' (#7896) from fix/reject-third-party-reports into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7896
2026-05-13 05:57:34 +00:00
Lain Soykaf
68e4bb53a2
Merge branch 'develop' into fix/reject-third-party-reports 2026-05-13 08:49:20 +04:00
nicole mikołajczyk
d8e9affded Merge pull request 'Handle reports with just actor ap id as the object' (#7897) from mkljczk/pleroma:iceshrimpnet-reports-fix into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7897
2026-05-12 23:08:23 +00:00
nicole mikołajczyk
4d3aea1fce Handle reports with just actor ap id as the object
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-05-13 00:56:56 +02:00
Phantasm
2b3ac2d7fe
lint 2026-05-13 00:44:33 +02:00
Phantasm
95eef879d7
ActivityPubController: add mismatched host test 2026-05-13 00:40:53 +02:00
Phantasm
c19bdf3814
SignatureRetryWorker: add mismatched host test, fix tests 2026-05-13 00:33:09 +02:00
Phantasm
95b15190de
ActivityPubController: require validated host header 2026-05-13 00:32:57 +02:00
Phantasm
6c2d8209c9
SignatureRetryWorker: require validated host header 2026-05-13 00:32:54 +02:00
Phantasm
6f415cf3fc
EnsureHostMatchesPlug: Remove match against default scheme port
Checking against the default port of the Endpoint URL scheme is
redundant as normal instances will have the combination https/443
by default created by pleroma.instance gen, Tor-only instances should
have combination http/80 and local testing instances httt/XXXX.

The default scheme port doesn't add anything usefull in these configs.
2026-05-12 23:31:55 +02:00
Lain Soykaf
0cf865f025
Reject third-party remote reports 2026-05-12 23:50:30 +04:00
Phantasm
35b5447f3f
EnsureHostMatchesPlug: Add more tests 2026-05-12 17:02:28 +02:00
Phantasm
90e390e45b
fix tests 2026-05-12 16:50:35 +02:00
Phantasm
d6d0ce7260
EnsureHostMatchesPlug: Add tests 2026-05-12 16:50:35 +02:00
Phantasm
ea886dc36b
EnsureHostMatchesPlug: Ensure Host header matches instance URI 2026-05-12 16:50:28 +02:00
lain
9b331d648b Merge branch 'develop' into bump/http-signatures-0.1.3 2026-05-12 06:33:52 +00:00
Lain Soykaf
71afba4825
Signature: Treat HTTP signature errors as invalid 2026-05-12 08:52:42 +04:00
Lain Soykaf
8a56cf5c0f
Clarify websocket token precedence test 2026-05-11 22:19:45 +04:00
lain
8e72f4cd17 Merge branch 'develop' into gitlab-mr-iid-4426 2026-05-11 18:16:12 +00:00
lain
960c730706 Merge pull request 'Update http_signatures to 0.1.3' (#7891) from bump/http-signatures-0.1.3 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7891
2026-05-11 18:15:21 +00:00
Lain Soykaf
ab9fd33762
Fix Phoenix upstream migration regressions 2026-05-11 22:14:01 +04:00
Lain Soykaf
216a00f73f
Merge develop into Phoenix upstream migration 2026-05-11 22:13:46 +04:00
Lain Soykaf
7f4890b6a9
Add changelog for http_signatures update 2026-05-11 21:12:34 +04:00
Lain Soykaf
61feb3dfcd
Update http_signatures to 0.1.3 2026-05-11 21:01:59 +04:00
lain
f579dc099c Merge pull request 'Use published Pleroma MFM parser package' (#7890) from lambadalambda/pleroma:mfm-backend-hex into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7890
2026-05-11 15:35:12 +00:00
Lain Soykaf
592be493c8
Use published Pleroma MFM parser package 2026-05-11 19:13:01 +04:00
Henry Jameson
6ff1d10e22 Merge remote-tracking branch 'origin/develop' into shigusegubu-new 2026-05-11 16:31:28 +03:00
lain
e1b2e788d9 Merge pull request 'Add backend MFM support' (#7889) from lambadalambda/pleroma:mfm-backend into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7889
2026-05-11 13:30:03 +00:00
Lain Soykaf
47021b5aba
Fix MFM validator alias ordering 2026-05-11 16:37:59 +04:00
Lain Soykaf
c780298ce7
Add changelog for MFM support 2026-05-11 15:05:00 +04:00
Lain Soykaf
6b86e31e5d
Add backend MFM support 2026-05-11 14:53:06 +04:00
Phantasm
ebcc7684c1 Merge pull request 'Update Pleroma-FE build artifacts URL' (#7885) from phnt/pleroma:fe-build-link into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7885
2026-05-06 13:56:10 +00:00
Phantasm
4873991983 Update Pleroma-FE build artifacts URL 2026-05-06 13:55:45 +00:00
lain
2082bf729a Merge pull request 'poll_view: try to read votersCount first, and then manually count local voters.' (#7883) from Yonle/pleroma:pf1 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7883
2026-05-06 09:55:03 +00:00
Lain Soykaf
c62d191986
Add changelog for votersCount inflation fix 2026-05-06 11:48:20 +04:00
lain
f1249d830f Merge branch 'develop' into pf1 2026-05-06 07:45:50 +00:00
Lain Soykaf
727e9e7749
Fix votersCount inflation in multiple-choice polls
increase_vote_count/3 was incrementing votersCount on every vote
activity, causing inflation when a single voter picks multiple options.
Now only increments when the actor is a new unique voter, and preserves
existing votersCount otherwise.

Also adds is_integer guard to voters_count/1 to handle nil safely, and
adds tests for the voters_count clause ordering and edge cases.
2026-05-06 11:33:34 +04:00
Phantasm
3c63877e61
Add custom Docker image for FE E2E tests
Woodpecker cannot do custom commands for services.
2026-05-05 21:34:10 +02:00
lain
86a5213523 Merge pull request 'litepub-0.1.jsonld cleanup' (#7871) from mkljczk/pleroma:context-cleanup into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7871
2026-05-05 08:22:29 +00:00
Yonle
aec0deef8b
poll_view: try to read votersCount first, and then manually count local voters. 2026-05-05 13:50:11 +07:00
Lain Soykaf
394db0dce0
Woodpecker CI: Target armv7 Docker runner 2026-05-04 10:09:44 +04:00
Lain Soykaf
684e9ef247
Woodpecker CI: Isolate armv7 Docker builds 2026-05-04 09:41:06 +04:00
Lain Soykaf
af175fbdfc
Woodpecker CI: Build armv7 Docker images 2026-05-03 21:19:24 +04:00
lain
4230887d7e Merge pull request 'Prepare 2.10.2 release' (#7880) from release/2.10.2 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7880
2026-05-03 16:27:21 +00:00
Lain Soykaf
8ccdd98914
Prepare 2.10.2 release 2026-05-03 20:23:35 +04:00
Lain Soykaf
78aef1b875
Merge branch 'stable' of ssh://git.pleroma.com/pleroma/pleroma into develop 2026-05-03 20:19:40 +04:00
Lain Soykaf
78a41dfdcd
Merge branch 'update-spoofing' of ssh://git.pleroma.social:22/pleroma-secteam/pleroma into develop 2026-05-03 20:07:00 +04:00
Lain Soykaf
621d86a31d
Validate WebFinger nicknames against actors 2026-05-03 18:02:59 +04:00
lain
2c7095d300 Merge pull request 'Prepare 2.10.1 release' (#7879) from release/2.10.1 into stable
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7879
2026-05-03 13:18:52 +00:00
Lain Soykaf
6553ba24aa
Prepare 2.10.1 release 2026-05-03 16:56:01 +04:00
Lain Soykaf
1a8d585cbf
Woodpecker CI: Allow rerunning OTP package uploads 2026-05-03 12:10:23 +04:00
Lain Soykaf
6ae02d71bd
Align inbox controller tests with signer mapping 2026-05-03 10:33:42 +04:00
Lain Soykaf
00dd1b5103
Add failed-signature retry regression tests 2026-05-03 10:19:33 +04:00
Lain Soykaf
4acd8c4e72
Log failed-signature retry rejections 2026-05-02 21:08:04 +04:00
Lain Soykaf
50651284a2
Woodpecker CI: Run generic workflows on amd64 2026-05-02 17:21:37 +04:00
Lain Soykaf
9fdad779b5
Woodpecker CI: Run Docker manifest combine on amd64 2026-05-02 16:12:47 +04:00
Lain Soykaf
a1f7413832
Merge branch 'develop' of https://git.pleroma.social/pleroma/pleroma into update-spoofing 2026-05-02 16:10:33 +04:00
Lain Soykaf
ee18feef7c
Woodpecker CI: Allow manual develop release runs 2026-05-02 15:04:40 +04:00
lain
93c155e4fa Merge pull request 'woodpecker-releases' (#7878) from woodpecker-releases into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7878
2026-05-02 10:46:15 +00:00
Lain Soykaf
47e6dbfade
Woodpecker CI: Work around script entrypoint truncation 2026-05-02 13:38:56 +04:00
Lain Soykaf
da9cbc8e2f
Merge origin/develop into woodpecker-releases 2026-05-02 12:47:13 +04:00
Lain Soykaf
3dbc570471
Woodpecker CI: Publish update-compatible OTP releases 2026-05-02 11:57:04 +04:00
Lain Soykaf
a35aa6551e
Fix Woodpecker path filters 2026-05-02 10:39:49 +04:00
Lain Soykaf
99b614a52e
Add spoofing fixes changelog entry 2026-05-01 23:06:16 +04:00
Lain Soykaf
4337e0eb1b
Fail closed on unresolved signed payloads
Reject unknown remote Update targets and invalidate signed payloads when their signer identity cannot be mapped, avoiding crashes and fail-open signature state.
2026-05-01 12:33:26 +04:00
Lain Soykaf
7756f491d5
Split failed-signature inbox retries
Route failed-signature ActivityPub inbox retries through a dedicated worker so legacy and malformed retry jobs fail closed before processing.
2026-05-01 08:43:42 +04:00
Lain Soykaf
bd45704dba
Clarify cross-domain spoofing regressions 2026-04-30 17:21:40 +04:00
Lain Soykaf
9c540995b4
Use Mox in spoofing regression tests 2026-04-30 15:36:55 +04:00
Lain Soykaf
80e72b79f5
Add spoofing regression tests 2026-04-30 14:31:06 +04:00
Phantasm
42683e79df
ReceiverWorker: Check that signature matches actor 2026-04-30 01:37:34 +02:00
Phantasm
da28a4c441
ReceiverWorker: Add cancels on actor does not match signature test 2026-04-30 01:37:33 +02:00
Phantasm
af6d12c0a5
UpdateValidator: Check Actor owns Object or updates itself 2026-04-30 01:36:58 +02:00
Phantasm
cb2271978e
UpdateValidator: fix tests 2026-04-30 00:17:59 +02:00
Henry Jameson
db7bca8945 Merge remote-tracking branch 'origin/develop' into shigusegubu-new 2026-04-29 11:00:00 +03:00
Phantasm
e4632eced3
Woodpecker CI: Only run stable release pipelines on tag events
Removes possible races when uploading images/bundles and purposeful
pipeline failures when both a push and tag happened (OTP bundles do not
allow overwriting).
2026-04-25 13:40:53 +02:00
Phantasm
a996d25b84
Woodpecker CI Docker: label workflow as high memory 2026-04-25 11:08:28 +02:00
Phantasm
25e543d44d
changelog 2026-04-24 23:38:29 +02:00
Phantasm
cafd75b072
Woodpecker CI docker-combine: Hoist docker_settings anchor 2026-04-24 23:15:00 +02:00
Phantasm
95a33855d1
pleroma_ctl: Update update logic to Gitea API 2026-04-24 22:02:01 +02:00
Phantasm
7f97e21910
pleroma_ctl: Properly handle user arguments with whitespace
When user supplied arguments to pleroma_ctl include whitespace
that has been properly quoted, all arguments were sent to
ReleaseTasks in one string, which then String.split/1 the input on any
whitespace. This broke Mix tasks that accept certain user input like
instance gen and user management.

To fix this, pleroma_ctl now sends the arguments in list
form. Additionally pleroma_ctl arguments now need to be pre-processed.

Fixes pleroma/pleroma#7874
2026-04-24 18:04:31 +02:00
Phantasm
209b9c0a1e
Woodpecker CI: Shorten zip archive names further
Hopefully this will also help with the workflows randomly failing to
create the zip archive due to the Woodpecker bug.
2026-04-23 17:06:31 +02:00
Phantasm
16b7a95c48
Woodpecker CI: Run Docker image workflows also on Dockerfile changes 2026-04-23 17:06:31 +02:00
Phantasm
2e968890de
Woodpecker CI: Remove branch requirement for tag
Tag events don't have CI_COMMIT_BRANCH set, and neither can they be
restricted to specific branches. The branch condition is ignored on
tags.
2026-04-23 17:06:31 +02:00
Phantasm
5229e8ae65
Woodpecker CI: Unify OTP builds into a single worfklow 2026-04-23 17:06:29 +02:00
Phantasm
d8b8cbbb8d
Woodpecker CI: Shorten commit sha to eight chars
This will hopefully help with avoiding:
https://github.com/woodpecker-ci/woodpecker/issues/5450
2026-04-23 17:05:27 +02:00
Phantasm
89a78d765c
Woodpecker CI: Unify Docker image workflows 2026-04-23 17:05:27 +02:00
Phantasm
dd29b9c11b
Woodpecker CI OTP: use CI_COMMIT_BRANCH variable instead of stable 2026-04-23 17:05:27 +02:00
Phantasm
eea01b54b7
Woodpecker CI: Allow running stable release jobs manually
Also allows Docker images to be tagged with a version in manual jobs
when CI_COMMIT_TAG is manually specified
2026-04-23 17:05:27 +02:00
Phantasm
42eb9706a5
Woodpecker CI: Build stable OTP releases 2026-04-23 17:05:27 +02:00
Phantasm
97a2e8c764
Woodpecker CI: Tag stable docker release with version tag 2026-04-23 17:05:27 +02:00
Phantasm
e002650e23
Woodpecker CI: Add Docker stable releases 2026-04-23 17:05:26 +02:00
Phantasm
f00c13602d
Woodpecker CI Develop: Also tag images using commit sha
With the commit sha being present, `tags` now has to be a list instead
of an array, otherwise Woodpecker raises a yaml compiler warning:

yaml: line 17: did not find expected ',' or ']'
2026-04-23 17:05:26 +02:00
Phantasm
13d6246ed9
Woodpecker CI: Cleanup develop releases CI code duplication 2026-04-23 17:05:24 +02:00
Phantasm
67e7f788c9
Woodpecker CI Docker Develop combine: Switch to plugin
Replaces manual tagging handling with a plugin, mostly to avoid dealing
with echoed out secrets in the job log, which should be censored
automatically, but who knows when that breaks...
2026-04-23 17:01:41 +02:00
Phantasm
e2adc796c4
Woodpecker CI: Multiplatform Docker image manifests 2026-04-23 17:01:40 +02:00
Phantasm
d2f7c9252f
Woodpecker CI Docker develop: Switch to kaniko 2026-04-23 17:01:40 +02:00
Phantasm
5351cd4ce9
Woodpecker CI: Add OTP develop pipeline
musl and glibc builds need to be split due to workspace polution.
Workflow's steps share the same workspace, so two separate build steps
can't be in the same workflow since they share the buld artifacts, deps.
2026-04-23 17:01:25 +02:00
Phantasm
fc5aea73ff
Woodpecker CI: Add develop Docker image build pipeline 2026-04-23 16:56:21 +02:00
nicole mikołajczyk
c2fb145c5f litepub-0.1.jsonld cleanup
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-04-10 21:33:20 +02:00
feld
683ab39160 Merge pull request 'Downgrade Hackney' (#7860) from hackney-downgrade into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7860
2026-04-08 19:29:50 +00:00
Mark Felder
7582b71f46 Downgrade Hackney to 1.20.1, before connection performance regressions
It appears the implementation of Happy Eyeballs in 1.22.0 is the origin of
some pretty serious performance regressions that remain even in the latest
Hackney 3.0 branch.

Connection tests:

=== 1.22.0 ===
First call:  9434ms
Second call: 14ms

=== 1.21.0 ===
First call:  228ms
Second call: 16ms

We went back further to 1.20.1 though because of reported problems with the mail
client and ssl_options. That bug was not reproduced by a dev, though, but we'll
trust it for now.
2026-04-08 12:27:47 -07:00
feld
ebfa0d88df Merge pull request 'Update Bandit' (#7868) from bandit into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7868
2026-04-07 16:12:52 +00:00
Mark Felder
00265751cc Update Bandit 2026-04-03 13:22:12 -07:00
feld
a3404e91bc Merge pull request 'DigestEmailsWorker: Change Oban queue to "background"' (#7865) from phnt/oban-digest-queue into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7865
2026-04-01 19:08:40 +00:00
Mark Felder
01ced6bea2 Fix the daily email digest job which was not executing 2026-04-01 11:59:23 -07:00
Phantasm
1405f5dc8b Merge pull request 'PR Woodpecker CI workflow' (#7825) from phnt/pleroma:woodpecker-pr-ci into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7825
2026-03-31 14:51:42 +00:00
Phantasm
fd7b809c54 Woodpecker CI: Only run lint and unit tests when relevant files changed 2026-03-31 14:32:52 +00:00
Phantasm
096c4ea980 Woodpecker CI: Run lint and unit tests also on push to default branch 2026-03-31 14:32:52 +00:00
Phantasm
072dc39d83 Woodpecker CI: Don't depend on changelog in lint workflow 2026-03-31 14:32:52 +00:00
Phantasm
7bba485397 Woodpecker CI: Disable cycles lint step for now since it always fails 2026-03-31 14:32:52 +00:00
Phantasm
1fe0970b64 woodpecker CI: Fix cycles in lint workflow 2026-03-31 14:32:52 +00:00
Phantasm
08bf6c8fed Woodpecker CI: Explicitely exit with non-zero exit code on fail 2026-03-31 14:32:52 +00:00
Phantasm
cdcc432f31 Woodpecker CI: Lint workflow, don't use brackets in shell tests 2026-03-31 14:32:52 +00:00
Phantasm
b0de9bd3cd Woodpecker CI: Make xref use fail stamp 2026-03-31 14:32:52 +00:00
Phantasm
56a25202b9 Woodpecker CI: Fix credo 2026-03-31 14:32:52 +00:00
Phantasm
265d3eeebc Woodpecker CI: Fix syntax error in lint workflow 2026-03-31 14:32:52 +00:00
Phantasm
b224a2dacc Woodpecker CI: Don't immediately fail whole lint workflow with one error 2026-03-31 14:32:52 +00:00
Phantasm
8640fcef22 Woodpecker CI: Fix compile error on Elixir 1.18 due to wrong OTP 2026-03-31 14:32:52 +00:00
Phantasm
0fd544722f Woodpecker: Ensure correct workflow status in lint pipeline 2026-03-31 14:32:52 +00:00
Phantasm
b67d7c1106 changelog 2026-03-31 14:32:52 +00:00
Phantasm
6f8233d780 Woodpecker CI: Add linting pipeline 2026-03-31 14:32:52 +00:00
Phantasm
2880aac617 Woodpecker CI: Unit test using Elixir 1.15 and 1.18 2026-03-31 14:32:52 +00:00
Phantasm
4493d0d187 Woodpecker CI: Update check-changelog script for Woodpecker 2026-03-31 14:32:52 +00:00
Phantasm
1a0af1c0c0 Woodpecker CI: Add check-changelog workflow 2026-03-31 14:32:52 +00:00
Phantasm
88a349f3ab Woodpecker CI: Retry failed tests using pleroma.test_runner
I didn't add the --cover option, but it would be useless right now
anyway
2026-03-31 14:32:52 +00:00
Phantasm
a9fe2fe4d8 Move main Woodpecker file to own directory 2026-03-31 14:32:52 +00:00
Phantasm
f138423814 Merge pull request 'lint-warnings' (#7867) from phnt/pleroma:lint-warnings into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7867
2026-03-31 14:32:35 +00:00
Phantasm
eb69576154
fix test after embed route got added back 2026-03-31 16:23:21 +02:00
Phantasm
c8baad165b
lint: fix warnings throughout codebase 2026-03-31 16:23:11 +02:00
Phantasm
799199f6b5 DigestEmailsWorker: Change Oban queue to "background"
The mailer queue has been long gone and that left Oban jobs always
stuck in the "available" state that would never execute.
2026-03-26 22:38:26 +00:00
feld
9db47790bb Merge pull request 'reverse_proxy,endpoint,uploaded_media: add immutable cache-control flag' (#7835) from Yonle/pleroma:develop into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7835
2026-03-26 21:28:50 +00:00
nicole mikołajczyk
9e22baa66a Merge pull request 'Federate votersCount correctly' (#7858) from mkljczk/pleroma:poll-voters-count into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7858
2026-03-26 11:55:36 +00:00
nicole mikołajczyk
5aa3c8a06e Federate votersCount correctly
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
Assisted-by: your mother
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-26 12:42:59 +01:00
feld
9af26e5fb5 Merge pull request 'Additional Search Indexing cleanup' (#7864) from search-indexing into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7864
2026-03-25 22:05:55 +00:00
Mark Felder
f06a0eab50 Move object_to_search_data/1 to Pleroma.Search
This standardizes this functionality within the Search module so
it doesn't need to be imported by other search backends from Meilisearch

Also integrate its filtering rules into Search.indexable?/1 for consistency
2026-03-25 14:47:39 -07:00
Mark Felder
ea78e76837 Fix add_to_index/1 to adhere to the typespec 2026-03-25 14:46:38 -07:00
feld
1d819195b6 Merge pull request 'Search: filter indexable activities before inserting Oban jobs' (#7538) from gitlab-mr-iid-4161 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7538
2026-03-25 20:38:15 +00:00
Mark Felder
711b33d81c Fix CommonAPI.favorite/2 arg order 2026-03-25 13:32:25 -07:00
Mark Felder
7cc9ba6f06 Merge remote-tracking branch 'origin/develop' into gitlab-mr-iid-4161 2026-03-25 13:31:07 -07:00
feld
63c9c7ea92 Merge pull request 'Harden rate limiter to deal with configuration issues' (#7795) from gitlab-mr-iid-4418 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7795
2026-03-25 19:55:08 +00:00
feld
d1bd24ba64 Merge pull request 'ReverseProxy: Follow redirects recursively until redirect_limit' (#7812) from gitlab-mr-iid-4435 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7812
2026-03-25 19:53:47 +00:00
feld
106a52eb2e Merge pull request 'Restore embed route' (#7857) from mkljczk/pleroma:restore-embeds into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7857
2026-03-25 19:52:16 +00:00
feld
eabfb2bd47 Merge pull request 'Fix LiveDashboard redirect not working when user added a path segment' (#7830) from live-dashboard-fix-redirect into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7830
2026-03-25 19:49:40 +00:00
feld
876913d2af Merge pull request 'Fix error codes for missing static files' (#7850) from shibao/pleroma:static-fix into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7850
Reviewed-by: Phantasm <phnt@noreply.git.pleroma.social>
2026-03-25 19:49:05 +00:00
feld
93d05efdb1 Merge pull request 'credo: fix ordering of aliases missed in pleroma/pleroma!7841' (#7852) from phnt/pleroma:credo-alias-fixes into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7852
2026-03-25 19:48:02 +00:00
feld
85d311adcf Merge pull request 'No-op code correctness improvements detected by Elixir 1.19 compiler' (#7863) from elixir-1.19-cherrypick into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7863
2026-03-25 19:38:16 +00:00
Mark Felder
cbb715b978 No-op code correctness improvements detected by Elixir 1.19 compiler 2026-03-25 12:36:16 -07:00
feld
dc7bd82968 Merge pull request 'Correct old migrations for expiring activities and user access tokens' (#7862) from fix-old-migrations into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7862
2026-03-25 19:25:18 +00:00
Mark Felder
e1a1e5c726 Correct old migrations for expiring activities and user access tokens. 2026-03-25 12:23:09 -07:00
Mark Felder
f3f72048ac Fix MoveActivityExpirationsToOban migration
This never would have worked correctly.

warning: Pleroma.Workers.PurgeExpiredActivity.enqueue/1 is undefined or private. Did you mean:

      * enqueue/2
2026-03-25 12:21:51 -07:00
Mark Felder
2937bb68b1 Fix MoveTokensExpirationIntoOban migration
Pleroma.Workers.PurgeExpiredToken.enqueue/1 no longer exists, so this migration would fail.

The enqueue/1 function is only used for this migration, so we can just include it in the
migration module directly.
2026-03-25 12:21:13 -07:00
Phantasm
750266f2e3 ActivityDraft: Add missing __MODULE__ matches and drop unneeded ones 2026-03-25 11:16:12 -07:00
Phantasm
645211812e Elixir 1.19 MRFTest: Replace matchable_regexes with regexes_match! func 2026-03-25 11:15:45 -07:00
Phantasm
ee55764501 lint 2026-03-25 11:14:42 -07:00
Phantasm
a9ad6297b7 Elixir 1.19: Fix Mastodon StatusControllerTest DateTime difference 2026-03-25 11:14:38 -07:00
Phantasm
6a3b5b3218 Elixir 1.19: Fix MRFTest regex tests
It is no longer possible to match regexes. Instead at least match that
the sources of the regexes (regexes themselves) are the same.

Notice the +1 Reference number below.

2) test subdomain_match/2 wildcard domains with one subdomain (Pleroma.Web.ActivityPub.MRFTest)
   test/pleroma/web/activity_pub/mrf_test.exs:36
   Assertion with == failed
   code:  assert regexes == [~r/^(.*\.)*unsafe.tld$/i]
   left:  [%Regex{opts: [:caseless], re_pattern: {:re_pattern, 1, 0, 0, #Reference<0.378940835.3277193222.129648>}, source: "^(.*\\.)*unsafe.tld$"}]
   right: [%Regex{opts: [:caseless], re_pattern: {:re_pattern, 1, 0, 0, #Reference<0.378940835.3277193222.129649>}, source: "^(.*\\.)*unsafe.tld$"}]
   stacktrace:
     test/pleroma/web/activity_pub/mrf_test.exs:39: (test)
2026-03-25 11:14:33 -07:00
Phantasm
bf86768e88 Elixir 1.19: Fix ConfigDBTest regex tests
It is not possible match regexes anymore as this worked by accident
previously. Instead, at least check that the sources of the regex (the
regex itself) match.

Notice the +1 difference in the regex Reference below.

1) test to_elixir_types/1 complex keyword with sigil (Pleroma.ConfigDBTest)
   test/pleroma/config_db_test.exs:460
   Assertion with == failed
   code:  assert ConfigDB.to_elixir_types([
            %{"tuple" => [":federated_timeline_removal", []]},
            %{"tuple" => [":reject", ["~r/comp[lL][aA][iI][nN]er/"]]},
            %{"tuple" => [":replace", []]}
          ]) == [federated_timeline_removal: [], reject: [~r/comp[lL][aA][iI][nN]er/], replace: []]
   left:  [federated_timeline_removal: [], reject: [%Regex{opts: [], re_pattern: {:re_pattern, 0, 0, 0, #Reference<0.230935836.591265794.259515>}, source: "comp[lL][aA][iI][nN]er"}], replace: []]
   right: [federated_timeline_removal: [], reject: [%Regex{opts: [], re_pattern: {:re_pattern, 0, 0, 0, #Reference<0.230935836.591265794.259516>}, source: "comp[lL][aA][iI][nN]er"}], replace: []]
   stacktrace:
     test/pleroma/config_db_test.exs:461: (test)
2026-03-25 11:14:28 -07:00
Phantasm
531041041a Elixir 1.19: Fix deprecation warning when invoking ParallelCompiler
warning: you must pass return_diagnostics: true when invoking Kernel.ParallelCompiler functions
  (elixir 1.19.5) lib/kernel/parallel_compiler.ex:324: Kernel.ParallelCompiler.spawn_workers/3
  (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/html.ex:13: Pleroma.HTML.compile_scrubbers/0
  (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/application.ex:48: Pleroma.Application.start/2
  (kernel 10.5) application_master.erl:299: :application_master.start_it_old/4

warning: you must pass return_diagnostics: true when invoking Kernel.ParallelCompiler functions
  (elixir 1.19.5) lib/kernel/parallel_compiler.ex:324: Kernel.ParallelCompiler.spawn_workers/3
  (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/application.ex:121: Pleroma.Application.load_custom_modules/0
  (pleroma 2.10.0-7-ga7a74d5e-elixir-1-19+test) lib/pleroma/application.ex:60: Pleroma.Application.start/2
  (kernel 10.5) application_master.erl:299: :application_master.start_it_old/4
2026-03-25 11:11:38 -07:00
Phantasm
f60a317c2f Elixir 1.19: Only match once on structs
Second match is not needed and a simple Map update is recommended by
the compiler
2026-03-25 11:11:17 -07:00
Phantasm
f4c28392e1 Elixir 1.19: Fix typing violation in MarkerTest
warning: a struct for Pleroma.Marker is expected on struct update:

        %Pleroma.Marker{refresh_record(marker) | unread_count: 2}

    but got type:

        dynamic()

    where "marker" was given the type:

        # type: dynamic()
        # from: test/pleroma/marker_test.exs:35:14
        marker = Pleroma.Factory.insert(:marker, user: user)

    you must assign "refresh_record(marker)" to variable and pattern match on "%Pleroma.Marker{}".

    hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

        user = some_function()
        %User{user | name: "John Doe"}

    it is enough to write:

        %User{} = user = some_function()
        %{user | name: "John Doe"}

    typing violation found at:
    │
 43 │              ) == [%Marker{refresh_record(marker) | unread_count: 2}]
    │                    ~
    │
    └─ test/pleroma/marker_test.exs:43:20: Pleroma.MarkerTest."test get_markers/2 returns user markers"/1
2026-03-25 11:10:48 -07:00
Phantasm
ec294b30c1 Elixir 1.19: Fix typing violation in RepoTest
warning: a struct for Pleroma.Web.OAuth.Token is expected on struct update:

        %Pleroma.Web.OAuth.Token{Pleroma.Factory.insert(:oauth_token) | user: user}

    but got type:

        dynamic()

    you must assign "Pleroma.Factory.insert(:oauth_token)" to variable and pattern match on "%Pleroma.Web.OAuth.Token{}".

    hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

        user = some_function()
        %User{user | name: "John Doe"}

    it is enough to write:

        %User{} = user = some_function()
        %{user | name: "John Doe"}

    typing violation found at:
    │
 27 │       token = %Pleroma.Web.OAuth.Token{insert(:oauth_token) | user: user}
    │               ~
    │
    └─ test/pleroma/repo_test.exs:27:15: Pleroma.RepoTest."test get_assoc/2 get assoc from preloaded data"/1
2026-03-25 11:10:43 -07:00
Phantasm
b8a66c22b3 Elixir 1.19: Fix typing violation in MediaControllerTest
warning: a struct for Plug.Upload is expected on struct update:

         %Plug.Upload{image | filename: "../../../../../nested/file.jpg"}

     but got type:

         dynamic()

     where "image" was given the type:

         # type: dynamic()
         # from: test/pleroma/web/mastodon_api/controllers/media_controller_test.exs:132:42
         %{conn: conn, image: image}

     when defining the variable "image", you must also pattern match on "%Plug.Upload{}".

     hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

         user = some_function()
         %User{user | name: "John Doe"}

     it is enough to write:

         %User{} = user = some_function()
         %{user | name: "John Doe"}

     typing violation found at:
     │
 133 │       image = %Plug.Upload{
     │               ~
     │
     └─ test/pleroma/web/mastodon_api/controllers/media_controller_test.exs:133:15: Pleroma.Web.MastodonAPI.MediaControllerTest."test Upload media Do not allow nested filename"/1
2026-03-25 11:10:37 -07:00
Phantasm
93e8f9d7d1 Elixir 1.19: Fix typing violations in ActivityPubTest 2026-03-25 11:10:13 -07:00
Phantasm
8417629b4b Elixir 1.19: Fix typing violation on struct updates in CommonAPI.Activity*
warning: a struct for Pleroma.Web.CommonAPI.ActivityDraft is expected on struct update:

         %Pleroma.Web.CommonAPI.ActivityDraft{draft | object: object}

     but got type:

         dynamic()

     where "draft" was given the type:

         # type: dynamic()
         # from: lib/pleroma/web/common_api/activity_draft.ex:91:22
         draft

     when defining the variable "draft", you must also pattern match on "%Pleroma.Web.CommonAPI.ActivityDraft{}".

     hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

         user = some_function()
         %User{user | name: "John Doe"}

     it is enough to write:

         %User{} = user = some_function()
         %{user | name: "John Doe"}

     typing violation found at:
     │
 102 │     %__MODULE__{draft | object: object}
     │     ~
     │
     └─ lib/pleroma/web/common_api/activity_draft.ex:102:5: Pleroma.Web.CommonAPI.ActivityDraft.listen_object/1
2026-03-25 11:09:36 -07:00
Phantasm
958d250fe5 Elixir 1.19: Fix typing violation on struct updates in Web.ApiSpec.Rend*
warning: a struct for OpenApiSpex.Cast.Error is expected on struct update:

        %OpenApiSpex.Cast.Error{err | name: err.value}

    but got type:

        dynamic(%{..., name: nil, reason: :invalid_enum})

    where "err" was given the type:

        # type: dynamic(%{..., name: nil, reason: :invalid_enum})
        # from: lib/pleroma/web/api_spec/render_error.ex:20:45
        %{name: nil, reason: :invalid_enum} = err

    when defining the variable "err", you must also pattern match on "%OpenApiSpex.Cast.Error{}".

    hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

        user = some_function()
        %User{user | name: "John Doe"}

    it is enough to write:

        %User{} = user = some_function()
        %{user | name: "John Doe"}

    typing violation found at:
    │
 21 │           %OpenApiSpex.Cast.Error{err | name: err.value}
    │           ~
    │
    └─ lib/pleroma/web/api_spec/render_error.ex:21:11: Pleroma.Web.ApiSpec.RenderError.call/2
2026-03-25 11:09:27 -07:00
Phantasm
19e05b4a7b Elixir 1.19: Fix typing violation on struct updates in Web.ApiSpec.Cast*
warning: a struct for Plug.Conn is expected on struct update:

         %Plug.Conn{conn | query_params: query_params}

     but got type:

         dynamic()

     where "conn" was given the type:

         # type: dynamic()
         # from: lib/pleroma/web/api_spec/cast_and_validate.ex:109:43
         conn

     when defining the variable "conn", you must also pattern match on "%Plug.Conn{}".

     hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

         user = some_function()
         %User{user | name: "John Doe"}

     it is enough to write:

         %User{} = user = some_function()
         %{user | name: "John Doe"}

     typing violation found at:
     │
 133 │         conn = %Conn{conn | query_params: query_params}
     │                ~
     │
     └─ lib/pleroma/web/api_spec/cast_and_validate.ex:133:16: Pleroma.Web.ApiSpec.CastAndValidate.cast_and_validate/6
2026-03-25 11:09:16 -07:00
Phantasm
5b6af83e86 Elixir 1.19: Fix typing violation on struct updates in Pleroma.Upload
warning: a struct for Pleroma.Upload is expected on struct update:

        %Pleroma.Upload{
          upload
          | path:
              case upload.path do
                x when x === false or x === nil ->
                  <<to_string(upload.id)::binary, "/", to_string(upload.name)::binary>>

                x ->
                  x
              end
        }

    but got type:

        dynamic()

    where "upload" was given the type:

        # type: dynamic()
        # from: lib/pleroma/upload.ex:95:24
        {:ok, upload} <- prepare_upload(upload, opts)

    when defining the variable "upload", you must also pattern match on "%Pleroma.Upload{}".

    hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

        user = some_function()
        %User{user | name: "John Doe"}

    it is enough to write:

        %User{} = user = some_function()
        %{user | name: "John Doe"}

    typing violation found at:
    │
 96 │          upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
    │                   ~
    │
    └─ lib/pleroma/upload.ex:96:19: Pleroma.Upload.store/2
2026-03-25 11:09:10 -07:00
Phantasm
1b9cd83d88 Elixir 1.19: Fix typing violation on struct updates in MFA.Changeset
warning: a struct for Pleroma.MFA.Settings is expected on struct update:

        %Pleroma.MFA.Settings{settings | enabled: false}

    but got type:

        dynamic()

    where "settings" was given the type:

        # type: dynamic()
        # from: lib/pleroma/mfa/changeset.ex:11:14
        settings = Pleroma.MFA.fetch_settings(Ecto.Changeset.apply_changes(changeset))

    when defining the variable "settings", you must also pattern match on "%Pleroma.MFA.Settings{}".

    hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

        user = some_function()
        %User{user | name: "John Doe"}

    it is enough to write:

        %User{} = user = some_function()
        %{user | name: "John Doe"}

    typing violation found at:
    │
 17 │       put_change(changeset, %Settings{settings | enabled: false})
    │                             ~
    │
    └─ lib/pleroma/mfa/changeset.ex:17:29: Pleroma.MFA.Changeset.disable/2

---

    warning: a struct for Pleroma.MFA.Settings is expected on struct update:

        %Pleroma.MFA.Settings{
          settings
          | totp: %Pleroma.MFA.Settings.TOTP{confirmed: false, delivery_type: "app", secret: nil}
        }

    but got type:

        dynamic()

    where "settings" was given the type:

        # type: dynamic()
        # from: lib/pleroma/mfa/changeset.ex:23:74
        %Pleroma.User{multi_factor_authentication_settings: settings} = user

    when defining the variable "settings", you must also pattern match on "%Pleroma.MFA.Settings{}".

    hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

        user = some_function()
        %User{user | name: "John Doe"}

    it is enough to write:

        %User{} = user = some_function()
        %{user | name: "John Doe"}

    typing violation found at:
    │
 25 │     |> put_change(%Settings{settings | totp: %Settings.TOTP{}})
    │                   ~
    │
    └─ lib/pleroma/mfa/changeset.ex:25:19: Pleroma.MFA.Changeset.disable_totp/1
2026-03-25 11:09:05 -07:00
Phantasm
dfaabb48ef Elixir 1.19: Fix typing violation on struct updates in Pleroma.Marker
warning: a struct for Pleroma.Marker is expected on struct update:

        %Pleroma.Marker{marker | user: user}

    but got type:

        dynamic()

    where "marker" was given the type:

        # type: dynamic()
        # from: lib/pleroma/marker.ex
        {:ok, marker}

    when defining the variable "marker", you must also pattern match on "%Pleroma.Marker{}".

    hint: given pattern matching is enough to catch typing errors, you may optionally convert the struct update into a map update. For example, instead of:

        user = some_function()
        %User{user | name: "John Doe"}

    it is enough to write:

        %User{} = user = some_function()
        %{user | name: "John Doe"}

    typing violation found at:
    │
 81 │       {:ok, marker} -> %__MODULE__{marker | user: user}
    │                        ~
    │
    └─ lib/pleroma/marker.ex:81:24: Pleroma.Marker.get_marker/2
2026-03-25 11:08:56 -07:00
nicole mikołajczyk
6bbfba7f6e Merge pull request 'Allow fine-grained announce visibilities (ported from Akkoma)' (#7832) from mkljczk/pleroma:boost-visibilities into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7832
2026-03-21 20:45:30 +00:00
nicole mikołajczyk
23cc812366 Restore embed route
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
Assisted-by: your mother
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-19 15:26:29 +01:00
nicole mikołajczyk
d0ef58a59d Merge pull request 'Normalize Hubzilla alsoKnownAs from string to array' (#7821) from phnt/pleroma:normalize-alsoKnownAs into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7821
2026-03-10 12:05:22 +00:00
nicole mikołajczyk
37cb2f9273 Merge pull request 'Avoid code duplication in UserView' (#7817) from mkljczk/pleroma:user-view-repeat into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7817
2026-03-10 12:04:43 +00:00
nicole mikołajczyk
70de4491c2 Merge pull request 'Support lists exclusive param' (#7831) from mkljczk/pleroma:exclusive-lists into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7831
2026-03-10 12:03:22 +00:00
nicole mikołajczyk
d1787966a6 Merge branch 'develop' into exclusive-lists 2026-03-10 12:03:13 +00:00
Phantasm
a0131ff733
credo: fix ordering of aliases missed in pleroma/pleroma!7841 2026-03-08 21:19:49 +01:00
shibao
bceb28b941 add changelog note for missing static files fix 2026-03-08 11:28:21 +00:00
shibao
4e1ba489ec fix 404s for missing static files 2026-03-08 11:28:21 +00:00
Phantasm
d95d7f6eba Merge pull request 'Gopher: Fix crash on (re)boot when ConfigDB is enabled' (#7826) from fix-gopher-crash into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7826
2026-03-08 08:46:54 +00:00
nicole mikołajczyk
0592f111f6 update tests
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 18:10:07 +01:00
nicole mikołajczyk
40bc79e5ce Merge pull request 'Various bookmark folders-related improvements' (#7829) from mkljczk/pleroma:bookmark-folders into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7829
2026-03-06 16:50:30 +00:00
nicole mikołajczyk
87b4e3f3ff Avoid code duplication in UserView
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 17:50:15 +01:00
nicole mikołajczyk
a1bb81bddb Merge pull request 'Don't use the confusing TwitterAPI namespace' (#7841) from mkljczk/pleroma:twitter-api-removal into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7841
2026-03-06 16:24:33 +00:00
nicole mikołajczyk
499b2ed118 remove unused alias
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 17:23:54 +01:00
Phantasm
3760480813 lint 2026-03-05 20:21:19 +00:00
Phantasm
5f321b0b5b Favicon Plug: Halt Plug pipeline when favicon not found 2026-03-05 20:21:19 +00:00
Phantasm
d0db1f00c3 Favicon Plug: assert HTTP 200 status in tests 2026-03-05 20:21:19 +00:00
Phantasm
662c9f36ac Favicon Plug: Update moduledoc and rename to adhere to convention 2026-03-05 20:21:19 +00:00
Phantasm
2388964b14 Favicon Plug: Add tests 2026-03-05 20:21:19 +00:00
Phantasm
d03ae43ee0 Favicon Plug: Simplify and pass when not requesting favicon 2026-03-05 20:21:19 +00:00
Yonle
8975129680 webplug(favicon): remove check on url path. 2026-03-05 20:21:19 +00:00
Yonle
96f252023e constants: remove favicon.png from static_only_files 2026-03-05 20:21:19 +00:00
Yonle
0879dd3950 endpoint: reorder: handle favicon plug first 2026-03-05 20:21:19 +00:00
Yonle
4bc0b26abe changelog.d: add cache-control-immutable 2026-03-05 20:21:19 +00:00
Yonle
8abd25950a endpoint: use favicon plug 2026-03-05 20:21:19 +00:00
Yonle
970e0f9044 endpoint: set cache control for favicon.png
Signed-off-by: Yonle <yonle@proton.me>
2026-03-05 20:21:19 +00:00
Yonle
848b3f5d5b reverse_proxy,endpoint,uploaded_media: add immutable cache-control flag 2026-03-05 20:21:19 +00:00
Phantasm
222306ff27 Merge pull request 'Fix AccountController Plug warning from typo' (#7848) from phnt/pleroma:plug-test-typo into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7848
2026-03-03 22:20:34 +00:00
Phantasm
ca38217898
Fix AccountController Plug warning
the URI path used in plug tests must start with "/", got: "api/v1/blocks"
  (plug 1.19.1) lib/plug/adapters/test/conn.ex:14: Plug.Adapters.Test.Conn.conn/4
  (phoenix 1.7.14) lib/phoenix/test/conn_test.ex:236: Phoenix.ConnTest.dispatch_endpoint/5
  (phoenix 1.7.14) lib/phoenix/test/conn_test.ex:225: Phoenix.ConnTest.dispatch/5
  test/pleroma/web/mastodon_api/controllers/account_controller_test.exs:2099: Pleroma.Web.MastodonAPI.AccountControllerTest."test getting a list of blocks"/1
  (ex_unit 1.19.5) lib/ex_unit/runner.ex:528: ExUnit.Runner.exec_test/2
  (ex_unit 1.19.5) lib/ex_unit/capture_log.ex:121: ExUnit.CaptureLog.with_log/2
  (ex_unit 1.19.5) lib/ex_unit/runner.ex:477: anonymous fn/3 in ExUnit.Runner.maybe_capture_log/3
  (stdlib 7.2) timer.erl:599: :timer.tc/2
  (ex_unit 1.19.5) lib/ex_unit/runner.ex:450: anonymous fn/6 in ExUnit.Runner.spawn_test_monitor/4
2026-03-03 23:11:39 +01:00
nicole mikołajczyk
19025563e2 fixes
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-03 17:32:32 +01:00
nicole mikołajczyk
65c7d0c7b9 Merge pull request 'Update comment for prepare_object, rename prepare_outgoing' (#7818) from mkljczk/pleroma:update-comment into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7818
2026-03-03 12:49:50 +00:00
nicole mikołajczyk
490cd33bc9 Support lists exclusive param
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-03 12:48:37 +00:00
nicole mikołajczyk
8921dbfffd changelog
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-03 12:48:12 +00:00
Oneric
b645643cfb Merge pull request 'Allow fine-grained announce visibilities' (#941) from Oneric/akkoma:announce-visibility into develop
Reviewed-on: https://akkoma.dev/AkkomaGang/akkoma/pulls/941
Reviewed-by: floatingghost <hannah@coffee-and-dreams.uk>
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-03 12:48:12 +00:00
nicole mikołajczyk
68de463392 Merge pull request 'update mix.exs deps versions to match mix.lock so they don't look that scary' (#7839) from mkljczk/pleroma:mix-exs-update into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7839
Reviewed-by: Phantasm <phnt@noreply.git.pleroma.social>
2026-03-02 23:37:01 +00:00
nicole mikołajczyk
1b182b07dc is this what i was meant to do?
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-03 00:23:14 +01:00
nicole mikołajczyk
2086561fbd Various bookmark folders-related improvements
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 23:41:46 +01:00
nicole mikołajczyk
3620726ff3 Merge pull request 'Add sane defaults for :database_config_whitelist, add a task to remove non-whitelisted configs' (#7837) from pleroma-database-config-whitelist into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7837
2026-03-02 22:38:31 +00:00
nicole mikołajczyk
37041aae60 update mix.exs deps versions to match mix.lock so they don't look that scary
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-02 22:50:49 +01:00
nicole mikołajczyk
c3b779036d Merge branch 'develop' into pleroma-database-config-whitelist 2026-03-01 22:44:08 +00:00
nicole mikołajczyk
36a79ab58e Merge pull request 'Add issue and pull request templates for Forgejo' (#7819) from mkljczk/pleroma:forgejo-templates into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7819
Reviewed-by: Phantasm <phnt@noreply.git.pleroma.social>
2026-03-01 22:43:10 +00:00
nicole mikołajczyk
d389359ec3 Merge pull request 'mix.exs: use correct override value' (#7838) from mkljczk/pleroma:mix-exs-fix into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7838
2026-03-01 22:42:17 +00:00
nicole mikołajczyk
6405a2e682 Merge pull request 'Move avatar_description and header_description fields to the account object' (#7828) from mkljczk/pleroma:avatar-description-mastodon-api into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7828
2026-03-01 22:40:01 +00:00
nicole mikołajczyk
38c30d50b4 Merge pull request 'Update docs on scrobbles' (#7836) from mkljczk/pleroma:docs-scrobble into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7836
2026-03-01 22:39:44 +00:00
nicole mikołajczyk
9040f97cea Merge pull request 'Do not use Enum.map for side-effects' (#7840) from mkljczk/pleroma:map-side-effects into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7840
2026-03-01 22:39:26 +00:00
nicole mikołajczyk
120719f28c Don't use the confusing TwitterAPI namespace
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-27 19:53:25 +01:00
nicole mikołajczyk
a9b5a28c26 Do not use Enum.map for side-effects
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-27 16:24:10 +01:00
nicole mikołajczyk
938ee4cb01 mix.exs: use correct override value
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-27 15:45:57 +01:00
mkljczk
c392b21db1 Update docs on scrobbles 2026-02-27 09:22:14 +00:00
Phantasm
588bc656f2 Merge pull request #7751 from gitlab-mr-iid-4374 into develop 2026-02-22 21:56:00 +00:00
Phantasm
ef7be0a1e5 DB prune: Add test for hashtags 2026-02-22 21:46:41 +00:00
Phantasm
e32ab8aef2 DB prune: Check if user follows hashtag with no objects before deletion 2026-02-22 21:46:41 +00:00
Phantasm
95c8b4732f
ReverseProxy Hackney: Add redirect handling logging 2026-02-22 11:27:03 +01:00
Phantasm
cbc2ea3315 typo 2026-02-22 10:05:50 +00:00
Phantasm
23a4d68c97 ReverseProxy: Follow redirects recursively until redirect_limit
Test post: https://possum.city/notes/ahqdvbhu3wug0at2
2026-02-22 10:05:50 +00:00
nicole mikołajczyk
0b950f6253 comment out stuff
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-18 13:37:10 +01:00
Phantasm
699a7e57e8
Fix LiveDashboard redirect not working when user added a path segment
/phoenix/live_dashboard -> /pleroma/live_dashboard would work

/phoenix/live_dashboard/ecto_stats -> /pleroma/live_dashboard/ecto_stats
would not work and instead reply with FE.
2026-02-17 21:10:56 +01:00
nicole mikołajczyk
3d9ac413af Move avatar_description and header_description fields to the account object
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-17 14:00:21 +01:00
Phantasm
eed4f4bba8
Gopher: Fix crash on (re)boot when ConfigDB is enabled
Ranch listener wasn't being properly stopped when the Gopher GenServer
received a shutdown message due Restarter rebooting Pleroma to apply
ConfigDB configuration (originating from
Config.TransferTask.load_and_update_env/2) when ConfigDB is enabled.

Handle by trapping exits in the GenServer, which causes the terminate/2
function to be called and the Ranch listener to be stopped from there.

23:22:29.871 [error] GenServer Restarter.Pleroma terminating
** (MatchError) no match of right hand side value:

    {:error,
     {{:shutdown,
       {:failed_to_start_child, Pleroma.Gopher.Server,
        {{:badmatch, {:error, {:already_started, #PID<0.4801.0>}}},
         [
           {Pleroma.Gopher.Server, :init, 1,
            [file: ~c"lib/pleroma/gopher/server.ex", line: 25]},
           {:gen_server, :init_it, 2, [file: ~c"gen_server.erl", line: 2276]},
           {:gen_server, :init_it, 6, [file: ~c"gen_server.erl", line: 2236]},
           {:proc_lib, :init_p_do_apply, 3, [file: ~c"proc_lib.erl", line: 333]}
         ]}}}, {Pleroma.Application, :start, [:normal, []]}}}

    (restarter 0.1.0) lib/pleroma.ex:104: Restarter.Pleroma.do_restart/1
    (restarter 0.1.0) lib/pleroma.ex:96: Restarter.Pleroma.handle_cast/2
    (stdlib 7.2) gen_server.erl:2460: :gen_server.try_handle_cast/3
    (stdlib 7.2) gen_server.erl:2418: :gen_server.handle_msg/3
    (stdlib 7.2) proc_lib.erl:333: :proc_lib.init_p_do_apply/3
Last message: {:"$gen_cast", {:after_boot, :dev}}
State: %{rebooted: false, need_reboot: false, after_boot: false}
2026-02-17 00:47:57 +01:00
Phantasm
f80c5744b1
Normalize Hubzilla alsoKnownAs from string to array 2026-02-12 18:47:22 +01:00
feld
1c685ea41a Update README.md
fix logo
2026-02-12 00:34:08 +00:00
nicole mikołajczyk
b798f7d6e9 Add issue and pull request templates for Forgejo
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-10 14:53:45 +01:00
nicole mikołajczyk
2e80c786bb Update comment for prepare_object, rename prepare_outgoing
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-10 14:37:30 +01:00
lain
ec6ffa4fdf Merge pull request 'CI: Add basic woodpecker file' (#7816) from woodpecker-ci into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7816
2026-02-09 17:46:19 +00:00
Lain Soykaf
4693dc837b CI: Only run on PR 2026-02-09 10:13:29 +04:00
Lain Soykaf
feda4d0718 CI: Add basic woodpecker file 2026-02-09 09:01:16 +04:00
nicole mikołajczyk
cb78699a3b Merge branch 'instance-profile-fields' into 'develop'
Add /api/v2/instance profile fields limits info used by Mastodon

See merge request pleroma/pleroma!4434
2026-01-30 23:04:49 +01:00
nicole mikołajczyk
833e9829ba Merge branch 'relationship-expires-at' into 'develop'
MastoAPI AccountView: Add mute/block expiry to the relationship object (simplified)

See merge request pleroma/pleroma!4433
2026-01-30 07:08:21 +01:00
nicole mikołajczyk
bd30d461b0 Add /api/v2/instance profile fields limits info used by Mastodon
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-30 07:05:45 +01:00
nicole mikołajczyk
5001fb3a78 Update changelog
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-28 14:02:55 +01:00
nicole mikołajczyk
bc0c7fb310 Fix tests, relationship should always define _expires_at
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-28 13:58:33 +01:00
Phantasm
c1e33bfadb MastoAPI AccountView AccountController: Add more block/mute expiry tests 2026-01-28 13:50:34 +01:00
Phantasm
e7a4d5ea66 MastoAPI AccountView: Add mute/block expiry to the relationship key 2026-01-28 13:50:23 +01:00
nicole mikołajczyk
6fac6ff7f1 MastoAPI AccountView: Add mute/block expiry to the relationship object
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-28 13:49:34 +01:00
nicole mikołajczyk
055242f438 Merge branch 'assign-users' into 'develop'
Allow assigning users to reports

See merge request pleroma/pleroma!3670
2026-01-28 11:22:09 +01:00
nicole mikołajczyk
80ede85f75 Allow assigning users to reports
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-28 10:35:19 +01:00
Henry Jameson
26234c032d Merge branch 'pleroma-mastoapi-account-relationships-3392' into shigusegubu-new 2026-01-26 22:06:34 +02:00
Phantasm
9b5afe9cd4
MastoAPI AccountView AccountController: Add more block/mute expiry tests 2026-01-24 21:35:27 +01:00
Phantasm
56b3db71ff
MastoAPI AccountView: Readd block/mute expiry outside the relationship 2026-01-24 21:33:49 +01:00
Phantasm
ddc1a86f40
MastoAPI AccountView: Add mute/block expiry to the relationship key 2026-01-24 21:32:42 +01:00
Haelwenn (lanodan) Monnier
6f86883cca
Web: remove legacy :set_put_layout plug
Phoenix 1.8 requires a View module with put_layout so can't set it that early.

It was introduced in 2019 with
commit 1097ce6d9f
but nothing seems to provide app.html (anymore?) and it would likely
better be set by something like OAuthController / OAuthView.
2026-01-18 10:40:10 +01:00
Haelwenn (lanodan) Monnier
be327ca982
Switch Phoenix back to upstream
See <https://github.com/phoenixframework/phoenix/pull/6094>
for `:sec_websocket_protocol` -> `:sec_websocket_headers`
2026-01-18 10:40:10 +01:00
Lain Soykaf
a4fb651fac ConfigController: Don't allow whitelist modification. 2026-01-17 13:30:07 +04:00
Lain Soykaf
117b0bd79e Config: Don't crash on falsy whitelist config 2026-01-17 13:03:02 +04:00
Lain Soykaf
49f9ab3034 Cheatsheet: Fix double slash 2026-01-17 13:02:18 +04:00
Lain Soykaf
0b871ff1f2 ConfigController: Don't allow updating the whitelist 2026-01-17 12:32:10 +04:00
Lain Soykaf
77a1d79f92 ConfigTest: Don't crash when whitelist is unset / disabled 2026-01-17 12:31:35 +04:00
nicole mikołajczyk
49985b1614 Update tests
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-16 21:37:02 +01:00
nicole mikołajczyk
92fd157cd8 Update cheatsheet
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-16 21:35:53 +01:00
nicole mikołajczyk
b66b93a94a Add task for filtering non-whitelisted configs
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-16 21:35:22 +01:00
nicole mikołajczyk
f0669997d3 Add test for default whitelist config
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-16 21:34:06 +01:00
nicole mikołajczyk
57a3b1f6d0 Add sane defaults for :database_config_whitelist
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-16 21:33:14 +01:00
nicole mikołajczyk
1af8997462 do not ever allow setting database_config_whitelist to database
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-16 21:32:35 +01:00
Lain Soykaf
b9c281a0c3 Add changelog 2026-01-06 16:42:29 +04:00
Lain Soykaf
bd61916270 ConfigDB, RateLimiter, RateLimit: Use new type to parse and cast rate limits. 2026-01-06 16:34:17 +04:00
Lain Soykaf
47f4bde0ea ConfigDBTest: Add failing test for invalid rate limiter values. 2026-01-06 16:06:51 +04:00
Lain Soykaf
958a4581d6 RateLimiter: Ensure that the rate limiter doesn't crash on bad values 2026-01-06 15:54:06 +04:00
Lain Soykaf
9ede9b92d3 RateLimiterTest: Add failing test for invalid values. 2026-01-06 15:32:01 +04:00
Lain Soykaf
3903f12c78 Add changelog 2026-01-01 10:04:55 +04:00
Lain Soykaf
ee17d6413d Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into better-user-search 2026-01-01 10:03:14 +04:00
Lain Soykaf
71f5a493f3 Search: Better sorting for user searches. 2026-01-01 10:02:37 +04:00
Mark Felder
592955a895 Improve add_to_index/1 when there is no Object 2024-06-26 11:05:25 -04:00
Mark Felder
7c09150cdb Validate the activity is public before indexing
Additional tests added
2024-06-26 10:08:27 -04:00
Mark Felder
77436451ad Indexable changelog 2024-06-26 09:37:34 -04:00
Mark Felder
a5c88eb39b Remove redundant checks from backends' add_to_index/1 2024-06-26 09:37:32 -04:00
Mark Felder
627c944fec Search Indexing: filter indexable activities before inserting Oban jobs 2024-06-26 09:24:01 -04:00
254 changed files with 8794 additions and 1521 deletions

View file

@ -0,0 +1,25 @@
name: 'Bug report'
about: 'Report a bug in Pleroma'
body:
- type: markdown
attributes:
value: |
### Precheck
* For support use https://git.pleroma.social/pleroma/pleroma-support or [community channels](https://git.pleroma.social/pleroma/pleroma#community-channels).
* Please do a quick search to ensure no similar bug has been reported before. If the bug has not been addressed after 2 weeks, it's fine to bump it.
* Try to ensure that the bug is actually related to the Pleroma backend. For example, if a bug happens in Pleroma-FE but not in Mastodon-FE or mobile clients, it's likely that the bug should be filed in [Pleroma-FE](https://git.pleroma.social/pleroma/pleroma-fe/issues/new) repository.
- type: textarea
id: environment
attributes:
label: Environment
value: |
* Installation type (OTP or From Source):
* Pleroma version (could be found in the "Version" tab of settings in Pleroma-FE):
* Elixir version (`elixir -v` for from source installations, N/A for OTP):
* Operating system:
* PostgreSQL version (`psql -V`):
- type: textarea
id: bug-description
attributes:
label: Bug description

View file

@ -0,0 +1,13 @@
### Checklist
- [ ] Adding a changelog: In the `changelog.d` directory, create a file named `<code>.<type>`.
<!--
`<code>` can be anything, but we recommend using a more or less unique identifier to avoid collisions, such as the branch name.
`<type>` can be `add`, `change`, `remove`, `fix`, `security` or `skip`. `skip` is only used if there is no user-visible change in the PR (for example, only editing comments in the code). Otherwise, choose a type that corresponds to your change.
In the file, write the changelog entry. For example, if a PR adds group functionality, we can create a file named `group.add` and write `Add group functionality` in it.
If one changelog entry is not enough, you may add more. But that might mean you can split it into two PRs. Only use more than one changelog entry if you really need to (for example, when one change in the code fix two different bugs, or when refactoring).
-->

3
.gitignore vendored
View file

@ -56,6 +56,9 @@ pleroma.iml
# asdf # asdf
.tool-versions .tool-versions
# mise
mise.toml
# Editor temp files # Editor temp files
*~ *~
*# *#

View file

@ -0,0 +1,20 @@
when:
- event: pull_request
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
labels:
platform: linux/amd64
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
check-changelog:
image: docker.io/alpine:3.23
entrypoint: *script_file_entrypoint
commands:
- apk add --no-cache git
- sh ./tools/check-changelog

View file

@ -0,0 +1,85 @@
when:
# Temporary PR validation while arm/v7 Docker builds are being brought up.
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
- event: tag
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
- event: manual
branch: stable
# Target the CAX21 arm64 host with native arm32 userspace support, not generic arm64 runners.
labels:
platform: linux/arm
armv7: native
variables:
docker_variables: &docker_variables
repo: pleroma/pleroma
registry: git.pleroma.social
armv7_build_settings: &armv7_build_settings
<<: *docker_variables
build_args:
- ELIXIR_IMG=arm32v7/elixir
- ELIXIR_TAG=1.17.3-otp-26-alpine
- ALPINE_IMG=arm32v7/alpine
- ALPINE_VER=3.20
extra_opts: --custom-platform=linux/arm/v7
armv7_push_settings: &armv7_push_settings
<<: *armv7_build_settings
username:
from_secret: pleroma-ci-user
password:
from_secret: pleroma-ci-password
steps:
docker-armv7-pr:
image: woodpeckerci/plugin-kaniko:2.3.1
when:
- event: pull_request
settings:
<<: *armv7_build_settings
dry_run: true
tags:
- armv7-pr-check
docker-develop-armv7:
image: woodpeckerci/plugin-kaniko:2.3.1
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
settings:
<<: *armv7_push_settings
tags:
- latest-armv7
- develop-armv7
- ${CI_COMMIT_SHA:0:8}-armv7
docker-stable-armv7:
image: woodpeckerci/plugin-kaniko:2.3.1
when:
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
settings:
<<: *armv7_push_settings
tags:
- latest-armv7
- stable-armv7
- ${CI_COMMIT_SHA:0:8}-armv7
docker-stable-tag-armv7:
image: woodpeckerci/plugin-kaniko:2.3.1
when:
- event: tag
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
settings:
<<: *armv7_push_settings
tags:
- latest-armv7
- stable-armv7
- ${CI_COMMIT_SHA:0:8}-armv7
- ${CI_COMMIT_TAG}-armv7

View file

@ -0,0 +1,60 @@
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
- event: tag
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
- event: manual
branch: stable
depends_on:
- docker
skip_clone: true
labels:
platform: linux/amd64
steps:
docker-develop-combine:
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
settings: &docker_settings
registry: "git.pleroma.social"
image: "pleroma/pleroma"
architectures: [amd64, arm64]
tags:
- latest
- develop
- ${CI_COMMIT_SHA:0:8}
username:
from_secret: pleroma-ci-user
password:
from_secret: pleroma-ci-password
docker-stable-combine:
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
when:
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
settings:
<<: *docker_settings
tags: &stable_docker_tags
- latest
- stable
- ${CI_COMMIT_SHA:0:8}
docker-stable-tag-combine:
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
when:
- event: tag
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
settings:
<<: *docker_settings
tags:
- <<: *stable_docker_tags
- ${CI_COMMIT_TAG}

108
.woodpecker/docker.yaml Normal file
View file

@ -0,0 +1,108 @@
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
- event: tag
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
- event: manual
branch: stable
matrix:
platform:
- linux/amd64
- linux/arm64
# This is needed for the when clauses below.
labels:
platform: ${platform}
memory: 'high'
variables:
docker_variables: &docker_variables
repo: pleroma/pleroma
registry: git.pleroma.social
username:
from_secret: pleroma-ci-user
password:
from_secret: pleroma-ci-password
kaniko_image: &kaniko_image woodpeckerci/plugin-kaniko:2.3.1
steps:
docker-develop-amd64:
image: woodpeckerci/plugin-kaniko:2.3.1
when:
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
settings:
<<: *docker_variables
tags:
- latest-amd64
- develop-amd64
- ${CI_COMMIT_SHA:0:8}-amd64
docker-develop-arm64:
image: woodpeckerci/plugin-kaniko:2.3.1
when:
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
settings:
<<: *docker_variables
tags:
- latest-arm64
- develop-arm64
- ${CI_COMMIT_SHA:0:8}-arm64
docker-stable-amd64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
settings:
<<: *docker_variables
tags: &amd64_tags
- latest-amd64
- stable-amd64
- ${CI_COMMIT_SHA:0:8}-amd64
# FE workflow runs only on linux/amd64
docker-stable-e2e-amd64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
settings:
<<: *docker_variables
dockerfile: Dockerfile-e2e
tags:
- stable-e2e
docker-stable-tag-amd64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
settings:
<<: *docker_variables
tags:
- <<: *amd64_tags
- ${CI_COMMIT_TAG}-amd64
docker-stable-arm64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
settings:
<<: *docker_variables
tags: &arm64_tags
- latest-arm64
- stable-arm64
- ${CI_COMMIT_SHA:0:8}-arm64
docker-stable-tag-arm64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
settings:
<<: *docker_variables
tags:
- <<: *arm64_tags
- ${CI_COMMIT_TAG}-arm64

78
.woodpecker/lint.yaml Normal file
View file

@ -0,0 +1,78 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
labels:
platform: linux/amd64
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
mix-format:
image: &elixir-image
docker.io/elixir:1.15-alpine
entrypoint: *script_file_entrypoint
failure: ignore
commands:
- |
if ! mix format --check-formatted; then
touch fail.stamp
exit 1
fi
credo:
image: *elixir-image
entrypoint: *script_file_entrypoint
failure: ignore
environment:
MIX_ENV: test
commands:
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
- adduser -D -h /home/testuser testuser
- mkdir -p /home/testuser/.mix /home/testuser/.hex
- chown -R testuser:testuser . /home/testuser
- su testuser -c "HOME=/home/testuser mix local.hex --force"
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
- su testuser -c "HOME=/home/testuser mix deps.get"
- |
if ! su testuser -c "HOME=/home/testuser mix analyze"; then
touch fail.stamp
exit 1
fi
# cycles:
# image: *elixir-image
# failure: ignore
# commands:
# - apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
# - adduser -D -h /home/testuser testuser
# - mkdir -p /home/testuser/.mix /home/testuser/.hex
# - chown -R testuser:testuser . /home/testuser
# - su testuser -c "HOME=/home/testuser mix local.hex --force"
# - su testuser -c "HOME=/home/testuser mix local.rebar --force"
# - su testuser -c "HOME=/home/testuser mix compile"
# - |
# if ! su testuser -c "HOME=/home/testuser mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != \"No cycles found\")}'"; then
# touch fail.stamp
# exit 1
# fi
ensure-status:
image: *elixir-image
entrypoint: *script_file_entrypoint
commands: |
if test -f fail.stamp; then
echo "One or more previous steps fails. Failing workflow..."
exit 1
else
echo "All steps passed"
exit 0
fi

292
.woodpecker/otp-musl.yaml Normal file
View file

@ -0,0 +1,292 @@
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
- event: tag
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
- event: manual
branch: stable
matrix:
platform:
- linux/amd64
- linux/arm
- linux/arm64
# This is needed for the when clauses below.
# When the platform clause is fixed, this might not be needed anymore
labels:
platform: ${platform}
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
build_cmds: &build_cmds
- apk add git build-base cmake file-dev openssl vips-dev zip
- echo "import Config" > config/prod.secret.exs
- mix local.hex --force
- mix local.rebar --force
- mix deps.get --only prod
- mkdir release
- export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH}
- mix release --path release
build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3-alpine
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
artifacts_uploader_settings: &artifacts_uploader_settings
user:
from_secret: pleroma-ci-user
password:
from_secret: pleroma-ci-password
owner: 'pleroma'
env: &env
MIX_ENV: prod
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
steps:
otp-develop-amd64-musl:
image: *build_image_amd64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
environment: *env
commands: &amd64_build
- <<: *build_cmds
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release
otp-stable-amd64-musl:
image: *build_image_amd64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
environment: *env
commands: *amd64_build
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
otp-stable-tag-amd64-musl:
image: *build_image_amd64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
environment: *env
commands:
- <<: *build_cmds
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release
otp-develop-arm-musl:
image: *build_image_arm
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
environment: *env
commands: &arm_build
- <<: *build_cmds
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip release
otp-stable-arm-musl:
image: *build_image_arm
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
environment: *env
commands: *arm_build
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
otp-stable-tag-arm-musl:
image: *build_image_arm
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
environment: *env
commands:
- <<: *build_cmds
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip release
otp-develop-arm64-musl:
image: *build_image_arm64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
environment: *env
commands: &arm64_build
- <<: *build_cmds
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release
otp-stable-arm64-musl:
image: *build_image_arm64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
environment: *env
commands: *arm64_build
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
otp-stable-tag-arm64-musl:
image: *build_image_arm64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
environment: *env
commands:
- <<: *build_cmds
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release
upload-artifacts-amd64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
update: 'true'
upload-latest-amd64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl
package_version: latest
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
file_name: pleroma.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-artifacts-tag-amd64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-amd64-musl
package_version: stable-${CI_COMMIT_SHA:0:8}-amd64-musl
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-latest-tag-amd64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-amd64-musl
package_version: latest
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
file_name: pleroma.zip
update: 'true'
upload-artifacts-arm-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
update: 'true'
upload-latest-arm-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl
package_version: latest
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
file_name: pleroma.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-artifacts-tag-arm-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm-musl
package_version: stable-${CI_COMMIT_SHA:0:8}-arm-musl
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-latest-tag-arm-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm-musl
package_version: latest
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
file_name: pleroma.zip
update: 'true'
upload-artifacts-arm64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
update: 'true'
upload-latest-arm64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl
package_version: latest
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
file_name: pleroma.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-artifacts-tag-arm64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm64-musl
package_version: stable-${CI_COMMIT_SHA:0:8}-arm64-musl
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-latest-tag-arm64-musl:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm64-musl
package_version: latest
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
file_name: pleroma.zip
update: 'true'

293
.woodpecker/otp.yaml Normal file
View file

@ -0,0 +1,293 @@
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
- event: tag
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
- event: manual
branch: stable
matrix:
platform:
- linux/amd64
- linux/arm
- linux/arm64
# This is needed for the when clauses below.
# When the platform clause is fixed, this might not be needed anymore
labels:
platform: ${platform}
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
build_cmds: &build_cmds
- apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git build-essential zip
- echo "import Config" > config/prod.secret.exs
- mix local.hex --force
- mix local.rebar --force
- mix deps.get --only prod
- mkdir release
- export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH}
- mix release --path release
build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
artifacts_uploader_settings: &artifacts_uploader_settings
user:
from_secret: pleroma-ci-user
password:
from_secret: pleroma-ci-password
owner: 'pleroma'
env: &env
MIX_ENV: prod
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
DEBIAN_FRONTEND: noninteractive
steps:
otp-develop-amd64:
image: *build_image_amd64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
environment: *env
commands: &amd64_build
- <<: *build_cmds
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip release
otp-stable-amd64:
image: *build_image_amd64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
environment: *env
commands: *amd64_build
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
otp-stable-tag-amd64:
image: *build_image_amd64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
environment: *env
commands:
- <<: *build_cmds
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64.zip release
otp-develop-arm:
image: *build_image_arm
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
environment: *env
commands: &arm_build
- <<: *build_cmds
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip release
otp-stable-arm:
image: *build_image_arm
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
environment: *env
commands: *arm_build
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
otp-stable-tag-arm:
image: *build_image_arm
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
environment: *env
commands:
- <<: *build_cmds
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm.zip release
otp-develop-arm64:
image: *build_image_arm64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
environment: *env
commands: &arm64_build
- <<: *build_cmds
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip release
otp-stable-arm64:
image: *build_image_arm64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
environment: *env
commands: *arm64_build
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
otp-stable-tag-arm64:
image: *build_image_arm64
entrypoint: *script_file_entrypoint
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
environment: *env
commands:
- <<: *build_cmds
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64.zip release
upload-artifacts-amd64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
update: 'true'
upload-latest-amd64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64
package_version: latest
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
file_name: pleroma.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-artifacts-tag-amd64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-amd64
package_version: stable-${CI_COMMIT_SHA:0:8}-amd64
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-latest-tag-amd64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-amd64
package_version: latest
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip
file_name: pleroma.zip
update: 'true'
upload-artifacts-arm:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
update: 'true'
upload-latest-arm:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm
package_version: latest
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
file_name: pleroma.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-artifacts-tag-arm:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm
package_version: stable-${CI_COMMIT_SHA:0:8}-arm
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-latest-tag-arm:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm
package_version: latest
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip
file_name: pleroma.zip
update: 'true'
upload-artifacts-arm64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
update: 'true'
upload-latest-arm64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64
package_version: latest
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
file_name: pleroma.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-artifacts-tag-arm64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm64
package_version: stable-${CI_COMMIT_SHA:0:8}-arm64
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64.zip
update: 'true'
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
upload-latest-tag-arm64:
image: *artifacts_uploader_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
settings:
<<: *artifacts_uploader_settings
package_name: pleroma-otp-stable-arm64
package_version: latest
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip
file_name: pleroma.zip
update: 'true'

View file

@ -0,0 +1,45 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
depends_on:
- lint
labels:
platform: linux/amd64
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
unit-testing-elixir-1.15:
image: elixir:1.15-alpine
entrypoint: *script_file_entrypoint
environment:
MIX_ENV: test
DB_HOST: postgres
DB_PORT: 5432
commands:
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
- adduser -D -h /home/testuser testuser
- mkdir -p /home/testuser/.mix /home/testuser/.hex
- chown -R testuser:testuser . /home/testuser
- su testuser -c "HOME=/home/testuser mix local.hex --force"
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
- su testuser -c "HOME=/home/testuser mix deps.get"
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --preload-modules"
services:
postgres:
image: postgres:13-alpine
environment:
POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres

View file

@ -0,0 +1,45 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
depends_on:
- lint
labels:
platform: linux/amd64
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
unit-testing-elixir-1.18:
image: elixir:1.18-otp-27-alpine
entrypoint: *script_file_entrypoint
environment:
MIX_ENV: test
DB_HOST: postgres
DB_PORT: 5432
commands:
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
- adduser -D -h /home/testuser testuser
- mkdir -p /home/testuser/.mix /home/testuser/.hex
- chown -R testuser:testuser . /home/testuser
- su testuser -c "HOME=/home/testuser mix local.hex --force"
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
- su testuser -c "HOME=/home/testuser mix deps.get"
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --preload-modules"
services:
postgres:
image: postgres:13-alpine
environment:
POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres

View file

@ -4,6 +4,60 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.10.2
### Security
- ActivityPub: Fixed failed-signature inbox retry handling and signer identity checks to prevent spoofed remote activities from being processed
## 2.10.1
### Changed
- Move avatar_description and header_description fields to the account object
- Update Bandit to 1.10.4
- No-op code correctness improvements detected by Elixir 1.19 compiler
- Downgrade Hackney to 1.20.1
- Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney
- Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation
- Paginate follow requests
- Moved Phoenix LiveDashboard to /pleroma/live_dashboard
- Add mute/block expiry to the relationship object
- Filter indexable activities before inserting indexing jobs into the queue.
### Added
- Allow assigning users to reports
- Allow fine-grained announce visibilities
- Add immutable tag on cache-control header for several endpoints that's serving the same exact things.
- Add reasonable defaults for :database_config_whitelist
- Support lists `exclusive` param
- Add v1/instance/domain_blocks endpoint
- Add /api/v2/instance profile fields limits info used by Mastodon
- Added Oban Web dashboard located at /pleroma/oban
- Add instructions on how to run a release in docker, to make it easier to run on older distros.
### Fixed
- Fix the daily email digest job which was not executing
- Encode custom emoji URLs in EmojiReact activity tags.
- Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled
- Fix fetching Hubzilla Actors with alsoKnownAs as string
- Fix /phoenix/live_dashboard redirect not working when user added a path segment
- Fix 404 error codes for missing static files
- Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input.
- Correct old migrations for expiring activities and user access tokens.
- Federate `votersCount` correctly
- DB prune: Check if user follows hashtag with no objects before deletion
- Stop the rate limiter from crashing when run with wrong settings.
- Restore embeds route
- ReverseProxy: Recursively follow redirects until redirect_limit is reached
- Fix compilation with vips-8.18.0 with bumping to vix 0.36.0
### Removed
- Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS)
## 2.10 ## 2.10
### Security ### Security

View file

@ -3,8 +3,10 @@ ARG ELIXIR_IMG=hexpm/elixir
ARG ELIXIR_VER=1.17.3 ARG ELIXIR_VER=1.17.3
ARG ERLANG_VER=26.2.5.6 ARG ERLANG_VER=26.2.5.6
ARG ALPINE_VER=3.17.9 ARG ALPINE_VER=3.17.9
ARG ELIXIR_TAG=${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER}
ARG ALPINE_IMG=alpine
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} AS build FROM ${ELIXIR_IMG}:${ELIXIR_TAG} AS build
COPY . . COPY . .
@ -20,7 +22,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev vips-dev &&\
mkdir release &&\ mkdir release &&\
mix release --path release mix release --path release
FROM alpine:${ALPINE_VER} FROM ${ALPINE_IMG}:${ALPINE_VER}
ARG BUILD_DATE ARG BUILD_DATE
ARG VCS_REF ARG VCS_REF

61
Dockerfile-e2e Normal file
View file

@ -0,0 +1,61 @@
# https://hub.docker.com/r/hexpm/elixir/tags
ARG ELIXIR_IMG=docker.io/hexpm/elixir
ARG ALPINE_IMG=docker.io/alpine
ARG ELIXIR_VER=1.18.4
ARG ERLANG_VER=27.3.4.11
ARG ALPINE_VER=3.23.4
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} AS build
COPY . .
ENV MIX_ENV=prod
ENV VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS
RUN apk add git gcc g++ musl-dev make cmake file-dev vips-dev &&\
echo "import Config" > config/prod.secret.exs &&\
mix local.hex --force &&\
mix local.rebar --force &&\
mix deps.clean --all &&\
mix deps.get --only prod &&\
mkdir release &&\
mix release --path release
FROM ${ALPINE_IMG}:${ALPINE_VER}
ARG BUILD_DATE
ARG VCS_REF
LABEL maintainer="ops@pleroma.social" \
org.opencontainers.image.title="pleroma" \
org.opencontainers.image.description="Pleroma FE E2E test image" \
org.opencontainers.image.authors="ops@pleroma.social" \
org.opencontainers.image.vendor="pleroma.social" \
org.opencontainers.image.documentation="https://git.pleroma.social/pleroma/pleroma" \
org.opencontainers.image.licenses="AGPL-3.0" \
org.opencontainers.image.url="https://pleroma.social" \
org.opencontainers.image.revision=$VCS_REF \
org.opencontainers.image.created=$BUILD_DATE
ARG HOME=/opt/pleroma
ARG DATA=/var/lib/pleroma
RUN apk update &&\
apk add exiftool ffmpeg vips libmagic ncurses postgresql-client &&\
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
mkdir -p ${DATA}/uploads &&\
mkdir -p ${DATA}/static &&\
chown -R pleroma ${DATA} &&\
mkdir -p /etc/pleroma &&\
chown -R pleroma /etc/pleroma
USER pleroma
COPY --from=build --chown=pleroma:0 /release ${HOME}
COPY --chown=pleroma --chmod=640 ./config/docker.exs /etc/pleroma/config.exs
COPY ./docker-entrypoint-e2e.sh ${HOME}
EXPOSE 4000
ENTRYPOINT ["/opt/pleroma/docker-entrypoint-e2e.sh"]

View file

@ -1,4 +1,4 @@
<img src="https://git.pleroma.social/pleroma/pleroma/uploads/8cec84f5a084d887339f57deeb8a293e/pleroma-banner-vector-nopad-notext.svg" width="300px" /> <img src="https://git.pleroma.social/attachments/06a95f5a-7cac-42ad-8b1d-1483f1739f38" width="300px" />
## About ## About

View file

@ -0,0 +1 @@
Switch native captcha to the published pleroma_captcha Hex package.

View file

@ -0,0 +1 @@
litepub-0.1.jsonld cleanup

View file

@ -1 +0,0 @@
Encode custom emoji URLs in EmojiReact activity tags.

View file

@ -0,0 +1 @@
Support fetching featured collection items from the first collection page

View file

@ -1 +0,0 @@
Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney

View file

@ -1 +0,0 @@
Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation

View file

@ -0,0 +1 @@
Ensure Host header is present and matches instance URI

View file

@ -0,0 +1 @@
Fix compatibility with timestamped HTTP Signatures used by GoToSocial

View file

@ -0,0 +1 @@
Handle reports with just actor ap id as the object

View file

@ -1 +0,0 @@
Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS)

View file

@ -1 +0,0 @@
Add v1/instance/domain_blocks endpoint

View file

@ -0,0 +1 @@
Update majic to 1.2.0.

View file

@ -0,0 +1 @@
Echo Mastodon-style `Sec-WebSocket-Protocol` tokens in streaming WebSocket handshakes.

View file

@ -0,0 +1 @@
Add backend support for Misskey Markdown (MFM) posts

View file

View file

@ -1 +0,0 @@
Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input.

View file

@ -0,0 +1 @@
Use the Hex package for oban_plugins_lazarus.

View file

@ -1 +0,0 @@
Added Oban Web dashboard located at /pleroma/oban

View file

@ -1 +0,0 @@
Paginate follow requests

View file

@ -1 +0,0 @@
Moved Phoenix LiveDashboard to /pleroma/live_dashboard

View file

@ -0,0 +1 @@
Switch patched Phoenix 1.7.14 back to upstream with Phoenix 1.8.0+

View file

@ -0,0 +1 @@
Updated Pleroma-FE build URL after Forgejo migration

View file

@ -0,0 +1 @@
Fix votersCount inflation when same voter picks multiple options

View file

@ -1 +0,0 @@
Reduce the number of flaky tests by making them sync if they affect the global state, and silence noisy test output.

View file

@ -0,0 +1 @@
Reject incoming reports when both the reporter and reported account are remote

View file

@ -1 +0,0 @@
Add instructions on how to run a release in docker, to make it easier to run on older distros.

View file

@ -0,0 +1 @@
Use upstream Hex releases for the `remote_ip` dependency and expose client IP ranges for remote IP resolution.

View file

@ -0,0 +1 @@
Improve user search / autocompletion ordering.

View file

@ -1 +0,0 @@
Fix compilation with vips-8.18.0 with bumping to vix 0.36.0

View file

View file

@ -0,0 +1 @@
RichMedia: Fix backfill causing old posts to show up on timelines by disabling it in MastoAPI StatusView

View file

@ -203,7 +203,8 @@ config :pleroma, :instance,
"text/plain", "text/plain",
"text/html", "text/html",
"text/markdown", "text/markdown",
"text/bbcode" "text/bbcode",
"text/x.misskeymarkdown"
], ],
autofollowed_nicknames: [], autofollowed_nicknames: [],
autofollowing_nicknames: [], autofollowing_nicknames: [],
@ -737,6 +738,7 @@ config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true, min_lifeti
config :pleroma, Pleroma.Web.Plugs.RemoteIp, config :pleroma, Pleroma.Web.Plugs.RemoteIp,
enabled: true, enabled: true,
headers: ["x-forwarded-for"], headers: ["x-forwarded-for"],
clients: [],
proxies: [], proxies: [],
reserved: [ reserved: [
"127.0.0.0/8", "127.0.0.0/8",
@ -775,7 +777,7 @@ config :pleroma, :frontends,
"name" => "pleroma-fe", "name" => "pleroma-fe",
"git" => "https://git.pleroma.social/pleroma/pleroma-fe", "git" => "https://git.pleroma.social/pleroma/pleroma-fe",
"build_url" => "build_url" =>
"https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build", "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-fe-builds/${ref}/latest.zip",
"ref" => "develop" "ref" => "develop"
}, },
"fedi-fe" => %{ "fedi-fe" => %{
@ -960,6 +962,15 @@ config :pleroma, Pleroma.Search.QdrantSearch,
vectors: %{size: 384, distance: "Cosine"} vectors: %{size: 384, distance: "Cosine"}
} }
config :pleroma, :database_config_whitelist, [
{:pleroma},
{:cors_plug},
{:ex_aws, :s3},
{:mime},
{:prometheus, Pleroma.Web.Endpoint.MetricsExporter},
{:web_push_encryption, :vapid_details}
]
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

View file

@ -815,7 +815,8 @@ config :pleroma, :config_description, [
"text/plain", "text/plain",
"text/html", "text/html",
"text/markdown", "text/markdown",
"text/bbcode" "text/bbcode",
"text/x.misskeymarkdown"
] ]
}, },
%{ %{
@ -1394,7 +1395,13 @@ config :pleroma, :config_description, [
label: "Post Content Type", label: "Post Content Type",
type: {:dropdown, :atom}, type: {:dropdown, :atom},
description: "Default post formatting option", description: "Default post formatting option",
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"] suggestions: [
"text/plain",
"text/html",
"text/markdown",
"text/bbcode",
"text/x.misskeymarkdown"
]
}, },
%{ %{
key: :redirectRootNoLogin, key: :redirectRootNoLogin,
@ -2903,7 +2910,7 @@ config :pleroma, :config_description, [
key: Pleroma.Web.Plugs.RemoteIp, key: Pleroma.Web.Plugs.RemoteIp,
type: :group, type: :group,
description: """ description: """
`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. `Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://hex.pm/packages/remote_ip) but with runtime configuration.
**If your instance is not behind at least one reverse proxy, you should not enable this plug.** **If your instance is not behind at least one reverse proxy, you should not enable this plug.**
""", """,
children: [ children: [
@ -2919,6 +2926,12 @@ config :pleroma, :config_description, [
A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`. A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`.
""" """
}, },
%{
key: :clients,
type: {:list, :string},
description:
"A list of client IPs or subnets in CIDR notation. These will not be treated as proxies or reserved ranges. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128."
},
%{ %{
key: :proxies, key: :proxies,
type: {:list, :string}, type: {:list, :string},

70
docker-entrypoint-e2e.sh Executable file
View file

@ -0,0 +1,70 @@
#!/bin/ash
set -eu
SEED_SENTINEL_PATH=/var/lib/pleroma/.e2e_seeded
CONFIG_OVERRIDE_PATH=/var/lib/pleroma/config.exs
echo '-- Waiting for database...'
while ! pg_isready -U "${DB_USER:-pleroma}" -d "postgres://${DB_HOST:-db}:${DB_PORT:-5432}/${DB_NAME:-pleroma}" -t 1; do
sleep 1s
done
echo '-- Writing E2E config overrides...'
cat > "$CONFIG_OVERRIDE_PATH" <<EOF
import Config
config :pleroma, Pleroma.Captcha,
enabled: false
config :pleroma, :instance,
registrations_open: true,
account_activation_required: false,
account_approval_required: false
EOF
echo '-- Running migrations...'
/opt/pleroma/bin/pleroma_ctl migrate
if [ ! -f "$SEED_SENTINEL_PATH" ]; then
if [ -n "${E2E_ADMIN_USERNAME:-}" ] && [ -n "${E2E_ADMIN_PASSWORD:-}" ] && [ -n "${E2E_ADMIN_EMAIL:-}" ]; then
echo '-- Seeding admin user' "$E2E_ADMIN_USERNAME" '...'
if ! PLEROMA_CTL_RPC_DISABLED=true /opt/pleroma/bin/pleroma_ctl user new "$E2E_ADMIN_USERNAME" "$E2E_ADMIN_EMAIL" --admin --password "$E2E_ADMIN_PASSWORD" -y; then
echo '-- User already exists or creation failed, ensuring admin + confirmed...'
PLEROMA_CTL_RPC_DISABLED=true /opt/pleroma/bin/pleroma_ctl user set "$E2E_ADMIN_USERNAME" --admin --confirmed
fi
else
echo '-- Skipping admin seeding (missing E2E_ADMIN_* env)'
fi
touch "$SEED_SENTINEL_PATH"
fi
echo '-- Starting!'
/opt/pleroma/bin/pleroma start &
PLEROMA_PID=$!
cleanup() {
if kill -0 "$PLEROMA_PID" 2>/dev/null; then
kill -TERM "$PLEROMA_PID"
wait "$PLEROMA_PID" || true
fi
}
trap cleanup INT TERM
echo '-- Waiting for API...'
api_ok=false
for _i in $(seq 1 120); do
if wget -qO- http://127.0.0.1:4000/api/v1/instance >/dev/null 2>&1; then
api_ok=true
break
fi
sleep 1s
done
if [ "$api_ok" != true ]; then
echo 'Timed out waiting for Pleroma API to become available'
exit 1
fi
wait "$PLEROMA_PID"

View file

@ -169,4 +169,18 @@ This forcibly removes any enabled MRF that does not exist and will fix the abili
=== "From Source" === "From Source"
```sh ```sh
mix pleroma.config fix_mrf_policies mix pleroma.config fix_mrf_policies
``` ```
## Remove non-whitelisted configs from the database
This removes any configuration value that is not explicitly whitelisted by `:pleroma, :database_config_whitelist`. Might be useful after updating the whitelist.
=== "OTP"
```sh
./bin/pleroma_ctl config filter_whitelisted
```
=== "From Source"
```sh
mix pleroma.config filter_whitelisted
```

View file

@ -1132,8 +1132,9 @@ Boolean, enables/disables in-database configuration. Read [Transferring the conf
List of valid configuration sections which are allowed to be configured from the List of valid configuration sections which are allowed to be configured from the
database. Settings stored in the database before the whitelist is configured are database. Settings stored in the database before the whitelist is configured are
still applied, so it is suggested to only use the whitelist on instances that still applied. Consider running the `mix pleroma.config filter_whitelisted` task
have not migrated the config to the database. after updating the whitelist. Read [Remove non-whitelisted configs from the database](../administration/CLI_tasks/config.md#remove-non-whitelisted-configs-from-the-database)
for more information.
Example: Example:
```elixir ```elixir

View file

@ -665,6 +665,7 @@ Status: 404
- *optional* `limit`: **integer** the number of records to retrieve - *optional* `limit`: **integer** the number of records to retrieve
- *optional* `page`: **integer** page number - *optional* `page`: **integer** page number
- *optional* `page_size`: **integer** number of log entries per page (default is `50`) - *optional* `page_size`: **integer** number of log entries per page (default is `50`)
- *optional* `assigned_account`: **string** assigned account ID
- Response: - Response:
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin - On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
- On success: JSON, returns a list of reports, where: - On success: JSON, returns a list of reports, where:
@ -749,6 +750,7 @@ Status: 404
"url": "https://pleroma.example.org/users/lain", "url": "https://pleroma.example.org/users/lain",
"username": "lain" "username": "lain"
}, },
"assigned_account": null,
"content": "Please delete it", "content": "Please delete it",
"created_at": "2019-04-29T19:48:15.000Z", "created_at": "2019-04-29T19:48:15.000Z",
"id": "9iJGOv1j8hxuw19bcm", "id": "9iJGOv1j8hxuw19bcm",
@ -868,6 +870,37 @@ Status: 404
] ]
``` ```
- Response:
- On failure:
- 400 Bad Request, JSON:
```json
[
{
`id`, // report id
`error` // error message
}
]
```
- On success: `204`, empty response
## `POST /api/v1/pleroma/admin/reports/assign_account`
### Assign account to one or multiple reports
- Params:
```json
`reports`: [
{
`id`, // required, report id
`nickname` // account nickname, use null to unassign account
},
...
]
```
- Response: - Response:
- On failure: - On failure:
- 400 Bad Request, JSON: - 400 Bad Request, JSON:

View file

@ -74,7 +74,7 @@ Pleroma does not process remote images and therefore cannot include fields such
The `GET /api/v1/bookmarks` endpoint accepts optional parameter `folder_id` for bookmark folder ID. The `GET /api/v1/bookmarks` endpoint accepts optional parameter `folder_id` for bookmark folder ID.
The `POST /api/v1/statuses/:id/bookmark` endpoint accepts optional parameter `folder_id` for bookmark folder ID. The `POST /api/v1/statuses/:id/bookmark` endpoint accepts optional parameter `folder_id` for bookmark folder ID. Bookmarking an already bookmarked post will update the folder association, or remove it if `folder_id` is omitted or `null`.
## Accounts ## Accounts
@ -127,8 +127,6 @@ Has these additional fields under the `pleroma` object:
- `notification_settings`: object, can be absent. See `/api/v1/pleroma/notification_settings` for the parameters/keys returned. - `notification_settings`: object, can be absent. See `/api/v1/pleroma/notification_settings` for the parameters/keys returned.
- `accepts_chat_messages`: boolean, but can be null if we don't have that information about a user - `accepts_chat_messages`: boolean, but can be null if we don't have that information about a user
- `favicon`: nullable URL string, Favicon image of the user's instance - `favicon`: nullable URL string, Favicon image of the user's instance
- `avatar_description`: string, image description for user avatar, defaults to empty string
- `header_description`: string, image description for user banner, defaults to empty string
### Source ### Source

View file

@ -690,6 +690,7 @@ Audio scrobbling in Pleroma is **deprecated**.
* `album`: the album of the media playing [optional] * `album`: the album of the media playing [optional]
* `artist`: the artist of the media playing [optional] * `artist`: the artist of the media playing [optional]
* `length`: the length of the media playing [optional] * `length`: the length of the media playing [optional]
* `external_link`: a URL referencing the media playing [optional]
* Response: the newly created media metadata entity representing the Listen activity * Response: the newly created media metadata entity representing the Listen activity
# Emoji Reactions # Emoji Reactions

View file

@ -234,6 +234,61 @@ defmodule Mix.Tasks.Pleroma.Config do
end) end)
end end
# Removes non-whitelisted configuration sections
def run(["filter_whitelisted" | rest]) do
{options, [], []} =
OptionParser.parse(
rest,
strict: [force: :boolean],
aliases: [f: :force]
)
force = Keyword.get(options, :force, false)
start_pleroma()
whitelisted_configs = Pleroma.Config.get(:database_config_whitelist)
if whitelisted_configs in [nil, false] do
shell_error("No unwanted settings in ConfigDB. No changes made.")
else
whitelisted_groups =
whitelisted_configs
|> Enum.filter(fn
{_group} -> true
_ -> false
end)
|> Enum.map(fn {group} -> group end)
whitelisted_keys =
whitelisted_configs
|> Enum.filter(fn
{_group, _key} -> true
_ -> false
end)
filtered =
from(c in ConfigDB)
|> Repo.all()
|> Enum.filter(&not_whitelisted?(&1, whitelisted_groups, whitelisted_keys))
if not Enum.empty?(filtered) do
shell_info("The following settings will be removed from ConfigDB:\n")
Enum.each(filtered, &dump(&1))
if force or shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
filtered_ids = Enum.map(filtered, fn %{id: id} -> id end)
Repo.delete_all(from(c in ConfigDB, where: c.id in ^filtered_ids))
else
shell_error("No changes made.")
end
else
shell_error("No unwanted settings in ConfigDB. No changes made.")
end
end
end
@spec migrate_to_db(Path.t() | nil) :: any() @spec migrate_to_db(Path.t() | nil) :: any()
def migrate_to_db(file_path \\ nil) do def migrate_to_db(file_path \\ nil) do
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
@ -434,4 +489,9 @@ defmodule Mix.Tasks.Pleroma.Config do
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;") Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;") Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
end end
defp not_whitelisted?(%{group: group, key: key}, whitelisted_groups, whitelisted_keys) do
not Enum.member?(whitelisted_groups, group) and
not Enum.member?(whitelisted_keys, {group, key})
end
end end

View file

@ -226,7 +226,12 @@ defmodule Mix.Tasks.Pleroma.Database do
DELETE FROM hashtags AS ht DELETE FROM hashtags AS ht
WHERE NOT EXISTS ( WHERE NOT EXISTS (
SELECT 1 FROM hashtags_objects hto SELECT 1 FROM hashtags_objects hto
WHERE ht.id = hto.hashtag_id) WHERE ht.id = hto.hashtag_id
)
AND NOT EXISTS (
SELECT 1 FROM user_follows_hashtag ufh
WHERE ht.id = ufh.hashtag_id
)
""" """
|> Repo.query() |> Repo.query()

View file

@ -22,7 +22,7 @@ defmodule Mix.Tasks.Pleroma.OpenapiSpec do
else else
{_, errors} -> {_, errors} ->
IO.puts(IO.ANSI.format([:red, :bright, "Spec check failed, errors:"])) IO.puts(IO.ANSI.format([:red, :bright, "Spec check failed, errors:"]))
Enum.map(errors, &IO.puts/1) Enum.each(errors, &IO.puts/1)
raise "Spec check failed" raise "Spec check failed"
end end

View file

@ -72,7 +72,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
query, query,
timeout: :infinity timeout: :infinity
) )
|> Stream.map(&Pleroma.Search.Meilisearch.object_to_search_data/1) |> Stream.map(&Pleroma.Search.object_to_search_data/1)
|> Stream.filter(fn o -> not is_nil(o) end) |> Stream.filter(fn o -> not is_nil(o) end)
|> Stream.chunk_every(chunk_size) |> Stream.chunk_every(chunk_size)
|> Stream.transform(0, fn objects, acc -> |> Stream.transform(0, fn objects, acc ->

View file

@ -38,7 +38,7 @@ defmodule Pleroma.Activity.HTML do
def invalidate_cache_for(activity_id) do def invalidate_cache_for(activity_id) do
keys = get_cache_keys_for(activity_id) keys = get_cache_keys_for(activity_id)
Enum.map(keys, &@cachex.del(:scrubber_cache, &1)) Enum.each(keys, &@cachex.del(:scrubber_cache, &1))
@cachex.del(:scrubber_management_cache, activity_id) @cachex.del(:scrubber_management_cache, activity_id)
end end

View file

@ -19,7 +19,7 @@ defmodule Pleroma.Bookmark do
schema "bookmarks" do schema "bookmarks" do
belongs_to(:user, User, type: FlakeId.Ecto.CompatType) belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType) belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.CompatType) belongs_to(:folder, BookmarkFolder, type: FlakeId.Ecto.Type)
timestamps() timestamps()
end end
@ -38,7 +38,7 @@ defmodule Pleroma.Bookmark do
|> validate_required([:user_id, :activity_id]) |> validate_required([:user_id, :activity_id])
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index) |> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|> Repo.insert( |> Repo.insert(
on_conflict: [set: [folder_id: folder_id]], on_conflict: [set: [folder_id: folder_id, updated_at: NaiveDateTime.utc_now()]],
conflict_target: [:user_id, :activity_id] conflict_target: [:user_id, :activity_id]
) )
end end
@ -76,11 +76,4 @@ defmodule Pleroma.Bookmark do
|> Repo.one() |> Repo.one()
|> Repo.delete() |> Repo.delete()
end end
def set_folder(bookmark, folder_id) do
bookmark
|> cast(%{folder_id: folder_id}, [:folder_id])
|> validate_required([:folder_id])
|> Repo.update()
end
end end

View file

@ -14,7 +14,7 @@ defmodule Pleroma.BookmarkFolder do
alias Pleroma.User alias Pleroma.User
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@primary_key {:id, FlakeId.Ecto.CompatType, autogenerate: true} @primary_key {:id, FlakeId.Ecto.Type, autogenerate: true}
schema "bookmark_folders" do schema "bookmark_folders" do
field(:name, :string) field(:name, :string)

View file

@ -10,6 +10,7 @@ defmodule Pleroma.ConfigDB do
import Pleroma.Web.Gettext import Pleroma.Web.Gettext
alias __MODULE__ alias __MODULE__
alias Pleroma.EctoType.Config.RateLimit
alias Pleroma.Repo alias Pleroma.Repo
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@ -60,8 +61,59 @@ defmodule Pleroma.ConfigDB do
|> cast(params, [:key, :group, :value]) |> cast(params, [:key, :group, :value])
|> validate_required([:key, :group, :value]) |> validate_required([:key, :group, :value])
|> unique_constraint(:key, name: :config_group_key_index) |> unique_constraint(:key, name: :config_group_key_index)
|> validate_rate_limit()
end end
defp validate_rate_limit(changeset) do
group = get_field(changeset, :group)
key = get_field(changeset, :key)
if group == :pleroma and key == :rate_limit do
value = get_field(changeset, :value)
case normalize_rate_limit(value) do
{:ok, normalized_value} ->
put_change(changeset, :value, normalized_value)
{:error, {limiter_name, reason}} ->
add_error(
changeset,
:value,
"invalid :rate_limit value for #{inspect(limiter_name)}: #{reason}"
)
end
else
changeset
end
end
defp normalize_rate_limit(nil), do: {:ok, nil}
defp normalize_rate_limit(%{} = value), do: normalize_rate_limit(Map.to_list(value))
defp normalize_rate_limit(value) when is_list(value) do
if Keyword.keyword?(value) do
value
|> Enum.reduce_while({:ok, []}, fn {limiter_name, limiter_value}, {:ok, acc} ->
case RateLimit.cast_with_error(limiter_value) do
{:ok, normalized_limiter_value} ->
{:cont, {:ok, [{limiter_name, normalized_limiter_value} | acc]}}
{:error, reason} ->
{:halt, {:error, {limiter_name, reason}}}
end
end)
|> case do
{:ok, acc} -> {:ok, Enum.reverse(acc)}
{:error, _} = error -> error
end
else
{:error, {:rate_limit, "must be a keyword list"}}
end
end
defp normalize_rate_limit(_), do: {:error, {:rate_limit, "must be a keyword list"}}
defp create(params) do defp create(params) do
%ConfigDB{} %ConfigDB{}
|> changeset(params) |> changeset(params)

View file

@ -22,13 +22,14 @@ defmodule Pleroma.Constants do
"generator", "generator",
"rules", "rules",
"language", "language",
"voters" "voters",
"assigned_account"
] ]
) )
const(static_only_files, const(static_only_files,
do: do:
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css) ~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js schemas doc embed.js embed.css)
) )
const(status_updatable_fields, const(status_updatable_fields,

View file

@ -0,0 +1,71 @@
# Pleroma: A lightweight social networking server
# Copyright © Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.EctoType.Config.RateLimit do
@moduledoc false
use Ecto.Type
@type t ::
nil
| {non_neg_integer(), non_neg_integer()}
| [{non_neg_integer(), non_neg_integer()}]
@impl true
def type, do: :term
@impl true
def cast(value) do
case cast_with_error(value) do
{:ok, normalized} -> {:ok, normalized}
{:error, _reason} -> :error
end
end
@impl true
def load(value), do: cast(value)
@impl true
def dump(value), do: cast(value)
@spec cast_with_error(term()) :: {:ok, t()} | {:error, String.t()}
def cast_with_error(nil), do: {:ok, nil}
def cast_with_error({scale, limit}) do
with {:ok, scale} <- parse_integer(scale, "scale"),
{:ok, limit} <- parse_integer(limit, "limit"),
true <- scale >= 1 and limit >= 1 do
{:ok, {scale, limit}}
else
false -> {:error, "scale and limit must be >= 1"}
{:error, reason} -> {:error, reason}
end
end
def cast_with_error([{_, _} = unauth, {_, _} = auth]) do
with {:ok, unauth} <- cast_with_error(unauth),
{:ok, auth} <- cast_with_error(auth) do
{:ok, [unauth, auth]}
else
{:error, reason} -> {:error, reason}
end
end
def cast_with_error(_),
do:
{:error, "must be a {scale, limit} tuple, a [{scale, limit}, {scale, limit}] list, or nil"}
defp parse_integer(value, _label) when is_integer(value), do: {:ok, value}
defp parse_integer(value, label) when is_binary(value) do
value = String.trim(value)
case Integer.parse(value) do
{number, ""} -> {:ok, number}
_ -> {:error, "#{label} must be an integer"}
end
end
defp parse_integer(_value, label), do: {:error, "#{label} must be an integer"}
end

View file

@ -127,6 +127,13 @@ defmodule Pleroma.Formatter do
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false}) Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false})
end end
def markdown_to_html(text, opts) do
Earmark.as_html!(
text,
%Earmark.Options{compact_output: true, smartypants: false} |> Map.merge(opts)
)
end
def html_escape({text, mentions, hashtags}, type) do def html_escape({text, mentions, hashtags}, type) do
{html_escape(text, type), mentions, hashtags} {html_escape(text, type), mentions, hashtags}
end end
@ -135,6 +142,10 @@ defmodule Pleroma.Formatter do
HTML.filter_tags(text) HTML.filter_tags(text)
end end
def html_escape(text, "text/x.misskeymarkdown") do
HTML.filter_tags(text)
end
def html_escape(text, "text/plain") do def html_escape(text, "text/plain") do
Regex.split(@link_regex, text, include_captures: true) Regex.split(@link_regex, text, include_captures: true)
|> Enum.map_every(2, fn chunk -> |> Enum.map_every(2, fn chunk ->

View file

@ -75,8 +75,8 @@ defmodule Pleroma.Frontend do
end end
defp download_build(frontend_info, dest) do defp download_build(frontend_info, dest) do
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]}")
url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"]) url = String.replace(frontend_info["build_url"], "${ref}", frontend_info["ref"])
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]} from #{url}")
with {:ok, %{status: 200, body: zip_body}} <- with {:ok, %{status: 200, body: zip_body}} <-
Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do Pleroma.HTTP.get(url, [], pool: :media, recv_timeout: 120_000) do

View file

@ -21,10 +21,13 @@ defmodule Pleroma.Gopher.Server do
def init([ip, port]) do def init([ip, port]) do
Logger.info("Starting gopher server on #{port}") Logger.info("Starting gopher server on #{port}")
Process.flag(:trap_exit, true)
listener = :gopher
{:ok, _pid} = {:ok, _pid} =
:ranch.start_listener( :ranch.start_listener(
:gopher, listener,
:ranch_tcp, :ranch_tcp,
%{ %{
num_acceptors: 100, num_acceptors: 100,
@ -35,7 +38,11 @@ defmodule Pleroma.Gopher.Server do
[] []
) )
{:ok, %{ip: ip, port: port}} {:ok, %{ip: ip, port: port, listener: listener}}
end
def terminate(_reason, state) do
:ranch.stop_listener(state.listener)
end end
end end

View file

@ -17,13 +17,14 @@ defmodule Pleroma.List do
field(:title, :string) field(:title, :string)
field(:following, {:array, :string}, default: []) field(:following, {:array, :string}, default: [])
field(:ap_id, :string) field(:ap_id, :string)
field(:exclusive, :boolean, default: false)
timestamps() timestamps()
end end
def title_changeset(list, attrs \\ %{}) do def update_changeset(list, attrs \\ %{}) do
list list
|> cast(attrs, [:title]) |> cast(attrs, [:title, :exclusive])
|> validate_required([:title]) |> validate_required([:title])
end end
@ -91,14 +92,14 @@ defmodule Pleroma.List do
|> Repo.all() |> Repo.all()
end end
def rename(%Pleroma.List{} = list, title) do def update(%Pleroma.List{} = list, params) do
list list
|> title_changeset(%{title: title}) |> update_changeset(params)
|> Repo.update() |> Repo.update()
end end
def create(title, %User{} = creator) do def create(params, %User{} = creator) do
changeset = title_changeset(%Pleroma.List{user_id: creator.id}, %{title: title}) changeset = update_changeset(%Pleroma.List{user_id: creator.id}, params)
if changeset.valid? do if changeset.valid? do
Repo.transaction(fn -> Repo.transaction(fn ->
@ -149,4 +150,14 @@ defmodule Pleroma.List do
end end
def member?(_, _), do: false def member?(_, _), do: false
def get_exclusive_list_members(%User{id: user_id}) do
Pleroma.List
|> where([l], l.user_id == ^user_id)
|> where([l], l.exclusive == true)
|> select([l], l.following)
|> Repo.all()
|> List.flatten()
|> Enum.uniq()
end
end end

View file

@ -78,7 +78,7 @@ defmodule Pleroma.Marker do
defp get_marker(user, timeline) do defp get_marker(user, timeline) do
case Repo.find_resource(get_query(user, timeline)) do case Repo.find_resource(get_query(user, timeline)) do
{:ok, marker} -> %__MODULE__{marker | user: user} {:ok, %__MODULE__{} = marker} -> %{marker | user: user}
_ -> %__MODULE__{timeline: timeline, user_id: user.id} _ -> %__MODULE__{timeline: timeline, user_id: user.id}
end end
end end

View file

@ -8,7 +8,8 @@ defmodule Pleroma.MFA.Changeset do
alias Pleroma.User alias Pleroma.User
def disable(%Ecto.Changeset{} = changeset, force \\ false) do def disable(%Ecto.Changeset{} = changeset, force \\ false) do
settings = %Settings{} =
settings =
changeset changeset
|> Ecto.Changeset.apply_changes() |> Ecto.Changeset.apply_changes()
|> MFA.fetch_settings() |> MFA.fetch_settings()
@ -20,20 +21,20 @@ defmodule Pleroma.MFA.Changeset do
end end
end end
def disable_totp(%User{multi_factor_authentication_settings: settings} = user) do def disable_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
user user
|> put_change(%Settings{settings | totp: %Settings.TOTP{}}) |> put_change(%Settings{settings | totp: %Settings.TOTP{}})
end end
def confirm_totp(%User{multi_factor_authentication_settings: settings} = user) do def confirm_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
totp_settings = %Settings.TOTP{settings.totp | confirmed: true} totp_settings = %Settings.TOTP{(%Settings.TOTP{} = settings.totp) | confirmed: true}
user user
|> put_change(%Settings{settings | totp: totp_settings, enabled: true}) |> put_change(%Settings{settings | totp: totp_settings, enabled: true})
end end
def setup_totp(%User{} = user, attrs) do def setup_totp(%User{} = user, attrs) do
mfa_settings = MFA.fetch_settings(user) %Settings{} = mfa_settings = MFA.fetch_settings(user)
totp_settings = totp_settings =
%Settings.TOTP{} %Settings.TOTP{}
@ -46,7 +47,7 @@ defmodule Pleroma.MFA.Changeset do
def cast_backup_codes(%User{} = user, codes) do def cast_backup_codes(%User{} = user, codes) do
user user
|> put_change(%Settings{ |> put_change(%Settings{
user.multi_factor_authentication_settings (%Settings{} = user.multi_factor_authentication_settings)
| backup_codes: codes | backup_codes: codes
}) })
end end

View file

@ -132,11 +132,18 @@ defmodule Pleroma.ModerationLog do
end end
def insert_log(%{actor: %User{}, action: action, subject: %Activity{} = subject} = attrs) def insert_log(%{actor: %User{}, action: action, subject: %Activity{} = subject} = attrs)
when action in ["report_note_delete", "report_update", "report_note"] do when action in [
"report_note_delete",
"report_update",
"report_note",
"report_unassigned",
"report_assigned"
] do
data = data =
attrs attrs
|> prepare_log_data |> prepare_log_data
|> Pleroma.Maps.put_if_present("text", attrs[:text]) |> Pleroma.Maps.put_if_present("text", attrs[:text])
|> Pleroma.Maps.put_if_present("assigned_account", attrs[:assigned_account])
|> Map.merge(%{"subject" => report_to_map(subject)}) |> Map.merge(%{"subject" => report_to_map(subject)})
insert_log_entry_with_message(%ModerationLog{data: data}) insert_log_entry_with_message(%ModerationLog{data: data})
@ -441,6 +448,35 @@ defmodule Pleroma.ModerationLog do
" with '#{state}' state" " with '#{state}' state"
end end
def get_log_entry_message(
%ModerationLog{
data: %{
"actor" => %{"nickname" => actor_nickname},
"action" => "report_assigned",
"subject" => %{"id" => subject_id, "type" => "report"},
"assigned_account" => assigned_account
}
} = log
) do
"@#{actor_nickname} assigned report ##{subject_id}" <>
subject_actor_nickname(log, " (on user ", ")") <>
" to user #{assigned_account}"
end
def get_log_entry_message(
%ModerationLog{
data: %{
"actor" => %{"nickname" => actor_nickname},
"action" => "report_unassigned",
"subject" => %{"id" => subject_id, "type" => "report"}
}
} = log
) do
"@#{actor_nickname} unassigned report ##{subject_id}" <>
subject_actor_nickname(log, " (on user ", ")") <>
" from a user"
end
def get_log_entry_message( def get_log_entry_message(
%ModerationLog{ %ModerationLog{
data: %{ data: %{

View file

@ -372,12 +372,28 @@ defmodule Pleroma.Object do
option option
end) end)
voters = [actor | object.data["voters"] || []] |> Enum.uniq() existing_voters = object.data["voters"] || []
voters = [actor | existing_voters] |> Enum.uniq()
new_voter? = actor not in existing_voters
existing_voters_count = object.data["votersCount"]
voters_count =
cond do
is_integer(existing_voters_count) and new_voter? ->
existing_voters_count + 1
is_integer(existing_voters_count) ->
existing_voters_count
true ->
length(voters)
end
data = data =
object.data object.data
|> Map.put(key, options) |> Map.put(key, options)
|> Map.put("voters", voters) |> Map.put("voters", voters)
|> Map.put("votersCount", voters_count)
object object
|> Object.change(%{data: data}) |> Object.change(%{data: data})

View file

@ -5,7 +5,10 @@
defmodule Pleroma.ReleaseTasks do defmodule Pleroma.ReleaseTasks do
@repo Pleroma.Repo @repo Pleroma.Repo
def run(args) do # TODO: Kept for some backwards compatibility with buggy pleroma_ctl,
# if a mismatch between pleroma_ctl and Pleroma accidentaly happens.
# Remove in the future.
def run(args) when is_binary(args) do
[task | args] = String.split(args) [task | args] = String.split(args)
case task do case task do
@ -16,6 +19,20 @@ defmodule Pleroma.ReleaseTasks do
end end
end end
# HACK: Script arguments need to be received as a list, otherwise (quoted) arguments with
# whitespace will be broken. Previously the broken string form above was used,
# escaping in the shell does not help.
def run(args) when is_list(args) do
[task | args] = args
case task do
"migrate" -> migrate(args)
"create" -> create()
"rollback" -> rollback(args)
task -> mix_task(task, args)
end
end
def find_module(task) do def find_module(task) do
module_name = module_name =
task task

View file

@ -12,7 +12,7 @@ defmodule Pleroma.ReverseProxy do
@keep_resp_headers @resp_cache_headers ++ @keep_resp_headers @resp_cache_headers ++
~w(content-length content-type content-disposition content-encoding) ++ ~w(content-length content-type content-disposition content-encoding) ++
~w(content-range accept-ranges vary) ~w(content-range accept-ranges vary)
@default_cache_control_header "public, max-age=1209600" @default_cache_control_header "public, max-age=1209600, immutable"
@valid_resp_codes [200, 206, 304] @valid_resp_codes [200, 206, 304]
@max_read_duration :timer.seconds(30) @max_read_duration :timer.seconds(30)
@max_body_length :infinity @max_body_length :infinity

View file

@ -4,6 +4,9 @@
defmodule Pleroma.ReverseProxy.Client.Hackney do defmodule Pleroma.ReverseProxy.Client.Hackney do
@behaviour Pleroma.ReverseProxy.Client @behaviour Pleroma.ReverseProxy.Client
@redirect_limit 5
require Logger
# In-app redirect handler to avoid Hackney redirect bugs: # In-app redirect handler to avoid Hackney redirect bugs:
# - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney) # - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
@ -31,26 +34,15 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
if opts[:follow_redirect] != false do if opts[:follow_redirect] != false do
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end) {_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
env = %{method: method, headers: headers, body: body, req_opts: req_opts}
res = :hackney.request(method, url, headers, body, req_opts) res = :hackney.request(method, url, headers, body, req_opts)
case res do case res do
{:ok, code, resp_headers, _client} when code in @redirect_statuses -> {:ok, code, resp_headers, _client} when code in @redirect_statuses ->
:hackney.request( redirect(url, resp_headers, env, @redirect_limit)
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
{:ok, code, resp_headers} when code in @redirect_statuses -> {:ok, code, resp_headers} when code in @redirect_statuses ->
:hackney.request( redirect(url, resp_headers, env, @redirect_limit)
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
_ -> _ ->
res res
@ -71,4 +63,32 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
@impl true @impl true
def close(ref), do: :hackney.close(ref) def close(ref), do: :hackney.close(ref)
defp redirect(url, resp_headers, env, limit) when limit == 0 do
new_url = absolute_redirect_url(url, resp_headers)
Logger.debug(
"#{__MODULE__}: Handling redirect #{url} -> #{new_url}; redirect limit was reached - returning response after final redirect"
)
:hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
end
defp redirect(url, resp_headers, env, limit) do
new_url = absolute_redirect_url(url, resp_headers)
Logger.debug("#{__MODULE__}: handling redirect #{url} -> #{new_url}; limit = #{limit}")
res = :hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
case res do
{:ok, code, new_resp_headers, _client} when code in @redirect_statuses ->
redirect(new_url, new_resp_headers, env, limit - 1)
{:ok, code, new_resp_headers} when code in @redirect_statuses ->
redirect(new_url, new_resp_headers, env, limit - 1)
_ ->
res
end
end
end end

View file

@ -1,11 +1,28 @@
defmodule Pleroma.Search do defmodule Pleroma.Search do
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Workers.SearchIndexingWorker alias Pleroma.Workers.SearchIndexingWorker
def add_to_index(%Pleroma.Activity{id: activity_id}) do @spec add_to_index(Activity.t()) :: {:ok, Oban.Job.t() | :noop} | {:error, Oban.Job.changeset()}
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id}) def add_to_index(%Activity{id: activity_id, object: %Object{} = object} = activity) do
|> Oban.insert() with {_, true} <- {:indexable, indexable?(activity)},
{_, "public"} <- {:visibility, Visibility.get_visibility(object)} do
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|> Oban.insert()
else
_ -> {:ok, :noop}
end
end end
def add_to_index(%Activity{id: activity_id}) do
case Activity.get_by_id_with_object(activity_id) do
%Activity{} = preloaded -> add_to_index(preloaded)
_ -> {:ok, :noop}
end
end
@spec remove_from_index(Object.t()) :: {:ok, Oban.Job.t()} | {:error, Oban.Job.changeset()}
def remove_from_index(%Pleroma.Object{id: object_id}) do def remove_from_index(%Pleroma.Object{id: object_id}) do
SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id}) SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id})
|> Oban.insert() |> Oban.insert()
@ -20,4 +37,44 @@ defmodule Pleroma.Search do
search_module = Pleroma.Config.get([Pleroma.Search, :module]) search_module = Pleroma.Config.get([Pleroma.Search, :module])
search_module.healthcheck_endpoints() search_module.healthcheck_endpoints()
end end
def object_to_search_data(%Object{} = object) do
data = object.data
content_str =
case data["content"] do
[nil | rest] -> to_string(rest)
str -> str
end
content =
with {:ok, scrubbed} <-
FastSanitize.Sanitizer.scrub(content_str, Pleroma.HTML.Scrubber.SearchIndexing),
trimmed <- String.trim(scrubbed) do
trimmed
end
# Make sure we have a non-empty string
if content != "" do
{:ok, published, _} = DateTime.from_iso8601(data["published"])
%{
id: object.id,
content: content,
ap: data["id"],
published: published |> DateTime.to_unix()
}
end
end
defp indexable?(%Activity{
data: %{"type" => "Create"},
object: %Object{
data: %{"content" => content, "published" => published, "type" => "Note"}
}
})
when not is_nil(content) and content not in ["", "."] and not is_nil(published),
do: true
defp indexable?(_), do: false
end end

View file

@ -4,6 +4,8 @@ defmodule Pleroma.Search.Meilisearch do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config.Getting, as: Config alias Pleroma.Config.Getting, as: Config
alias Pleroma.Object
alias Pleroma.Search
import Pleroma.Search.DatabaseSearch import Pleroma.Search.DatabaseSearch
import Ecto.Query import Ecto.Query
@ -118,66 +120,24 @@ defmodule Pleroma.Search.Meilisearch do
end end
end end
def object_to_search_data(object) do
# Only index public or unlisted Notes
if not is_nil(object) and object.data["type"] == "Note" and
not is_nil(object.data["content"]) and
not is_nil(object.data["published"]) and
(Pleroma.Constants.as_public() in object.data["to"] or
Pleroma.Constants.as_public() in object.data["cc"]) and
object.data["content"] not in ["", "."] do
data = object.data
content_str =
case data["content"] do
[nil | rest] -> to_string(rest)
str -> str
end
content =
with {:ok, scrubbed} <-
FastSanitize.Sanitizer.scrub(content_str, Pleroma.HTML.Scrubber.SearchIndexing),
trimmed <- String.trim(scrubbed) do
trimmed
end
# Make sure we have a non-empty string
if content != "" do
{:ok, published, _} = DateTime.from_iso8601(data["published"])
%{
id: object.id,
content: content,
ap: data["id"],
published: published |> DateTime.to_unix()
}
end
end
end
@impl true @impl true
def add_to_index(activity) do def add_to_index(%Activity{object: %Object{} = object} = activity) do
maybe_search_data = object_to_search_data(activity.object) search_data = Search.object_to_search_data(object)
if activity.data["type"] == "Create" and maybe_search_data do result =
result = meili_put(
meili_put( "/indexes/objects/documents",
"/indexes/objects/documents", [search_data]
[maybe_search_data] )
)
with {:ok, %{"status" => "enqueued"}} <- result do with {:ok, %{"status" => "enqueued"}} <- result do
# Added successfully # Added successfully
:ok
else
_ ->
# There was an error, report it
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
{:error, result}
end
else
# The post isn't something we can search, that's ok
:ok :ok
else
_ ->
# There was an error, report it
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
{:error, result}
end end
end end

View file

@ -4,11 +4,12 @@ defmodule Pleroma.Search.QdrantSearch do
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Config.Getting, as: Config alias Pleroma.Config.Getting, as: Config
alias Pleroma.Object
alias Pleroma.Search
alias __MODULE__.OpenAIClient alias __MODULE__.OpenAIClient
alias __MODULE__.QdrantClient alias __MODULE__.QdrantClient
import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1]
import Pleroma.Search.DatabaseSearch, only: [maybe_fetch: 3] import Pleroma.Search.DatabaseSearch, only: [maybe_fetch: 3]
@impl true @impl true
@ -82,23 +83,18 @@ defmodule Pleroma.Search.QdrantSearch do
end end
@impl true @impl true
def add_to_index(activity) do def add_to_index(%Activity{object: %Object{} = object} = activity) do
# This will only index public or unlisted notes search_data = Search.object_to_search_data(object)
maybe_search_data = object_to_search_data(activity.object)
if activity.data["type"] == "Create" and maybe_search_data do with {:ok, embedding} <- get_embedding(search_data.content),
with {:ok, embedding} <- get_embedding(maybe_search_data.content), {:ok, %{status: 200}} <-
{:ok, %{status: 200}} <- QdrantClient.put(
QdrantClient.put( "/collections/posts/points",
"/collections/posts/points", build_index_payload(activity, embedding)
build_index_payload(activity, embedding) ) do
) do
:ok
else
e -> {:error, e}
end
else
:ok :ok
else
e -> {:error, e}
end end
end end

View file

@ -104,7 +104,7 @@ defmodule Pleroma.Signature do
|> put_req_header("(request-target)", request_target) |> put_req_header("(request-target)", request_target)
|> put_req_header("@request-target", request_target) |> put_req_header("@request-target", request_target)
@http_signatures_impl.validate_conn(conn) @http_signatures_impl.validate_conn(conn) == true
end end
@spec validate_signature(Plug.Conn.t()) :: boolean() @spec validate_signature(Plug.Conn.t()) :: boolean()

View file

@ -93,7 +93,7 @@ defmodule Pleroma.Upload do
def store(upload, opts \\ []) do def store(upload, opts \\ []) do
opts = get_opts(opts) opts = get_opts(opts)
with {:ok, upload} <- prepare_upload(upload, opts), with {:ok, %__MODULE__{} = upload} <- prepare_upload(upload, opts),
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"}, upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload), {:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
description = get_description(upload), description = get_description(upload),

View file

@ -342,7 +342,7 @@ defmodule Pleroma.User.Backup do
dir, dir,
"outbox", "outbox",
fn a -> fn a ->
with {:ok, activity} <- Transmogrifier.prepare_outgoing(a.data) do with {:ok, activity} <- Transmogrifier.prepare_activity(a.data) do
{:ok, Map.delete(activity, "@context")} {:ok, Map.delete(activity, "@context")}
end end
end end

View file

@ -4,6 +4,7 @@
defmodule Pleroma.User.Search do defmodule Pleroma.User.Search do
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType
alias Pleroma.Instances.Instance
alias Pleroma.Pagination alias Pleroma.Pagination
alias Pleroma.User alias Pleroma.User
@ -88,12 +89,13 @@ defmodule Pleroma.User.Search do
|> filter_invisible_users() |> filter_invisible_users()
|> filter_internal_users() |> filter_internal_users()
|> filter_blocked_domains(for_user) |> filter_blocked_domains(for_user)
|> filter_unreachable_users()
|> fts_search(query_string) |> fts_search(query_string)
|> select_top_users(top_user_ids) |> select_top_users(top_user_ids)
|> trigram_rank(query_string) |> trigram_rank(query_string)
|> boost_search_rank(for_user, top_user_ids) |> boost_search_rank(for_user, top_user_ids)
|> subquery() |> subquery()
|> order_by(desc: :search_rank) |> order_by_search_rank(for_user)
|> maybe_restrict_local(for_user) |> maybe_restrict_local(for_user)
|> maybe_restrict_accepting_chat_messages(capabilities) |> maybe_restrict_accepting_chat_messages(capabilities)
|> filter_deactivated_users() |> filter_deactivated_users()
@ -196,6 +198,14 @@ defmodule Pleroma.User.Search do
defp filter_blocked_domains(query, _), do: query defp filter_blocked_domains(query, _), do: query
defp filter_unreachable_users(query) do
from(u in query,
left_join: i in Instance,
on: i.host == fragment("substring(? from '.*://([^/]*)')", u.ap_id),
where: is_nil(i.unreachable_since)
)
end
defp maybe_resolve(true, user, query) do defp maybe_resolve(true, user, query) do
case {limit(), user} do case {limit(), user} do
{:all, _} -> :noop {:all, _} -> :noop
@ -236,6 +246,16 @@ defmodule Pleroma.User.Search do
from(u in subquery(query), from(u in subquery(query),
select_merge: %{ select_merge: %{
search_type:
fragment(
"""
CASE WHEN (?) THEN 2
WHEN (?) THEN 1
ELSE 0 END
""",
u.id in ^top_user_ids,
u.id in ^friends_ids or u.id in ^followers_ids
),
search_rank: search_rank:
fragment( fragment(
""" """
@ -261,6 +281,14 @@ defmodule Pleroma.User.Search do
defp boost_search_rank(query, _for_user, top_user_ids) do defp boost_search_rank(query, _for_user, top_user_ids) do
from(u in subquery(query), from(u in subquery(query),
select_merge: %{ select_merge: %{
search_type:
fragment(
"""
CASE WHEN (?) THEN 2
ELSE 0 END
""",
u.id in ^top_user_ids
),
search_rank: search_rank:
fragment( fragment(
""" """
@ -273,4 +301,22 @@ defmodule Pleroma.User.Search do
} }
) )
end end
defp order_by_search_rank(query, %User{}) do
order_by(
query,
[u],
desc: u.search_type,
desc_nulls_last:
fragment(
"CASE WHEN ? = 1 THEN COALESCE(?, ?) ELSE NULL END",
u.search_type,
u.last_status_at,
u.last_active_at
),
desc: u.search_rank
)
end
defp order_by_search_rank(query, _), do: order_by(query, desc: :search_rank)
end end

View file

@ -45,7 +45,7 @@ defmodule Pleroma.UserRelationship do
do: exists?(unquote(relationship_type), source, target) do: exists?(unquote(relationship_type), source, target)
# `def get_block_expire_date/2`, `def get_mute_expire_date/2`, # `def get_block_expire_date/2`, `def get_mute_expire_date/2`,
# `def get_reblog_mute_expire_date/2`, `def get_notification_mute_exists?/2`, # `def get_reblog_mute_expire_date/2`, `def get_notification_mute_expire_date/2`,
# `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2` # `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2`
def unquote(:"get_#{relationship_type}_expire_date")(source, target), def unquote(:"get_#{relationship_type}_expire_date")(source, target),
do: get_expire_date(unquote(relationship_type), source, target) do: get_expire_date(unquote(relationship_type), source, target)

View file

@ -17,7 +17,7 @@ defmodule Pleroma.Utils do
dir dir
|> File.ls!() |> File.ls!()
|> Enum.map(&Path.join(dir, &1)) |> Enum.map(&Path.join(dir, &1))
|> Kernel.ParallelCompiler.compile() |> Kernel.ParallelCompiler.compile(return_diagnostics: true)
end end
@doc """ @doc """

View file

@ -30,7 +30,7 @@ defmodule Pleroma.Web do
def controller do def controller do
quote do quote do
use Phoenix.Controller, namespace: Pleroma.Web use Phoenix.Controller, formats: [json: "View", html: "View"]
import Plug.Conn import Plug.Conn
@ -42,7 +42,10 @@ defmodule Pleroma.Web do
plug(:set_put_layout) plug(:set_put_layout)
defp set_put_layout(conn, _) do defp set_put_layout(conn, _) do
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html")) case Pleroma.Config.get(:app_layout, "app.html") do
false -> put_layout(conn, false)
layout -> put_layout(conn, {Pleroma.Web.LayoutView, layout})
end
end end
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain # Marks plugs intentionally skipped and blocks their execution if present in plugs chain

View file

@ -1003,6 +1003,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp restrict_state(query, _), do: query defp restrict_state(query, _), do: query
defp restrict_assigned_account(query, %{assigned_account: assigned_account}) do
from(activity in query,
where: fragment("?->>'assigned_account' = ?", activity.data, ^assigned_account)
)
end
defp restrict_assigned_account(query, _), do: query
defp restrict_favorited_by(query, %{favorited_by: ap_id}) do defp restrict_favorited_by(query, %{favorited_by: ap_id}) do
from( from(
[_activity, object] in query, [_activity, object] in query,
@ -1471,6 +1479,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_actor(opts) |> restrict_actor(opts)
|> restrict_type(opts) |> restrict_type(opts)
|> restrict_state(opts) |> restrict_state(opts)
|> restrict_assigned_account(opts)
|> restrict_favorited_by(opts) |> restrict_favorited_by(opts)
|> restrict_blocked(restrict_blocked_opts) |> restrict_blocked(restrict_blocked_opts)
|> restrict_blockers_visibility(opts) |> restrict_blockers_visibility(opts)
@ -1609,6 +1618,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image() defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
defp normalize_image(_), do: nil defp normalize_image(_), do: nil
defp normalize_also_known_as(urls) when is_list(urls), do: urls
defp normalize_also_known_as(url) when is_binary(url), do: [url]
defp normalize_also_known_as(nil), do: []
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
Map.put(map, "name", description) Map.put(map, "name", description)
end end
@ -1664,44 +1677,80 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
show_birthday = !!birthday show_birthday = !!birthday
# if WebFinger request was already done, we probably have acct, otherwise with {:ok, nickname} <- nickname_from_actor(data, additional) do
# we request WebFinger here {:ok,
nickname = additional[:nickname_from_acct] || generate_nickname(data) %{
ap_id: data["id"],
uri: get_actor_url(data["url"]),
banner: normalize_image(data["image"]),
fields: fields,
emoji: emojis,
is_locked: is_locked,
is_discoverable: is_discoverable,
invisible: invisible,
avatar: normalize_image(data["icon"]),
name: data["name"],
follower_address: data["followers"],
following_address: data["following"],
featured_address: featured_address,
bio: data["summary"] || "",
actor_type: actor_type,
also_known_as: normalize_also_known_as(data["alsoKnownAs"]),
public_key: public_key,
inbox: data["inbox"],
shared_inbox: shared_inbox,
accepts_chat_messages: accepts_chat_messages,
birthday: birthday,
show_birthday: show_birthday,
pinned_objects: pinned_objects,
nickname: nickname
}}
end
end
%{ defp nickname_from_actor(data, additional) do
ap_id: data["id"], generated = generated_nickname(data)
uri: get_actor_url(data["url"]),
banner: normalize_image(data["image"]), case additional[:nickname_from_acct] do
fields: fields, ^generated when is_binary(generated) ->
emoji: emojis, {:ok, generated}
is_locked: is_locked,
is_discoverable: is_discoverable, acct when is_binary(acct) ->
invisible: invisible, with ^acct <- webfinger_nickname(data) do
avatar: normalize_image(data["icon"]), {:ok, acct}
name: data["name"], else
follower_address: data["followers"], _ -> {:error, {:webfinger_actor_mismatch, acct, data["id"]}}
following_address: data["following"], end
featured_address: featured_address,
bio: data["summary"] || "", _ ->
actor_type: actor_type, {:ok, generate_nickname(data)}
also_known_as: Map.get(data, "alsoKnownAs", []), end
public_key: public_key, end
inbox: data["inbox"],
shared_inbox: shared_inbox, defp generated_nickname(%{"preferredUsername" => username, "id" => ap_id})
accepts_chat_messages: accepts_chat_messages, when is_binary(username) and is_binary(ap_id) do
birthday: birthday, case URI.parse(ap_id) do
show_birthday: show_birthday, %URI{host: host} when is_binary(host) -> "#{username}@#{host}"
pinned_objects: pinned_objects, _ -> nil
nickname: nickname end
} end
defp generated_nickname(_), do: nil
defp webfinger_nickname(data) do
with generated when is_binary(generated) <- generated_nickname(data),
{:ok, %{"subject" => "acct:" <> acct, "ap_id" => ap_id}} <- WebFinger.finger(generated),
true <- ap_id == data["id"] do
acct
end
end end
defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do
generated = "#{username}@#{URI.parse(data["id"]).host}" generated = generated_nickname(data)
if Config.get([WebFinger, :update_nickname_on_user_fetch]) do if Config.get([WebFinger, :update_nickname_on_user_fetch]) do
case WebFinger.finger(generated) do case webfinger_nickname(data) do
{:ok, %{"subject" => "acct:" <> acct}} -> acct acct when is_binary(acct) -> acct
_ -> generated _ -> generated
end end
else else
@ -1781,9 +1830,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp collection_private(_data), do: {:ok, true} defp collection_private(_data), do: {:ok, true}
def user_data_from_user_object(data, additional \\ []) do def user_data_from_user_object(data, additional \\ []) do
with {:ok, data} <- MRF.filter(data) do with {:ok, data} <- MRF.filter(data),
{:ok, object_to_user_data(data, additional)} {:ok, data} <- object_to_user_data(data, additional) do
{:ok, data}
else else
{:error, _} = e -> e
e -> {:error, e} e -> {:error, e}
end end
end end
@ -1830,11 +1881,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
end end
@featured_collection_types ["OrderedCollection", "Collection"]
@featured_collection_page_types ["OrderedCollectionPage", "CollectionPage"]
@featured_collection_item_types @featured_collection_types ++ @featured_collection_page_types
def pin_data_from_featured_collection(%{ def pin_data_from_featured_collection(%{
"type" => type, "type" => type,
"orderedItems" => objects "orderedItems" => objects
}) })
when type in ["OrderedCollection", "Collection"] do when type in @featured_collection_item_types do
Map.new(objects, fn Map.new(objects, fn
%{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()} %{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()}
object_ap_id when is_binary(object_ap_id) -> {object_ap_id, NaiveDateTime.utc_now()} object_ap_id when is_binary(object_ap_id) -> {object_ap_id, NaiveDateTime.utc_now()}
@ -1852,7 +1907,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
def fetch_and_prepare_featured_from_ap_id(ap_id) do def fetch_and_prepare_featured_from_ap_id(ap_id) do
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
{:ok, pin_data_from_featured_collection(data)} {:ok, prepare_featured_collection(data)}
else else
e -> e ->
Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}") Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}")
@ -1860,6 +1915,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
end end
defp prepare_featured_collection(%{"orderedItems" => objects} = data) when is_list(objects) do
pin_data_from_featured_collection(data)
end
defp prepare_featured_collection(%{
"type" => type,
"first" => %{"type" => page_type} = first
})
when type in @featured_collection_types and page_type in @featured_collection_page_types do
pin_data_from_featured_collection(first)
end
defp prepare_featured_collection(%{"type" => type, "first" => first})
when type in @featured_collection_types and is_binary(first) do
case Fetcher.fetch_and_contain_remote_object_from_id(first) do
{:ok, data} ->
pin_data_from_featured_collection(data)
e ->
Logger.error("Could not decode featured collection page at fetch #{first}, #{inspect(e)}")
%{}
end
end
defp prepare_featured_collection(data) do
pin_data_from_featured_collection(data)
end
def enqueue_pin_fetches(%{pinned_objects: pins}) do def enqueue_pin_fetches(%{pinned_objects: pins}) do
# enqueue a task to fetch all pinned objects # enqueue a task to fetch all pinned objects
Enum.each(pins, fn {ap_id, _} -> Enum.each(pins, fn {ap_id, _} ->

View file

@ -303,7 +303,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end end
end end
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do def inbox(
%{
assigns: %{valid_signature: true, valid_host_header: true}
} = conn,
%{"nickname" => nickname} = params
) do
with {:recipient_exists, %User{} = recipient} <- with {:recipient_exists, %User{} = recipient} <-
{:recipient_exists, User.get_cached_by_nickname(nickname)}, {:recipient_exists, User.get_cached_by_nickname(nickname)},
{:sender_exists, {:ok, %User{} = actor}} <- {:sender_exists, {:ok, %User{} = actor}} <-
@ -342,13 +347,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end end
end end
def inbox(%{assigns: %{valid_signature: true}} = conn, params) do def inbox(%{assigns: %{valid_signature: true, valid_host_header: true}} = conn, params) do
Federator.incoming_ap_doc(params) Federator.incoming_ap_doc(params)
json(conn, "ok") json(conn, "ok")
end end
def inbox(%{assigns: %{valid_signature: false}} = conn, params) do def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
Federator.incoming_ap_doc(%{ Federator.incoming_failed_signature_ap_doc(%{
method: conn.method, method: conn.method,
req_headers: conn.req_headers, req_headers: conn.req_headers,
request_path: conn.request_path, request_path: conn.request_path,

View file

@ -332,21 +332,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()} @spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
def announce(actor, object, options \\ []) do def announce(actor, object, options \\ []) do
public? = Keyword.get(options, :public, false) visibility = Keyword.get(options, :visibility, "public")
to = {to, cc} =
cond do if actor.ap_id == Relay.ap_id() do
actor.ap_id == Relay.ap_id() -> {[actor.follower_address], []}
[actor.follower_address] else
Pleroma.Web.CommonAPI.Utils.get_to_and_cc_for_visibility(
public? and Visibility.local_public?(object) -> visibility,
[actor.follower_address, object.data["actor"], Utils.as_local_public()] actor.follower_address,
nil,
public? -> [object.data["actor"]]
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()] )
true ->
[actor.follower_address, object.data["actor"]]
end end
{:ok, {:ok,
@ -355,6 +352,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
"actor" => actor.ap_id, "actor" => actor.ap_id,
"object" => object.data["id"], "object" => object.data["id"],
"to" => to, "to" => to,
"cc" => cc,
"context" => object.data["context"], "context" => object.data["context"],
"type" => "Announce", "type" => "Announce",
"published" => Utils.make_date() "published" => Utils.make_date()

View file

@ -6,9 +6,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
use Ecto.Schema use Ecto.Schema
alias Pleroma.EctoType.ActivityPub.ObjectValidators alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
alias Pleroma.Web.ActivityPub.Transmogrifier alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI.Utils
import Ecto.Changeset import Ecto.Changeset
@ -26,6 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
end end
field(:replies, {:array, ObjectValidators.ObjectID}, default: []) field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
field(:source, :map)
end end
def cast_and_apply(data) do def cast_and_apply(data) do
@ -80,6 +84,113 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
def fix_attachments(data), do: data def fix_attachments(data), do: data
defp remote_mention_resolver(
%{"id" => ap_id, "tag" => tags},
"@" <> nickname = mention,
buffer,
opts,
acc
)
when is_binary(ap_id) and is_list(tags) do
initial_host =
ap_id
|> URI.parse()
|> Map.get(:host)
with mention_tag when not is_nil(mention_tag) <-
Enum.find(tags, &mention_tag?(&1, mention, initial_host)),
href when is_binary(href) <- mention_tag["href"],
%User{} = user <- User.get_cached_by_ap_id(href) do
link = Pleroma.Formatter.mention_from_user(user, opts)
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
else
_ -> {buffer, acc}
end
end
defp remote_mention_resolver(_object, _mention, buffer, _opts, acc), do: {buffer, acc}
defp mention_tag?(%{"type" => "Mention", "name" => name}, mention, initial_host)
when is_binary(name) do
name == mention || mention == "#{name}@#{initial_host}"
end
defp mention_tag?(_tag, _mention, _initial_host), do: false
defp scrub_content(%{"content" => content} = object) when is_binary(content) do
Map.put(object, "content", HTML.filter_tags(content))
end
defp scrub_content(object), do: object
defp mfm_parse_limit do
min(Pleroma.Config.get([:instance, :limit]), Pleroma.Config.get([:instance, :remote_limit]))
end
defp normalize_source(%{"source" => source} = object) when is_binary(source) do
object
|> Map.put("source", %{"content" => source})
|> normalize_source()
end
defp normalize_source(%{"source" => source} = object) when is_map(source) do
source =
case source["content"] do
content when is_binary(content) ->
if String.length(content) <= mfm_parse_limit() do
source
else
Map.delete(source, "content")
end
nil ->
source
_ ->
Map.delete(source, "content")
end
Map.put(object, "source", source)
end
defp normalize_source(object), do: object
defp fix_misskey_content(%{"htmlMfm" => true, "content" => content} = object)
when is_binary(content) do
Map.put(object, "content", HTML.filter_tags(content))
end
defp fix_misskey_content(%{"htmlMfm" => true} = object), do: object
defp fix_misskey_content(
%{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object
)
when is_binary(content) do
mention_handler = fn nick, buffer, opts, acc ->
remote_mention_resolver(object, nick, buffer, opts, acc)
end
{linked, _mentions, _tags} =
Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
Map.put(object, "content", linked)
end
defp fix_misskey_content(%{"source" => %{"mediaType" => "text/x.misskeymarkdown"}} = object),
do: scrub_content(object)
defp fix_misskey_content(%{"_misskey_content" => content} = object) when is_binary(content) do
object
|> Map.put("source", %{
"content" => content,
"mediaType" => "text/x.misskeymarkdown"
})
|> Map.delete("_misskey_content")
|> fix_misskey_content()
end
defp fix_misskey_content(object), do: object
defp fix(data) do defp fix(data) do
data data
|> CommonFixes.fix_actor() |> CommonFixes.fix_actor()
@ -88,6 +199,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|> fix_tag() |> fix_tag()
|> fix_replies() |> fix_replies()
|> fix_attachments() |> fix_attachments()
|> normalize_source()
|> fix_misskey_content()
|> CommonFixes.fix_quote_url() |> CommonFixes.fix_quote_url()
|> CommonFixes.fix_likes() |> CommonFixes.fix_likes()
|> Transmogrifier.fix_emoji() |> Transmogrifier.fix_emoji()

View file

@ -32,6 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
quote bind_quoted: binding() do quote bind_quoted: binding() do
field(:content, :string) field(:content, :string)
field(:contentMap, ObjectValidators.ContentLanguageMap) field(:contentMap, ObjectValidators.ContentLanguageMap)
field(:htmlMfm, :boolean)
field(:published, ObjectValidators.DateTime) field(:published, ObjectValidators.DateTime)
field(:updated, ObjectValidators.DateTime) field(:updated, ObjectValidators.DateTime)

View file

@ -28,6 +28,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
end end
field(:closed, ObjectValidators.DateTime) field(:closed, ObjectValidators.DateTime)
field(:votersCount, :integer)
field(:voters, {:array, ObjectValidators.ObjectID}, default: []) field(:voters, {:array, ObjectValidators.ObjectID}, default: [])
field(:nonAnonymous, :boolean) field(:nonAnonymous, :boolean)
embeds_many(:anyOf, QuestionOptionsValidator) embeds_many(:anyOf, QuestionOptionsValidator)

View file

@ -75,15 +75,40 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
end end
end end
# For remote Updates, verify the host is the same. # For remote Updates, verify the Actor is the same
def validate_updating_rights_remote(cng) do def validate_updating_rights_remote(cng) do
with actor = get_field(cng, :actor), with actor = get_field(cng, :actor),
object = get_field(cng, :object), object = get_field(cng, :object),
{:ok, object_id} <- ObjectValidators.ObjectID.cast(object), {:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
actor_uri <- URI.parse(actor), entity <-
object_uri <- URI.parse(object_id), Object.normalize(object_id, fetch: false) || User.get_cached_by_ap_id(object_id) do
true <- actor_uri.host == object_uri.host do case entity do
cng # Actor must own Object to update it
%Object{} ->
if actor == entity.data["actor"] do
cng
else
cng
|> add_error(:object, "Can't be updated by this actor")
end
# Actor must only be allowed to update itself
%User{} ->
if actor == entity.ap_id do
cng
else
cng
|> add_error(:object, "Can't be updated by this actor")
end
nil ->
cng
|> add_error(:object, "Can't be updated by this actor")
_ ->
cng
|> add_error(:object, "Update is neither for Object or Actor")
end
else else
_e -> _e ->
cng cng

View file

@ -79,7 +79,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Determine if an activity can be represented by running it through Transmogrifier. Determine if an activity can be represented by running it through Transmogrifier.
""" """
def representable?(%Activity{} = activity) do def representable?(%Activity{} = activity) do
with {:ok, _data} <- @transmogrifier_impl.prepare_outgoing(activity.data) do with {:ok, _data} <- @transmogrifier_impl.prepare_activity(activity.data) do
true true
else else
_e -> _e ->
@ -102,14 +102,14 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Logger.debug("Federating #{ap_id} to #{inbox}") Logger.debug("Federating #{ap_id} to #{inbox}")
uri = %{path: path} = URI.parse(inbox) uri = %{path: path} = URI.parse(inbox)
{:ok, data} = @transmogrifier_impl.prepare_outgoing(activity.data) {:ok, data} = @transmogrifier_impl.prepare_activity(activity.data)
{actor, data} = {actor, data} =
with {_, false} <- {:actor_changed?, data["actor"] != activity.data["actor"]} do with {_, false} <- {:actor_changed?, data["actor"] != activity.data["actor"]} do
{actor, data} {actor, data}
else else
{:actor_changed?, true} -> {:actor_changed?, true} ->
# If prepare_outgoing changes the actor, re-get it from the db # If prepare_activity changes the actor, re-get it from the db
new_actor = User.get_cached_by_ap_id(data["actor"]) new_actor = User.get_cached_by_ap_id(data["actor"])
{new_actor, data} {new_actor, data}
end end

View file

@ -430,6 +430,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end) end)
end end
defp reject_third_party_report(%User{local: false}, %User{local: false} = account) do
{:reject, "[Transmogrifier] third-party report: #{account.ap_id}"}
end
defp reject_third_party_report(_, _), do: :ok
def handle_incoming(data, options \\ []) do def handle_incoming(data, options \\ []) do
data data
|> fix_recursive(&strip_internal_fields/1) |> fix_recursive(&strip_internal_fields/1)
@ -444,9 +450,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
) do ) do
with context <- data["context"] || Utils.generate_context_id(), with context <- data["context"] || Utils.generate_context_id(),
content <- data["content"] || "", content <- data["content"] || "",
objects <- List.wrap(objects),
%User{} = actor <- User.get_cached_by_ap_id(actor), %User{} = actor <- User.get_cached_by_ap_id(actor),
# Reduce the object list to find the reported user. # Reduce the object list to find the reported user.
%User{} = account <- get_reported(objects), %User{} = account <- get_reported(objects),
:ok <- reject_third_party_report(actor, account),
# Remove the reported user from the object list. # Remove the reported user from the object list.
statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
%{ %{
@ -783,7 +791,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
def set_replies(obj_data), do: obj_data def set_replies(obj_data), do: obj_data
# Prepares the object of an outgoing create activity. defp set_voters_count(%{"voters" => [_ | _] = voters} = obj) do
Map.merge(obj, %{"votersCount" => length(voters)})
end
defp set_voters_count(obj), do: obj
# Prepares and sanitizes the object for federation.
def prepare_object(object) do def prepare_object(object) do
object object
|> add_hashtags |> add_hashtags
@ -795,6 +809,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> set_reply_to_uri |> set_reply_to_uri
|> set_quote_url |> set_quote_url
|> set_replies |> set_replies
|> set_voters_count
|> CommonFixes.maybe_add_content_map() |> CommonFixes.maybe_add_content_map()
|> strip_internal_fields |> strip_internal_fields
|> strip_internal_tags |> strip_internal_tags
@ -824,7 +839,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# internal -> Mastodon # internal -> Mastodon
# """ # """
def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data) def prepare_activity(%{"type" => activity_type, "object" => object_id} = data)
when activity_type in ["Create", "Listen"] do when activity_type in ["Create", "Listen"] do
object = object =
object_id object_id
@ -840,7 +855,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data} {:ok, data}
end end
def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data) def prepare_activity(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
when objtype in Pleroma.Constants.updatable_object_types() do when objtype in Pleroma.Constants.updatable_object_types() do
data = data =
data data
@ -851,7 +866,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data} {:ok, data}
end end
def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data) def prepare_activity(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
when objtype in Pleroma.Constants.actor_types() do when objtype in Pleroma.Constants.actor_types() do
object = object =
object object
@ -868,11 +883,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data} {:ok, data}
end end
def prepare_outgoing(%{"type" => "Update", "object" => %{}} = data) do def prepare_activity(%{"type" => "Update", "object" => %{}} = data) do
raise "Requested to serve an Update for non-updateable object type: #{inspect(data)}" raise "Requested to serve an Update for non-updateable object type: #{inspect(data)}"
end end
def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do def prepare_activity(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
object = object =
object_id object_id
|> Object.normalize(fetch: false) |> Object.normalize(fetch: false)
@ -895,7 +910,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# Mastodon Accept/Reject requires a non-normalized object containing the actor URIs, # Mastodon Accept/Reject requires a non-normalized object containing the actor URIs,
# because of course it does. # because of course it does.
def prepare_outgoing(%{"type" => "Accept"} = data) do def prepare_activity(%{"type" => "Accept"} = data) do
with follow_activity <- Activity.normalize(data["object"]) do with follow_activity <- Activity.normalize(data["object"]) do
object = %{ object = %{
"actor" => follow_activity.actor, "actor" => follow_activity.actor,
@ -913,7 +928,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
end end
def prepare_outgoing(%{"type" => "Reject"} = data) do def prepare_activity(%{"type" => "Reject"} = data) do
with follow_activity <- Activity.normalize(data["object"]) do with follow_activity <- Activity.normalize(data["object"]) do
object = %{ object = %{
"actor" => follow_activity.actor, "actor" => follow_activity.actor,
@ -931,7 +946,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
end end
def prepare_outgoing(%{"type" => "Flag"} = data) do def prepare_activity(%{"type" => "Flag"} = data) do
with {:ok, stripped_activity} <- Utils.strip_report_status_data(data), with {:ok, stripped_activity} <- Utils.strip_report_status_data(data),
stripped_activity <- Utils.maybe_anonymize_reporter(stripped_activity), stripped_activity <- Utils.maybe_anonymize_reporter(stripped_activity),
stripped_activity <- Map.merge(stripped_activity, Utils.make_json_ld_header()) do stripped_activity <- Map.merge(stripped_activity, Utils.make_json_ld_header()) do
@ -939,7 +954,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end end
end end
def prepare_outgoing(%{"type" => _type} = data) do def prepare_activity(%{"type" => _type} = data) do
data = data =
data data
|> strip_internal_fields |> strip_internal_fields

View file

@ -7,5 +7,5 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.API do
Behaviour for the subset of Transmogrifier used by Publisher. Behaviour for the subset of Transmogrifier used by Publisher.
""" """
@callback prepare_outgoing(map()) :: {:ok, map()} | {:error, term()} @callback prepare_activity(map()) :: {:ok, map()} | {:error, term()}
end end

View file

@ -120,7 +120,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"https://www.w3.org/ns/activitystreams", "https://www.w3.org/ns/activitystreams",
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld", "#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
%{ %{
"@language" => get_language(data) "@language" => get_language(data),
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
} }
] ]
} }
@ -863,6 +864,34 @@ defmodule Pleroma.Web.ActivityPub.Utils do
def update_report_state(_, _), do: {:error, "Unsupported state"} def update_report_state(_, _), do: {:error, "Unsupported state"}
def assign_report_to_account(%Activity{} = activity, nil = _account) do
new_data = Map.delete(activity.data, "assigned_account")
activity
|> Changeset.change(data: new_data)
|> Repo.update()
end
def assign_report_to_account(%Activity{} = activity, account) do
new_data = Map.put(activity.data, "assigned_account", account)
activity
|> Changeset.change(data: new_data)
|> Repo.update()
end
def assign_report_to_account(activity_ids, account) do
activities_num = length(activity_ids)
from(a in Activity, where: a.id in ^activity_ids)
|> update(set: [data: fragment("jsonb_set(data, '{assigned_account}', ?)", ^account)])
|> Repo.update_all([])
|> case do
{^activities_num, _} -> :ok
_ -> {:error, activity_ids}
end
end
def strip_report_status_data(%Activity{} = activity) do def strip_report_status_data(%Activity{} = activity) do
with {:ok, new_data} <- strip_report_status_data(activity.data) do with {:ok, new_data} <- strip_report_status_data(activity.data) do
{:ok, %{activity | data: new_data}} {:ok, %{activity | data: new_data}}

View file

@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
end end
def render("object.json", %{object: %Activity{} = activity}) do def render("object.json", %{object: %Activity{} = activity}) do
{:ok, ap_data} = Transmogrifier.prepare_outgoing(activity.data) {:ok, ap_data} = Transmogrifier.prepare_activity(activity.data)
ap_data ap_data
end end

View file

@ -35,32 +35,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do
def render("endpoints.json", _), do: %{} def render("endpoints.json", _), do: %{}
def render("service.json", %{user: user}) do def render("service.json", %{user: user}) do
{:ok, _, public_key} = Keys.keys_from_pem(user.keys) Map.merge(common_actor_fields(user), %{
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
endpoints = render("endpoints.json", %{user: user})
%{
"id" => user.ap_id,
"type" => "Application", "type" => "Application",
"following" => "#{user.ap_id}/following",
"followers" => "#{user.ap_id}/followers",
"inbox" => "#{user.ap_id}/inbox",
"outbox" => "#{user.ap_id}/outbox",
"name" => "Pleroma", "name" => "Pleroma",
"summary" => "summary" =>
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.", "An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
"url" => user.ap_id,
"manuallyApprovesFollowers" => false, "manuallyApprovesFollowers" => false,
"publicKey" => %{
"id" => "#{user.ap_id}#main-key",
"owner" => user.ap_id,
"publicKeyPem" => public_key
},
"endpoints" => endpoints,
"invisible" => User.invisible?(user) "invisible" => User.invisible?(user)
} })
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
@ -77,13 +59,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
end end
def render("user.json", %{user: user}) do def render("user.json", %{user: user}) do
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
user = User.sanitize_html(user) user = User.sanitize_html(user)
endpoints = render("endpoints.json", %{user: user})
emoji_tags = Transmogrifier.take_emoji_tags(user) emoji_tags = Transmogrifier.take_emoji_tags(user)
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue")) fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
@ -102,25 +79,9 @@ defmodule Pleroma.Web.ActivityPub.UserView do
do: Date.to_iso8601(user.birthday), do: Date.to_iso8601(user.birthday),
else: nil else: nil
%{ Map.merge(common_actor_fields(user), %{
"id" => user.ap_id,
"type" => user.actor_type,
"following" => "#{user.ap_id}/following",
"followers" => "#{user.ap_id}/followers",
"inbox" => "#{user.ap_id}/inbox",
"outbox" => "#{user.ap_id}/outbox",
"featured" => "#{user.ap_id}/collections/featured", "featured" => "#{user.ap_id}/collections/featured",
"preferredUsername" => user.nickname, "preferredUsername" => user.nickname,
"name" => user.name,
"summary" => user.bio,
"url" => user.ap_id,
"manuallyApprovesFollowers" => user.is_locked,
"publicKey" => %{
"id" => "#{user.ap_id}#main-key",
"owner" => user.ap_id,
"publicKeyPem" => public_key
},
"endpoints" => endpoints,
"attachment" => fields, "attachment" => fields,
"tag" => emoji_tags, "tag" => emoji_tags,
# Note: key name is indeed "discoverable" (not an error) # Note: key name is indeed "discoverable" (not an error)
@ -130,7 +91,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
"vcard:bday" => birthday, "vcard:bday" => birthday,
"webfinger" => "acct:#{User.full_nickname(user)}", "webfinger" => "acct:#{User.full_nickname(user)}",
"published" => Pleroma.Web.CommonAPI.Utils.to_masto_date(user.inserted_at) "published" => Pleroma.Web.CommonAPI.Utils.to_masto_date(user.inserted_at)
} })
|> Map.merge( |> Map.merge(
maybe_make_image( maybe_make_image(
&User.avatar_url/2, &User.avatar_url/2,
@ -283,7 +244,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
}) do }) do
collection = collection =
Enum.map(activities, fn activity -> Enum.map(activities, fn activity ->
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data) {:ok, data} = Transmogrifier.prepare_activity(activity.data)
data data
end) end)
@ -309,6 +270,33 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
defp common_actor_fields(%User{} = user) do
endpoints = render("endpoints.json", %{user: user})
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
public_key = :public_key.pem_encode([public_key])
%{
"id" => user.ap_id,
"type" => user.actor_type,
"following" => "#{user.ap_id}/following",
"followers" => "#{user.ap_id}/followers",
"inbox" => "#{user.ap_id}/inbox",
"outbox" => "#{user.ap_id}/outbox",
"name" => user.name,
"summary" => user.bio,
"url" => user.ap_id,
"manuallyApprovesFollowers" => user.is_locked,
"endpoints" => endpoints,
"publicKey" => %{
"id" => "#{user.ap_id}#main-key",
"owner" => user.ap_id,
"publicKeyPem" => public_key
}
}
end
defp maybe_put_total_items(map, false, _total), do: map defp maybe_put_total_items(map, false, _total), do: map
defp maybe_put_total_items(map, true, total) do defp maybe_put_total_items(map, true, total) do

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