Compare commits

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

241 commits

Author SHA1 Message Date
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
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
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
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
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
212 changed files with 6374 additions and 1349 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
.tool-versions
# mise
mise.toml
# Editor temp files
*~
*#

View file

@ -0,0 +1,19 @@
when:
- event: pull_request
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,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}

96
.woodpecker/docker.yaml Normal file
View file

@ -0,0 +1,96 @@
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
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

77
.woodpecker/lint.yaml Normal file
View file

@ -0,0 +1,77 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
- 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,44 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
- 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,44 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
- 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/).
## 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
### Security

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

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

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

@ -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 @@
Add backend support for Misskey Markdown (MFM) posts

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

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

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

View file

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

View file

@ -203,7 +203,8 @@ config :pleroma, :instance,
"text/plain",
"text/html",
"text/markdown",
"text/bbcode"
"text/bbcode",
"text/x.misskeymarkdown"
],
autofollowed_nicknames: [],
autofollowing_nicknames: [],
@ -775,7 +776,7 @@ config :pleroma, :frontends,
"name" => "pleroma-fe",
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
"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"
},
"fedi-fe" => %{
@ -960,6 +961,15 @@ config :pleroma, Pleroma.Search.QdrantSearch,
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
# of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs"

View file

@ -815,7 +815,8 @@ config :pleroma, :config_description, [
"text/plain",
"text/html",
"text/markdown",
"text/bbcode"
"text/bbcode",
"text/x.misskeymarkdown"
]
},
%{
@ -1394,7 +1395,13 @@ config :pleroma, :config_description, [
label: "Post Content Type",
type: {:dropdown, :atom},
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,

View file

@ -169,4 +169,18 @@ This forcibly removes any enabled MRF that does not exist and will fix the abili
=== "From Source"
```sh
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
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
have not migrated the config to the database.
still applied. Consider running the `mix pleroma.config filter_whitelisted` task
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:
```elixir

View file

@ -665,6 +665,7 @@ Status: 404
- *optional* `limit`: **integer** the number of records to retrieve
- *optional* `page`: **integer** page number
- *optional* `page_size`: **integer** number of log entries per page (default is `50`)
- *optional* `assigned_account`: **string** assigned account ID
- Response:
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
- On success: JSON, returns a list of reports, where:
@ -749,6 +750,7 @@ Status: 404
"url": "https://pleroma.example.org/users/lain",
"username": "lain"
},
"assigned_account": null,
"content": "Please delete it",
"created_at": "2019-04-29T19:48:15.000Z",
"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:
- On failure:
- 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 `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
@ -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.
- `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
- `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

View file

@ -690,6 +690,7 @@ Audio scrobbling in Pleroma is **deprecated**.
* `album`: the album of the media playing [optional]
* `artist`: the artist 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
# Emoji Reactions

View file

@ -234,6 +234,61 @@ defmodule Mix.Tasks.Pleroma.Config do
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()
def migrate_to_db(file_path \\ nil) 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, "ALTER SEQUENCE config_id_seq RESTART;")
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

View file

@ -226,7 +226,12 @@ defmodule Mix.Tasks.Pleroma.Database do
DELETE FROM hashtags AS ht
WHERE NOT EXISTS (
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()

View file

@ -22,7 +22,7 @@ defmodule Mix.Tasks.Pleroma.OpenapiSpec do
else
{_, 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"
end

View file

@ -72,7 +72,7 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
query,
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.chunk_every(chunk_size)
|> Stream.transform(0, fn objects, acc ->

View file

@ -38,7 +38,7 @@ defmodule Pleroma.Activity.HTML do
def invalidate_cache_for(activity_id) do
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)
end

View file

@ -19,7 +19,7 @@ defmodule Pleroma.Bookmark do
schema "bookmarks" do
belongs_to(:user, User, 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()
end
@ -38,7 +38,7 @@ defmodule Pleroma.Bookmark do
|> validate_required([:user_id, :activity_id])
|> unique_constraint(:activity_id, name: :bookmarks_user_id_activity_id_index)
|> 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]
)
end
@ -76,11 +76,4 @@ defmodule Pleroma.Bookmark do
|> Repo.one()
|> Repo.delete()
end
def set_folder(bookmark, folder_id) do
bookmark
|> cast(%{folder_id: folder_id}, [:folder_id])
|> validate_required([:folder_id])
|> Repo.update()
end
end

View file

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

View file

@ -10,6 +10,7 @@ defmodule Pleroma.ConfigDB do
import Pleroma.Web.Gettext
alias __MODULE__
alias Pleroma.EctoType.Config.RateLimit
alias Pleroma.Repo
@type t :: %__MODULE__{}
@ -60,8 +61,59 @@ defmodule Pleroma.ConfigDB do
|> cast(params, [:key, :group, :value])
|> validate_required([:key, :group, :value])
|> unique_constraint(:key, name: :config_group_key_index)
|> validate_rate_limit()
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
%ConfigDB{}
|> changeset(params)

View file

@ -22,13 +22,14 @@ defmodule Pleroma.Constants do
"generator",
"rules",
"language",
"voters"
"voters",
"assigned_account"
]
)
const(static_only_files,
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,

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})
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
{html_escape(text, type), mentions, hashtags}
end
@ -135,6 +142,10 @@ defmodule Pleroma.Formatter do
HTML.filter_tags(text)
end
def html_escape(text, "text/x.misskeymarkdown") do
HTML.filter_tags(text)
end
def html_escape(text, "text/plain") do
Regex.split(@link_regex, text, include_captures: true)
|> Enum.map_every(2, fn chunk ->

View file

@ -75,8 +75,8 @@ defmodule Pleroma.Frontend do
end
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"])
Logger.info("Downloading pre-built bundle for #{frontend_info["name"]} from #{url}")
with {:ok, %{status: 200, body: zip_body}} <-
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
Logger.info("Starting gopher server on #{port}")
Process.flag(:trap_exit, true)
listener = :gopher
{:ok, _pid} =
:ranch.start_listener(
:gopher,
listener,
:ranch_tcp,
%{
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

View file

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

View file

@ -78,7 +78,7 @@ defmodule Pleroma.Marker do
defp get_marker(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}
end
end

View file

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

View file

@ -132,11 +132,18 @@ defmodule Pleroma.ModerationLog do
end
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 =
attrs
|> prepare_log_data
|> 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)})
insert_log_entry_with_message(%ModerationLog{data: data})
@ -441,6 +448,35 @@ defmodule Pleroma.ModerationLog do
" with '#{state}' state"
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(
%ModerationLog{
data: %{

View file

@ -372,12 +372,28 @@ defmodule Pleroma.Object do
option
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 =
object.data
|> Map.put(key, options)
|> Map.put("voters", voters)
|> Map.put("votersCount", voters_count)
object
|> Object.change(%{data: data})

View file

@ -5,7 +5,10 @@
defmodule Pleroma.ReleaseTasks do
@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)
case task do
@ -16,6 +19,20 @@ defmodule Pleroma.ReleaseTasks do
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
module_name =
task

View file

@ -12,7 +12,7 @@ defmodule Pleroma.ReverseProxy do
@keep_resp_headers @resp_cache_headers ++
~w(content-length content-type content-disposition content-encoding) ++
~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]
@max_read_duration :timer.seconds(30)
@max_body_length :infinity

View file

@ -4,6 +4,9 @@
defmodule Pleroma.ReverseProxy.Client.Hackney do
@behaviour Pleroma.ReverseProxy.Client
@redirect_limit 5
require Logger
# In-app redirect handler to avoid Hackney redirect bugs:
# - 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
{_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)
case res do
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
:hackney.request(
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
redirect(url, resp_headers, env, @redirect_limit)
{:ok, code, resp_headers} when code in @redirect_statuses ->
:hackney.request(
method,
absolute_redirect_url(url, resp_headers),
headers,
body,
req_opts
)
redirect(url, resp_headers, env, @redirect_limit)
_ ->
res
@ -71,4 +63,32 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
@impl true
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

View file

@ -1,11 +1,28 @@
defmodule Pleroma.Search do
alias Pleroma.Activity
alias Pleroma.Object
alias Pleroma.Web.ActivityPub.Visibility
alias Pleroma.Workers.SearchIndexingWorker
def add_to_index(%Pleroma.Activity{id: activity_id}) do
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|> Oban.insert()
@spec add_to_index(Activity.t()) :: {:ok, Oban.Job.t() | :noop} | {:error, Oban.Job.changeset()}
def add_to_index(%Activity{id: activity_id, object: %Object{} = object} = activity) do
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
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
SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id})
|> Oban.insert()
@ -20,4 +37,44 @@ defmodule Pleroma.Search do
search_module = Pleroma.Config.get([Pleroma.Search, :module])
search_module.healthcheck_endpoints()
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

View file

@ -4,6 +4,8 @@ defmodule Pleroma.Search.Meilisearch do
alias Pleroma.Activity
alias Pleroma.Config.Getting, as: Config
alias Pleroma.Object
alias Pleroma.Search
import Pleroma.Search.DatabaseSearch
import Ecto.Query
@ -118,66 +120,24 @@ defmodule Pleroma.Search.Meilisearch do
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
def add_to_index(activity) do
maybe_search_data = object_to_search_data(activity.object)
def add_to_index(%Activity{object: %Object{} = object} = activity) do
search_data = Search.object_to_search_data(object)
if activity.data["type"] == "Create" and maybe_search_data do
result =
meili_put(
"/indexes/objects/documents",
[maybe_search_data]
)
result =
meili_put(
"/indexes/objects/documents",
[search_data]
)
with {:ok, %{"status" => "enqueued"}} <- result do
# 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
with {:ok, %{"status" => "enqueued"}} <- result do
# 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
end

View file

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

View file

@ -93,7 +93,7 @@ defmodule Pleroma.Upload do
def store(upload, opts \\ []) do
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}"},
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
description = get_description(upload),

View file

@ -342,7 +342,7 @@ defmodule Pleroma.User.Backup do
dir,
"outbox",
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")}
end
end

View file

@ -45,7 +45,7 @@ defmodule Pleroma.UserRelationship do
do: exists?(unquote(relationship_type), source, target)
# `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 unquote(:"get_#{relationship_type}_expire_date")(source, target),
do: get_expire_date(unquote(relationship_type), source, target)

View file

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

View file

@ -1003,6 +1003,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
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
from(
[_activity, object] in query,
@ -1471,6 +1479,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> restrict_actor(opts)
|> restrict_type(opts)
|> restrict_state(opts)
|> restrict_assigned_account(opts)
|> restrict_favorited_by(opts)
|> restrict_blocked(restrict_blocked_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(_), 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
Map.put(map, "name", description)
end
@ -1664,44 +1677,80 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
show_birthday = !!birthday
# if WebFinger request was already done, we probably have acct, otherwise
# we request WebFinger here
nickname = additional[:nickname_from_acct] || generate_nickname(data)
with {:ok, nickname} <- nickname_from_actor(data, additional) do
{:ok,
%{
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
%{
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: Map.get(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
}
defp nickname_from_actor(data, additional) do
generated = generated_nickname(data)
case additional[:nickname_from_acct] do
^generated when is_binary(generated) ->
{:ok, generated}
acct when is_binary(acct) ->
with ^acct <- webfinger_nickname(data) do
{:ok, acct}
else
_ -> {:error, {:webfinger_actor_mismatch, acct, data["id"]}}
end
_ ->
{:ok, generate_nickname(data)}
end
end
defp generated_nickname(%{"preferredUsername" => username, "id" => ap_id})
when is_binary(username) and is_binary(ap_id) do
case URI.parse(ap_id) do
%URI{host: host} when is_binary(host) -> "#{username}@#{host}"
_ -> nil
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
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
case WebFinger.finger(generated) do
{:ok, %{"subject" => "acct:" <> acct}} -> acct
case webfinger_nickname(data) do
acct when is_binary(acct) -> acct
_ -> generated
end
else
@ -1781,9 +1830,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp collection_private(_data), do: {:ok, true}
def user_data_from_user_object(data, additional \\ []) do
with {:ok, data} <- MRF.filter(data) do
{:ok, object_to_user_data(data, additional)}
with {:ok, data} <- MRF.filter(data),
{:ok, data} <- object_to_user_data(data, additional) do
{:ok, data}
else
{:error, _} = e -> e
e -> {:error, e}
end
end

View file

@ -348,7 +348,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
end
def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
Federator.incoming_ap_doc(%{
Federator.incoming_failed_signature_ap_doc(%{
method: conn.method,
req_headers: conn.req_headers,
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()}
def announce(actor, object, options \\ []) do
public? = Keyword.get(options, :public, false)
visibility = Keyword.get(options, :visibility, "public")
to =
cond do
actor.ap_id == Relay.ap_id() ->
[actor.follower_address]
public? and Visibility.local_public?(object) ->
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
public? ->
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
true ->
[actor.follower_address, object.data["actor"]]
{to, cc} =
if actor.ap_id == Relay.ap_id() do
{[actor.follower_address], []}
else
Pleroma.Web.CommonAPI.Utils.get_to_and_cc_for_visibility(
visibility,
actor.follower_address,
nil,
[object.data["actor"]]
)
end
{:ok,
@ -355,6 +352,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
"actor" => actor.ap_id,
"object" => object.data["id"],
"to" => to,
"cc" => cc,
"context" => object.data["context"],
"type" => "Announce",
"published" => Utils.make_date()

View file

@ -6,9 +6,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
use Ecto.Schema
alias Pleroma.EctoType.ActivityPub.ObjectValidators
alias Pleroma.HTML
alias Pleroma.User
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
alias Pleroma.Web.ActivityPub.Transmogrifier
alias Pleroma.Web.CommonAPI.Utils
import Ecto.Changeset
@ -26,6 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
end
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
field(:source, :map)
end
def cast_and_apply(data) do
@ -80,6 +84,113 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
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
data
|> CommonFixes.fix_actor()
@ -88,6 +199,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|> fix_tag()
|> fix_replies()
|> fix_attachments()
|> normalize_source()
|> fix_misskey_content()
|> CommonFixes.fix_quote_url()
|> CommonFixes.fix_likes()
|> Transmogrifier.fix_emoji()

View file

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

View file

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

View file

@ -75,15 +75,40 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
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
with actor = get_field(cng, :actor),
object = get_field(cng, :object),
{:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
actor_uri <- URI.parse(actor),
object_uri <- URI.parse(object_id),
true <- actor_uri.host == object_uri.host do
cng
entity <-
Object.normalize(object_id, fetch: false) || User.get_cached_by_ap_id(object_id) do
case entity do
# 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
_e ->
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.
"""
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
else
_e ->
@ -102,14 +102,14 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
Logger.debug("Federating #{ap_id} to #{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} =
with {_, false} <- {:actor_changed?, data["actor"] != activity.data["actor"]} do
{actor, data}
else
{: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, data}
end

View file

@ -783,7 +783,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
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
object
|> add_hashtags
@ -795,6 +801,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|> set_reply_to_uri
|> set_quote_url
|> set_replies
|> set_voters_count
|> CommonFixes.maybe_add_content_map()
|> strip_internal_fields
|> strip_internal_tags
@ -824,7 +831,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# 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
object =
object_id
@ -840,7 +847,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
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
data =
data
@ -851,7 +858,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
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
object =
object
@ -868,11 +875,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
{:ok, data}
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)}"
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_id
|> Object.normalize(fetch: false)
@ -895,7 +902,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
# Mastodon Accept/Reject requires a non-normalized object containing the actor URIs,
# 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
object = %{
"actor" => follow_activity.actor,
@ -913,7 +920,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
def prepare_outgoing(%{"type" => "Reject"} = data) do
def prepare_activity(%{"type" => "Reject"} = data) do
with follow_activity <- Activity.normalize(data["object"]) do
object = %{
"actor" => follow_activity.actor,
@ -931,7 +938,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
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),
stripped_activity <- Utils.maybe_anonymize_reporter(stripped_activity),
stripped_activity <- Map.merge(stripped_activity, Utils.make_json_ld_header()) do
@ -939,7 +946,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
end
end
def prepare_outgoing(%{"type" => _type} = data) do
def prepare_activity(%{"type" => _type} = data) do
data =
data
|> 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.
"""
@callback prepare_outgoing(map()) :: {:ok, map()} | {:error, term()}
@callback prepare_activity(map()) :: {:ok, map()} | {:error, term()}
end

View file

@ -120,7 +120,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
"https://www.w3.org/ns/activitystreams",
"#{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 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
with {:ok, new_data} <- strip_report_status_data(activity.data) do
{:ok, %{activity | data: new_data}}

View file

@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
end
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
end

View file

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

View file

@ -174,6 +174,8 @@ defmodule Pleroma.Web.AdminAPI.ConfigController do
end
end
defp whitelisted_config?(":pleroma", ":database_config_whitelist"), do: false
defp whitelisted_config?(group, key) do
if whitelisted_configs = Config.get(:database_config_whitelist) do
Enum.any?(whitelisted_configs, fn

View file

@ -10,6 +10,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
alias Pleroma.Activity
alias Pleroma.ModerationLog
alias Pleroma.ReportNote
alias Pleroma.User
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Web.AdminAPI
alias Pleroma.Web.AdminAPI.Report
@ -24,7 +25,7 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
plug(
OAuthScopesPlug,
%{scopes: ["admin:write:reports"]}
when action in [:update, :notes_create, :notes_delete]
when action in [:update, :assign_account, :notes_create, :notes_delete]
)
action_fallback(AdminAPI.FallbackController)
@ -79,6 +80,22 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
end
end
def assign_account(
%{
assigns: %{user: admin},
private: %{open_api_spex: %{body_params: %{reports: reports}}}
} = conn,
_
) do
result = Enum.map(reports, &do_assign_account(&1, admin))
if Enum.any?(result, &Map.has_key?(&1, :error)) do
json_response(conn, :bad_request, result)
else
json_response(conn, :no_content, "")
end
end
def notes_create(
%{
assigns: %{user: user},
@ -131,4 +148,40 @@ defmodule Pleroma.Web.AdminAPI.ReportController do
_ -> json_response(conn, :bad_request, "")
end
end
defp do_assign_account(%{assigned_account: nil, id: id}, admin) do
with {:ok, activity} <- CommonAPI.assign_report_to_account(id, nil),
report <- Activity.get_by_id_with_user_actor(activity.id) do
ModerationLog.insert_log(%{
action: "report_unassigned",
actor: admin,
subject: activity,
subject_actor: report.user_actor
})
activity
else
{:error, message} ->
%{id: id, error: message}
end
end
defp do_assign_account(%{assigned_account: assigned_account, id: id}, admin) do
with %User{id: account} = user <- User.get_cached_by_nickname(assigned_account),
{:ok, activity} <- CommonAPI.assign_report_to_account(id, account),
report <- Activity.get_by_id_with_user_actor(activity.id) do
ModerationLog.insert_log(%{
action: "report_assigned",
actor: admin,
subject: activity,
subject_actor: report.user_actor,
assigned_account: user.nickname
})
activity
else
{:error, message} ->
%{id: id, error: message}
end
end
end

View file

@ -13,6 +13,11 @@ defmodule Pleroma.Web.AdminAPI.Report do
user = User.get_cached_by_ap_id(actor)
account = User.get_cached_by_ap_id(account_ap_id)
assigned_account =
if Map.has_key?(report.data, "assigned_account") do
User.get_cached_by_id(report.data["assigned_account"])
end
statuses =
status_ap_ids
|> Enum.reject(&is_nil(&1))
@ -26,7 +31,13 @@ defmodule Pleroma.Web.AdminAPI.Report do
Activity.get_by_ap_id_with_object(act)
end)
%{report: report, user: user, account: account, statuses: statuses}
%{
report: report,
user: user,
account: account,
statuses: statuses,
assigned_account: assigned_account
}
end
defp make_fake_activity(act, user) do

View file

@ -26,7 +26,13 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
}
end
def render("show.json", %{report: report, user: user, account: account, statuses: statuses}) do
def render("show.json", %{
report: report,
user: user,
account: account,
statuses: statuses,
assigned_account: assigned_account
}) do
created_at = Utils.to_masto_date(report.data["published"])
content =
@ -36,6 +42,11 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
nil
end
assigned_account =
if assigned_account do
merge_account_views(assigned_account)
end
%{
id: report.id,
account: merge_account_views(account),
@ -49,7 +60,8 @@ defmodule Pleroma.Web.AdminAPI.ReportView do
}),
state: report.data["state"],
notes: render(__MODULE__, "index_notes.json", %{notes: report.report_notes}),
rules: rules(Map.get(report.data, "rules", nil))
rules: rules(Map.get(report.data, "rules", nil)),
assigned_account: assigned_account
}
end

View file

@ -106,7 +106,14 @@ defmodule Pleroma.Web.ApiSpec.CastAndValidate do
OpenApiSpex.cast_and_validate(spec, operation, conn, content_type, cast_opts)
end
defp cast_and_validate(spec, operation, conn, content_type, false = _strict, cast_opts) do
defp cast_and_validate(
spec,
operation,
%Conn{} = conn,
content_type,
false = _strict,
cast_opts
) do
case OpenApiSpex.cast_and_validate(spec, operation, conn, content_type) do
{:ok, conn} ->
{:ok, conn}

View file

@ -53,6 +53,12 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
:query,
%Schema{type: :integer, default: 50},
"Number number of log entries per page"
),
Operation.parameter(
:assigned_account,
:query,
%Schema{type: :string},
"Filter by assigned account ID"
)
| admin_api_params()
],
@ -103,6 +109,22 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
}
end
def assign_account_operation do
%Operation{
tags: ["Report management"],
summary: "Assign account to specified reports",
operationId: "AdminAPI.ReportController.assign_account",
security: [%{"oAuth" => ["admin:write:reports"]}],
parameters: admin_api_params(),
requestBody: request_body("Parameters", assign_account_request(), required: true),
responses: %{
204 => no_content_response(),
400 => Operation.response("Bad Request", "application/json", update_400_response()),
403 => Operation.response("Forbidden", "application/json", ApiError)
}
}
end
def notes_create_operation do
%Operation{
tags: ["Report management"],
@ -186,7 +208,10 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
hint: %Schema{type: :string, nullable: true}
}
}
}
},
assigned_account:
account_admin()
|> Map.put(:nullable, true)
}
}
end
@ -242,6 +267,34 @@ defmodule Pleroma.Web.ApiSpec.Admin.ReportOperation do
}
end
defp assign_account_request do
%Schema{
type: :object,
required: [:reports],
properties: %{
reports: %Schema{
type: :array,
items: %Schema{
type: :object,
properties: %{
id: %Schema{allOf: [FlakeID], description: "Required, report ID"},
assigned_account: %Schema{
type: :string,
description: "User nickname",
nullable: true
}
}
},
example: %{
"reports" => [
%{"id" => "123", "assigned_account" => "pleroma"}
]
}
}
}
}
end
defp update_400_response do
%Schema{
type: :array,

View file

@ -342,6 +342,18 @@ defmodule Pleroma.Web.ApiSpec.InstanceOperation do
max_pinned_statuses: %Schema{
type: :integer,
description: "The maximum number of pinned statuses for each account."
},
max_profile_fields: %Schema{
type: :integer,
description: "The maximum number of custom profile fields allowed to be set."
},
profile_field_name_limit: %Schema{
type: :integer,
description: "The maximum size of a profile field name, in characters."
},
profile_field_value_limit: %Schema{
type: :integer,
description: "The maximum size of a profile field value, in characters."
}
}
},

View file

@ -36,7 +36,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
summary: "Create a list",
description: "Fetch the list with the given ID. Used for verifying the title of a list.",
operationId: "ListController.create",
requestBody: create_update_request(),
requestBody: create_request(),
security: [%{"oAuth" => ["write:lists"]}],
responses: %{
200 => Operation.response("List", "application/json", List),
@ -68,7 +68,7 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
description: "Change the title of a list",
operationId: "ListController.update",
parameters: [id_param()],
requestBody: create_update_request(),
requestBody: update_request(),
security: [%{"oAuth" => ["write:lists"]}],
responses: %{
200 => Operation.response("List", "application/json", List),
@ -164,14 +164,18 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
)
end
defp create_update_request do
defp create_request do
request_body(
"Parameters",
%Schema{
description: "POST body for creating or updating a List",
description: "POST body for creating a List",
type: :object,
properties: %{
title: %Schema{type: :string, description: "List title"}
title: %Schema{type: :string, description: "List title"},
exclusive: %Schema{
type: :boolean,
description: "Whether members of the list should be removed from the “Home” feed"
}
},
required: [:title]
},
@ -179,6 +183,24 @@ defmodule Pleroma.Web.ApiSpec.ListOperation do
)
end
defp update_request do
request_body(
"Parameters",
%Schema{
description: "PUT body for updating a List",
type: :object,
properties: %{
title: %Schema{type: :string, description: "List title"},
exclusive: %Schema{
type: :boolean,
description: "Whether members of the list should be removed from the “Home” feed"
}
}
},
required: true
)
end
defp add_remove_accounts_request(required) when is_boolean(required) do
request_body(
"Parameters",

View file

@ -2,7 +2,7 @@
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
defmodule Pleroma.Web.ApiSpec.PleromaUtilOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
alias Pleroma.Web.ApiSpec.Schemas.ApiError
@ -19,7 +19,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
%Operation{
tags: ["Custom emojis"],
summary: "List all custom emojis",
operationId: "UtilController.emoji",
operationId: "PleromaAPI.UtilController.emoji",
parameters: [],
responses: %{
200 =>
@ -48,7 +48,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
%Operation{
tags: ["Others"],
summary: "Dump frontend configurations",
operationId: "UtilController.frontend_configurations",
operationId: "PleromaAPI.UtilController.frontend_configurations",
parameters: [],
responses: %{
200 =>
@ -70,7 +70,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "Change account password",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.change_password",
operationId: "PleromaAPI.UtilController.change_password",
requestBody: request_body("Parameters", change_password_request(), required: true),
responses: %{
200 =>
@ -106,7 +106,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "Change account email",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.change_email",
operationId: "PleromaAPI.UtilController.change_email",
requestBody: request_body("Parameters", change_email_request(), required: true),
responses: %{
200 =>
@ -141,7 +141,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Settings"],
summary: "Update Notification Settings",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.update_notification_settings",
operationId: "PleromaAPI.UtilController.update_notification_settings",
parameters: [
Operation.parameter(
:block_from_strangers,
@ -173,7 +173,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "Disable Account",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.disable_account",
operationId: "PleromaAPI.UtilController.disable_account",
parameters: [
Operation.parameter(:password, :query, :string, "Password")
],
@ -193,7 +193,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "Delete Account",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.delete_account",
operationId: "PleromaAPI.UtilController.delete_account",
parameters: [
Operation.parameter(:password, :query, :string, "Password")
],
@ -212,7 +212,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
def captcha_operation do
%Operation{
summary: "Get a captcha",
operationId: "UtilController.captcha",
operationId: "PleromaAPI.UtilController.captcha",
tags: ["Others"],
parameters: [],
responses: %{
@ -226,7 +226,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "Move account",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.move_account",
operationId: "PleromaAPI.UtilController.move_account",
requestBody: request_body("Parameters", move_account_request(), required: true),
responses: %{
200 =>
@ -262,7 +262,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "List account aliases",
security: [%{"oAuth" => ["read:accounts"]}],
operationId: "UtilController.list_aliases",
operationId: "PleromaAPI.UtilController.list_aliases",
responses: %{
200 =>
Operation.response("Success", "application/json", %Schema{
@ -286,7 +286,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "Add an alias to this account",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.add_alias",
operationId: "PleromaAPI.UtilController.add_alias",
requestBody: request_body("Parameters", add_alias_request(), required: true),
responses: %{
200 =>
@ -326,7 +326,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Account credentials"],
summary: "Delete an alias from this account",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.delete_alias",
operationId: "PleromaAPI.UtilController.delete_alias",
requestBody: request_body("Parameters", delete_alias_request(), required: true),
responses: %{
200 =>
@ -366,7 +366,7 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
tags: ["Others"],
summary: "Quick status check on the instance",
security: [%{"oAuth" => ["write:accounts"]}],
operationId: "UtilController.healthcheck",
operationId: "PleromaAPI.UtilController.healthcheck",
parameters: [],
responses: %{
200 => Operation.response("Healthy", "application/json", %Schema{type: :object}),
@ -376,52 +376,6 @@ defmodule Pleroma.Web.ApiSpec.TwitterUtilOperation do
}
end
def remote_subscribe_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Remote Subscribe",
operationId: "UtilController.remote_subscribe",
parameters: [],
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
}
end
def remote_interaction_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Remote interaction",
operationId: "UtilController.remote_interaction",
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
responses: %{
200 =>
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
}
}
end
defp remote_interaction_request do
%Schema{
title: "RemoteInteractionRequest",
description: "POST body for remote interaction",
type: :object,
required: [:ap_id, :profile],
properties: %{
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
profile: %Schema{type: :string, description: "Remote profile webfinger"}
}
}
end
def show_subscribe_form_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Show remote subscribe form",
operationId: "UtilController.show_subscribe_form",
parameters: [],
responses: %{200 => Operation.response("Web Page", "test/html", %Schema{type: :string})}
}
end
defp delete_account_request do
%Schema{
title: "AccountDeleteRequest",

View file

@ -0,0 +1,99 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Web.ApiSpec.RemoteInteractionOperation do
alias OpenApiSpex.Operation
alias OpenApiSpex.Schema
import Pleroma.Web.ApiSpec.Helpers
def open_api_operation(action) do
operation = String.to_existing_atom("#{action}_operation")
apply(__MODULE__, operation, [])
end
def remote_interaction_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Remote interaction",
operationId: "RemoteInteractionController.remote_interaction",
requestBody: request_body("Parameters", remote_interaction_request(), required: true),
responses: %{
200 =>
Operation.response("Remote interaction URL", "application/json", %Schema{type: :object})
}
}
end
defp remote_interaction_request do
%Schema{
title: "RemoteInteractionRequest",
description: "POST body for remote interaction",
type: :object,
required: [:ap_id, :profile],
properties: %{
ap_id: %Schema{type: :string, description: "Profile or status ActivityPub ID"},
profile: %Schema{type: :string, description: "Remote profile webfinger"}
}
}
end
def follow_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Display follow form",
operationId: "RemoteInteractionController.follow",
parameters: [],
responses: %{
200 => Operation.response("Web Page", "text/html", %Schema{type: :string}),
302 => Operation.response("Redirect to the status page", nil, nil)
}
}
end
def do_follow_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Perform follow activity",
operationId: "RemoteInteractionController.do_follow",
parameters: [],
responses: %{
200 => Operation.response("Web page", "text/html", %Schema{type: :string}),
302 => Operation.response("Redirect to the account page", nil, nil)
}
}
end
def authorize_interaction_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Authorize remote interaction",
operationId: "RemoteInteractionController.authorize_interaction",
parameters: [],
responses: %{
302 => Operation.response("Redirect to remote_interaction path", nil, nil)
}
}
end
def show_subscribe_form_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Show remote subscribe form",
operationId: "RemoteInteractionController.show_subscribe_form",
parameters: [],
responses: %{200 => Operation.response("Web Page", "text/html", %Schema{type: :string})}
}
end
def remote_subscribe_operation do
%Operation{
tags: ["Remote interaction"],
summary: "Remote Subscribe",
operationId: "RemoteInteractionController.remote_subscribe",
parameters: [],
responses: %{200 => Operation.response("Web Page", "text/html", %Schema{type: :string})}
}
end
end

View file

@ -17,11 +17,11 @@ defmodule Pleroma.Web.ApiSpec.RenderError do
def call(conn, errors) do
errors =
Enum.map(errors, fn
%{name: nil, reason: :invalid_enum} = err ->
%OpenApiSpex.Cast.Error{err | name: err.value}
%OpenApiSpex.Cast.Error{name: nil, reason: :invalid_enum} = err ->
%{err | name: err.value}
%{name: nil} = err ->
%OpenApiSpex.Cast.Error{err | name: List.last(err.path)}
%OpenApiSpex.Cast.Error{name: nil} = err ->
%{err | name: List.last(err.path)}
err ->
err

View file

@ -21,6 +21,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
acct: %Schema{type: :string},
avatar_static: %Schema{type: :string, format: :uri},
avatar: %Schema{type: :string, format: :uri},
avatar_description: %Schema{type: :string},
bot: %Schema{type: :boolean},
created_at: %Schema{type: :string, format: "date-time"},
display_name: %Schema{type: :string},
@ -31,6 +32,7 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
following_count: %Schema{type: :integer},
header_static: %Schema{type: :string, format: :uri},
header: %Schema{type: :string, format: :uri},
header_description: %Schema{type: :string},
id: FlakeID,
locked: %Schema{type: :boolean},
note: %Schema{type: :string, format: :html},
@ -111,8 +113,8 @@ defmodule Pleroma.Web.ApiSpec.Schemas.Account do
nullable: true,
description: "Favicon image of the user's instance"
},
avatar_description: %Schema{type: :string},
header_description: %Schema{type: :string}
avatar_description: %Schema{type: :string, deprecated: true},
header_description: %Schema{type: :string, deprecated: true}
}
},
source: %Schema{

View file

@ -26,7 +26,9 @@ defmodule Pleroma.Web.ApiSpec.Schemas.AccountRelationship do
requested: %Schema{type: :boolean},
showing_reblogs: %Schema{type: :boolean},
subscribing: %Schema{type: :boolean},
notifying: %Schema{type: :boolean}
notifying: %Schema{type: :boolean},
mute_expires_at: %Schema{type: :string, format: "date-time", nullable: true},
block_expires_at: %Schema{type: :string, format: "date-time", nullable: true}
},
example: %{
"blocked_by" => false,

View file

@ -15,12 +15,18 @@ defmodule Pleroma.Web.ApiSpec.Schemas.BookmarkFolder do
properties: %{
id: FlakeID,
name: %Schema{type: :string, description: "Folder name"},
emoji: %Schema{type: :string, description: "Folder emoji", nullable: true}
emoji: %Schema{type: :string, description: "Folder emoji", nullable: true},
emoji_url: %Schema{
type: :string,
description: "URL of the folder emoji if it's a custom emoji, null otherwise",
nullable: true
}
},
example: %{
"id" => "9toJCu5YZW7O7gfvH6",
"name" => "Read later",
"emoji" => nil
"emoji" => nil,
"emoji_url" => nil
}
})
end

View file

@ -13,7 +13,11 @@ defmodule Pleroma.Web.ApiSpec.Schemas.List do
type: :object,
properties: %{
id: %Schema{type: :string, description: "The internal database ID of the list"},
title: %Schema{type: :string, description: "The user-defined title of the list"}
title: %Schema{type: :string, description: "The user-defined title of the list"},
exclusive: %Schema{
type: :boolean,
description: "Whether members of the list should be removed from the “Home” feed"
}
},
example: %{
"id" => "12249",

View file

@ -222,8 +222,8 @@ defmodule Pleroma.Web.CommonAPI do
with %Activity{data: %{"type" => "Create"}} = activity <- Activity.get_by_id(id),
object = %Object{} <- Object.normalize(activity, fetch: false),
{_, nil} <- {:existing_announce, Utils.get_existing_announce(user.ap_id, object)},
public = public_announce?(object, params),
{:ok, announce, _} <- Builder.announce(user, object, public: public),
visibility = announce_visibility(object, params),
{:ok, announce, _} <- Builder.announce(user, object, visibility: visibility),
{:ok, activity, _} <- Pipeline.common_pipeline(announce, local: true) do
{:ok, activity}
else
@ -407,13 +407,11 @@ defmodule Pleroma.Web.CommonAPI do
end
end
defp public_announce?(_, %{visibility: visibility})
when visibility in ~w{public unlisted private direct},
do: visibility in ~w(public unlisted)
def announce_visibility(_, %{visibility: visibility})
when visibility in ~w{public unlisted private direct local},
do: visibility
defp public_announce?(object, _) do
Visibility.public?(object)
end
def announce_visibility(object, _), do: Visibility.get_visibility(object)
@spec get_visibility(map(), map() | nil, Participation.t() | nil) ::
{String.t() | nil, String.t() | nil}
@ -709,6 +707,22 @@ defmodule Pleroma.Web.CommonAPI do
end
end
def assign_report_to_account(activity_ids, user) when is_list(activity_ids) do
case Utils.assign_report_to_account(activity_ids, user) do
:ok -> {:ok, activity_ids}
_ -> {:error, dgettext("errors", "Could not assign account")}
end
end
def assign_report_to_account(activity_id, user) do
with %Activity{} = activity <- Activity.get_by_id(activity_id) do
Utils.assign_report_to_account(activity, user)
else
nil -> {:error, :not_found}
_ -> {:error, dgettext("errors", "Could not assign account")}
end
end
@spec update_activity_scope(String.t(), map()) :: {:ok, any()} | {:error, any()}
def update_activity_scope(activity_id, opts \\ %{}) do
with %Activity{} = activity <- Activity.get_by_id_with_object(activity_id),

View file

@ -88,7 +88,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> validate()
end
defp listen_object(draft) do
defp listen_object(%__MODULE__{} = draft) do
object =
draft.params
|> Map.take([:album, :artist, :title, :length])
@ -99,34 +99,34 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> Map.put("cc", draft.cc)
|> Map.put("actor", draft.user.ap_id)
%__MODULE__{draft | object: object}
%{draft | object: object}
end
defp put_params(draft, params) do
defp put_params(%__MODULE__{} = draft, params) do
params = Map.put_new(params, :in_reply_to_status_id, params[:in_reply_to_id])
%__MODULE__{draft | params: params}
%{draft | params: params}
end
defp status(%{params: %{status: status}} = draft) do
%__MODULE__{draft | status: String.trim(status)}
defp status(%__MODULE__{params: %{status: status}} = draft) do
%{draft | status: String.trim(status)}
end
defp summary(%{params: params} = draft) do
%__MODULE__{draft | summary: Map.get(params, :spoiler_text, "")}
defp summary(%__MODULE__{params: params} = draft) do
%{draft | summary: Map.get(params, :spoiler_text, "")}
end
defp full_payload(%{status: status, summary: summary} = draft) do
defp full_payload(%__MODULE__{status: status, summary: summary} = draft) do
full_payload = String.trim(status <> summary)
case Utils.validate_character_limit(full_payload, draft.attachments) do
:ok -> %__MODULE__{draft | full_payload: full_payload}
:ok -> %{draft | full_payload: full_payload}
{:error, message} -> add_error(draft, message)
end
end
defp attachments(%{params: params} = draft) do
defp attachments(%__MODULE__{params: params} = draft) do
attachments = Utils.attachments_from_ids(params, draft.user)
draft = %__MODULE__{draft | attachments: attachments}
draft = %{draft | attachments: attachments}
case Utils.validate_attachments_count(attachments) do
:ok -> draft
@ -134,9 +134,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
end
defp in_reply_to(%{params: %{in_reply_to_status_id: ""}} = draft), do: draft
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: ""}} = draft), do: draft
defp in_reply_to(%{params: %{in_reply_to_status_id: id}} = draft) when is_binary(id) do
defp in_reply_to(%__MODULE__{params: %{in_reply_to_status_id: id}} = draft)
when is_binary(id) do
# If a post was deleted all its activities (except the newly added Delete) are purged too,
# thus lookup by Create db ID will yield nil just as if it never existed in the first place.
#
@ -148,7 +149,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
with %Activity{} = activity <- Activity.get_by_id(id),
true <- Visibility.visible_for_user?(activity, draft.user),
{_, type} when type in ["Create", "Announce"] <- {:type, activity.data["type"]} do
%__MODULE__{draft | in_reply_to: activity}
%{draft | in_reply_to: activity}
else
nil ->
add_error(draft, dgettext("errors", "Cannot reply to a deleted status"))
@ -166,40 +167,43 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
end
end
defp in_reply_to(%{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft) do
%__MODULE__{draft | in_reply_to: in_reply_to}
defp in_reply_to(
%__MODULE__{params: %{in_reply_to_status_id: %Activity{} = in_reply_to}} = draft
) do
%{draft | in_reply_to: in_reply_to}
end
defp in_reply_to(draft), do: draft
defp quote_post(%{params: %{quoted_status_id: id}} = draft) when not_empty_string(id) do
defp quote_post(%__MODULE__{params: %{quoted_status_id: id}} = draft)
when not_empty_string(id) do
case Activity.get_by_id_with_object(id) do
%Activity{} = activity ->
%__MODULE__{draft | quote_post: activity}
%{draft | quote_post: activity}
_ ->
draft
end
end
defp quote_post(%{params: %{quote_id: id}} = draft) when not_empty_string(id) do
defp quote_post(%__MODULE__{params: %{quote_id: id}} = draft) when not_empty_string(id) do
quote_post(%{draft | params: Map.put(draft.params, :quoted_status_id, id)})
end
defp quote_post(draft), do: draft
defp in_reply_to_conversation(draft) do
defp in_reply_to_conversation(%__MODULE__{} = draft) do
in_reply_to_conversation = Participation.get(draft.params[:in_reply_to_conversation_id])
%__MODULE__{draft | in_reply_to_conversation: in_reply_to_conversation}
%{draft | in_reply_to_conversation: in_reply_to_conversation}
end
defp visibility(%{params: params} = draft) do
defp visibility(%__MODULE__{params: params} = draft) do
case CommonAPI.get_visibility(params, draft.in_reply_to, draft.in_reply_to_conversation) do
{visibility, "direct"} when visibility != "direct" ->
add_error(draft, dgettext("errors", "The message visibility must be direct"))
{visibility, _} ->
%__MODULE__{draft | visibility: visibility}
%{draft | visibility: visibility}
end
end
@ -215,7 +219,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
false
end
defp quoting_visibility(%{quote_post: %Activity{}} = draft) do
defp quoting_visibility(%__MODULE__{quote_post: %Activity{}} = draft) do
with %Object{} = object <- Object.normalize(draft.quote_post, fetch: false),
true <- can_quote?(draft, object, Visibility.get_visibility(object)) do
draft
@ -226,24 +230,24 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
defp quoting_visibility(draft), do: draft
defp expires_at(draft) do
defp expires_at(%__MODULE__{} = draft) do
case CommonAPI.check_expiry_date(draft.params[:expires_in]) do
{:ok, expires_at} -> %__MODULE__{draft | expires_at: expires_at}
{:ok, expires_at} -> %{draft | expires_at: expires_at}
{:error, message} -> add_error(draft, message)
end
end
defp poll(draft) do
defp poll(%__MODULE__{} = draft) do
case Utils.make_poll_data(draft.params) do
{:ok, {poll, poll_emoji}} ->
%__MODULE__{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
%{draft | extra: poll, emoji: Map.merge(draft.emoji, poll_emoji)}
{:error, message} ->
add_error(draft, message)
end
end
defp content(%{mentions: mentions} = draft) do
defp content(%__MODULE__{mentions: mentions} = draft) do
{content_html, mentioned_users, tags} = Utils.make_content_html(draft)
mentioned_ap_ids =
@ -254,25 +258,25 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> Kernel.++(mentioned_ap_ids)
|> Utils.get_addressed_users(draft.params[:to])
%__MODULE__{draft | content_html: content_html, mentions: mentions, tags: tags}
%{draft | content_html: content_html, mentions: mentions, tags: tags}
end
defp to_and_cc(draft) do
defp to_and_cc(%__MODULE__{} = draft) do
{to, cc} = Utils.get_to_and_cc(draft)
%__MODULE__{draft | to: to, cc: cc}
%{draft | to: to, cc: cc}
end
defp context(draft) do
defp context(%__MODULE__{} = draft) do
context = Utils.make_context(draft.in_reply_to, draft.in_reply_to_conversation)
%__MODULE__{draft | context: context}
%{draft | context: context}
end
defp sensitive(draft) do
defp sensitive(%__MODULE__{} = draft) do
sensitive = draft.params[:sensitive]
%__MODULE__{draft | sensitive: sensitive}
%{draft | sensitive: sensitive}
end
defp language(draft) do
defp language(%__MODULE__{} = draft) do
language =
with language <- draft.params[:language],
true <- good_locale_code?(language) do
@ -281,10 +285,10 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
_ -> LanguageDetector.detect(draft.content_html <> " " <> draft.summary)
end
%__MODULE__{draft | language: language}
%{draft | language: language}
end
defp object(draft) do
defp object(%__MODULE__{} = draft) do
emoji = Map.merge(Pleroma.Emoji.Formatter.get_emoji_map(draft.full_payload), draft.emoji)
# Sometimes people create posts with subject containing emoji,
@ -313,6 +317,7 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
emoji = Map.merge(emoji, summary_emoji)
media_type = Utils.get_content_type(draft.params[:content_type])
{:ok, note_data, _meta} = Builder.note(draft)
object =
@ -320,20 +325,24 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
|> Map.put("emoji", emoji)
|> Map.put("source", %{
"content" => draft.status,
"mediaType" => Utils.get_content_type(draft.params[:content_type])
"mediaType" => media_type
})
|> maybe_put("htmlMfm", true, media_type == "text/x.misskeymarkdown")
|> Map.put("generator", draft.params[:generator])
|> Map.put("language", draft.language)
%__MODULE__{draft | object: object}
%{draft | object: object}
end
defp preview?(draft) do
defp maybe_put(map, key, value, true), do: Map.put(map, key, value)
defp maybe_put(map, _key, _value, _condition), do: map
defp preview?(%__MODULE__{} = draft) do
preview? = Pleroma.Web.Utils.Params.truthy_param?(draft.params[:preview])
%__MODULE__{draft | preview?: preview?}
%{draft | preview?: preview?}
end
defp changes(draft) do
defp changes(%__MODULE__{} = draft) do
direct? = draft.visibility == "direct"
additional = %{"cc" => draft.cc, "directMessage" => direct?}
@ -353,14 +362,14 @@ defmodule Pleroma.Web.CommonAPI.ActivityDraft do
}
|> Utils.maybe_add_list_data(draft.user, draft.visibility)
%__MODULE__{draft | changes: changes}
%{draft | changes: changes}
end
defp with_valid(%{valid?: true} = draft, func), do: func.(draft)
defp with_valid(draft, _func), do: draft
defp add_error(draft, message) do
%__MODULE__{draft | valid?: false, errors: [message | draft.errors]}
defp add_error(%__MODULE__{} = draft, message) do
%{draft | valid?: false, errors: [message | draft.errors]}
end
defp validate(%{valid?: true} = draft), do: {:ok, draft}

View file

@ -75,48 +75,70 @@ defmodule Pleroma.Web.CommonAPI.Utils do
{Enum.map(participation.recipients, & &1.ap_id), []}
end
def get_to_and_cc(%{visibility: visibility} = draft) when visibility in ["public", "local"] do
to =
case visibility do
"public" -> [Pleroma.Constants.as_public() | draft.mentions]
"local" -> [Utils.as_local_public() | draft.mentions]
def get_to_and_cc(%{visibility: visibility} = draft) do
# If the OP is a DM already, add the implicit actor
mentions =
if visibility == "direct" && draft.in_reply_to && Visibility.direct?(draft.in_reply_to) do
Enum.uniq([draft.in_reply_to.data["actor"] | draft.mentions])
else
draft.mentions
end
cc = [draft.user.follower_address]
if draft.in_reply_to do
{Enum.uniq([draft.in_reply_to.data["actor"] | to]), cc}
else
{to, cc}
end
get_to_and_cc_for_visibility(
visibility,
draft.user.follower_address,
draft.in_reply_to && draft.in_reply_to.data["actor"],
mentions
)
end
def get_to_and_cc(%{visibility: "unlisted"} = draft) do
to = [draft.user.follower_address | draft.mentions]
cc = [Pleroma.Constants.as_public()]
def get_to_and_cc_for_visibility("public", follower_collection, parent_actor, mentions) do
scope_addr = Pleroma.Constants.as_public()
if draft.in_reply_to do
{Enum.uniq([draft.in_reply_to.data["actor"] | to]), cc}
else
{to, cc}
end
to =
if parent_actor,
do: Enum.uniq([parent_actor, scope_addr | mentions]),
else: [scope_addr | mentions]
{to, [follower_collection]}
end
def get_to_and_cc(%{visibility: "private"} = draft) do
{to, cc} = get_to_and_cc(struct(draft, visibility: "direct"))
{[draft.user.follower_address | to], cc}
def get_to_and_cc_for_visibility("local", follower_collection, parent_actor, mentions) do
recipients =
if parent_actor,
do: Enum.uniq([parent_actor | mentions]),
else: mentions
to = [
Utils.as_local_public()
| Enum.filter(recipients, fn addr ->
String.starts_with?(addr, Pleroma.Web.Endpoint.url() <> "/")
end)
]
{to, [follower_collection]}
end
def get_to_and_cc(%{visibility: "direct"} = draft) do
# If the OP is a DM already, add the implicit actor.
if draft.in_reply_to && Visibility.direct?(draft.in_reply_to) do
{Enum.uniq([draft.in_reply_to.data["actor"] | draft.mentions]), []}
else
{draft.mentions, []}
end
def get_to_and_cc_for_visibility("unlisted", follower_collection, parent_actor, mentions) do
to =
if parent_actor,
do: Enum.uniq([parent_actor, follower_collection | mentions]),
else: [follower_collection | mentions]
{to, [Pleroma.Constants.as_public()]}
end
def get_to_and_cc(%{visibility: {:list, _}, mentions: mentions}), do: {mentions, []}
def get_to_and_cc_for_visibility("private", follower_collection, _, mentions) do
{[follower_collection | mentions], []}
end
def get_to_and_cc_for_visibility("direct", _, _, mentions) do
{mentions, []}
end
def get_to_and_cc_for_visibility({:list, _}, _, _, mentions) do
{mentions, []}
end
def get_addressed_users(_, to) when is_list(to) do
User.get_ap_ids_by_nicknames(to)
@ -300,6 +322,14 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> Formatter.linkify(options)
end
def format_input(text, "text/x.misskeymarkdown", options) do
text
|> Formatter.markdown_to_html(%{breaks: true})
|> safe_mfm_to_html()
|> Formatter.linkify(options)
|> Formatter.html_escape("text/x.misskeymarkdown")
end
def format_input(text, "text/markdown", options) do
text
|> Formatter.mentions_escape(options)
@ -308,6 +338,16 @@ defmodule Pleroma.Web.CommonAPI.Utils do
|> Formatter.html_escape("text/html")
end
defp safe_mfm_to_html(html) do
html
|> MfmParser.Parser.parse()
|> MfmParser.Encoder.to_html()
rescue
_ -> html
catch
_, _ -> html
end
def format_naive_asctime(date) do
date |> DateTime.from_naive!("Etc/UTC") |> format_asctime
end

View file

@ -20,6 +20,7 @@ defmodule Pleroma.Web.EmbedController do
conn
|> delete_resp_header("x-frame-options")
|> delete_resp_header("content-security-policy")
|> put_layout({Pleroma.Web.LayoutView, :embed})
|> render("show.html",
activity: activity,
author: User.sanitize_html(author),

View file

@ -46,8 +46,10 @@ defmodule Pleroma.Web.Endpoint do
plug(Pleroma.Web.Plugs.HTTPSecurityPlug)
plug(Pleroma.Web.Plugs.UploadedMedia)
@static_cache_control "public, max-age=1209600"
@static_cache_control "public, max-age=1209600, immutable"
@static_cache_disabled "public, no-cache"
# cache for a day
@favicon_cache_control "public, max=age=86400, immutable"
# InstanceStatic needs to be before Plug.Static to be able to override shipped-static files
# If you're adding new paths to `only:` you'll need to configure them in InstanceStatic as well
@ -64,6 +66,15 @@ defmodule Pleroma.Web.Endpoint do
}
)
plug(Pleroma.Web.Plugs.FaviconPlug,
at: "/",
only: ["favicon.png"],
cache_control_for_etags: @favicon_cache_control,
headers: %{
"cache-control" => @favicon_cache_control
}
)
plug(Pleroma.Web.Plugs.InstanceStatic,
at: "/",
gzip: true,

View file

@ -29,9 +29,18 @@ defmodule Pleroma.Web.Fallback.RedirectController do
)
end
def live_dashboard(conn, _params) do
def live_dashboard(conn, %{"path" => path}) do
query_params = conn.query_string
redirect_path =
if query_params == "" do
"/pleroma/live_dashboard/#{path}"
else
"/pleroma/live_dashboard/#{path}?#{query_params}"
end
conn
|> redirect(to: "/pleroma/live_dashboard")
|> redirect(to: redirect_path)
end
def redirector(conn, _params, code \\ 200) do

View file

@ -11,6 +11,7 @@ defmodule Pleroma.Web.Federator do
alias Pleroma.Web.ActivityPub.Utils
alias Pleroma.Workers.PublisherWorker
alias Pleroma.Workers.ReceiverWorker
alias Pleroma.Workers.SignatureRetryWorker
require Logger
@ -35,12 +36,21 @@ defmodule Pleroma.Web.Federator do
end
# Client API
def incoming_ap_doc(%{params: params, req_headers: req_headers}) do
ReceiverWorker.new(
def incoming_failed_signature_ap_doc(%{
method: method,
params: params,
req_headers: req_headers,
request_path: request_path,
query_string: query_string
}) do
SignatureRetryWorker.new(
%{
"op" => "incoming_ap_doc",
"op" => "incoming_failed_signature_ap_doc",
"method" => method,
"req_headers" => req_headers,
"params" => params,
"request_path" => request_path,
"query_string" => query_string,
"timeout" => :timer.seconds(20)
},
priority: 2

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