Compare commits

..

751 commits

Author SHA1 Message Date
Henry Jameson
cafc59bf4f Merge remote-tracking branch 'origin/develop' into shigusegubu-new 2026-10-25 18:45:39 +02:00
lain
9edce3a0a6 Merge pull request 'Merge stable 2.11.0 into develop' (#8009) from merge-stable-2.11.0 into develop 2026-10-24 07:52:45 +00:00
lain
256882f8fb Merge pull request 'Release 2.11.0' (#8008) from release/2.11 into stable 2026-10-24 07:51:52 +00:00
Lain Soykaf
3f139680a3
Changelog: fix two list items in 2.11.0 2026-10-24 11:29:06 +04:00
Lain Soykaf
16d86dbaa9
Changelog: add plug_crypto 2.2.1 to 2.11.0 2026-10-24 09:38:16 +04:00
Lain Soykaf
e415eee9b6
Merge remote-tracking branch 'secteam/audit-mix-audit-report' into release/2.11 2026-10-24 09:37:34 +04:00
Lain Soykaf
6c5c5f2970
Sec: Update plug_crypto to version 2.2.1 2026-10-24 09:37:01 +04:00
Lain Soykaf
902fb8abc7
Changelog: add certifi 2.18.0 to 2.11.0 2026-10-24 08:47:35 +04:00
Lain Soykaf
8a5512f69f
Merge remote-tracking branch 'secteam/audit-mix-audit-report' into release/2.11 2026-10-24 08:46:14 +04:00
Lain Soykaf
932d1b6f94
Sec: Update certifi to version 2.18.0 2026-10-24 08:46:06 +04:00
feld
256c493ec6 Merge pull request 'Delete legacy Postgres sequences' (#7969) from legacy_sequences into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7969
2026-10-22 18:10:36 +00:00
Lain Soykaf
e085365d85
Changelog: add the review fixes to 2.11.0 2026-10-22 16:37:17 +04:00
Lain Soykaf
2982472d00
Merge branch 'fix/emudhra-ca-trust' (#77) into release/2.11 2026-10-22 16:37:08 +04:00
Lain Soykaf
9ea8d37e9f
Merge branch 'audit-mix-audit-report' (#69) into release/2.11
# Conflicts:
#	lib/pleroma/http/adapter_helper/hackney.ex
2026-10-22 16:37:08 +04:00
Lain Soykaf
532762bd9b
fix(http): keep the configured TLS options with Gun
[:http, :adapter, :ssl_options] is Hackney's key, and AdminFE sets it.
Gun reads tls_opts, so with Gun as the default these settings were lost
without a warning. Use them as tls_opts unless tls_opts is set.
2026-10-22 16:36:16 +04:00
Lain Soykaf
b2e4580a81
feat(http): make Gun the default HTTP client
Hackney 4 reads every response completely before Pleroma can check its
size, so max_body no longer limits downloads or memory there; Gun stops
at the limit. Document the Hackney limitation for those who switch back.
2026-10-22 16:36:16 +04:00
Lain Soykaf
1be50ed28b
fix(html): check link schemes the way browsers read them
FastSanitize checks the scheme with URI.parse/1, which finds none in
java<TAB>script:alert(1). Browsers remove tabs and newlines and leading
control characters first and run it as a javascript: link. The scrubbers
now use Pleroma.HTML.URIAttributes, which normalizes the URL the same
way before the scheme check.
2026-10-22 16:36:16 +04:00
Lain Soykaf
58d4f6aa2c
fix(formatter): percent-encode quotes in Markdown URLs instead of cutting
Cutting the URL at the first quote dropped the rest of valid links such
as ?q=&quot;cat&quot;&page=2. Percent-encoding ", < and > keeps the URL
complete and still keeps it inside the attribute.
2026-10-22 16:28:06 +04:00
Lain Soykaf
de6ac2392c
fix(formatter): keep links with formatted text in MFM posts
The email autolink clause also matched a link whose only child is an
element, raised on "mailto:" <> element, and the rescue turned the
whole post into escaped source text.
2026-10-22 16:25:54 +04:00
Lain Soykaf
1f2179872a
docs: require Elixir 1.17 and OTP 27 in the install guides 2026-10-22 16:25:03 +04:00
Lain Soykaf
e52ba68303
fix(http): make Hackney use HTTP/1.1 only
Hackney 4 negotiates HTTP/2 and shares one connection per host. The
media proxy closes a response with :hackney.close/1, which stops the
whole connection, so parallel downloads from one host broke each other.
2026-10-22 16:24:47 +04:00
Lain Soykaf
4943e6a914
fix(http): pass IP proxy hosts to Hackney as strings
ConfigDB turns a numeric proxy host into an IP tuple. Hackney 1 took it,
Hackney 4 ignores such a proxy and connects directly, which skips a
SOCKS proxy (Tor) without any error.
2026-10-22 16:23:40 +04:00
Lain Soykaf
302039e437
fix(http): keep admin CAs, secure HTTPS proxies, make insecure explicit
- An admin-set cacerts no longer gets the CAStore cacertfile added;
  Hackney drops cacerts when both are set.
- Set proxy_ssl_options too: with an HTTPS proxy, Hackney 4 uses them
  for the proxy connection and falls back to certifi.
- Hackney 4 ignores the top-level insecure option and uses certifi.
  Turn it into an explicit verify_none.
2026-10-22 15:31:30 +04:00
Lain Soykaf
8d8d9d3354
docs: recommend the published admin-fe stable build 2026-10-22 13:54:18 +04:00
Lain Soykaf
fa38d27b34
Changelog: add the admin-fe install fix to 2.11.0 2026-10-22 13:04:41 +04:00
Lain Soykaf
3faf7dd1c8
docs: only list published admin-fe builds
Only develop has a package. stable (2022) and the old tags have no
Woodpecker config, so they get no build.
2026-10-22 13:04:20 +04:00
Lain Soykaf
a03e5df39c
fix: install admin-fe from the package registry 2026-10-22 13:04:20 +04:00
Lain Soykaf
04528f29d0
Prepare 2.11.0 release 2026-10-22 11:34:11 +04:00
Lain Soykaf
fc91131892
Merge branch 'fix/emudhra-ca-trust' (#77) into release/2.11 2026-10-22 11:27:15 +04:00
Lain Soykaf
f4a3bc2300
Static: Update bundled frontend to 2.11.4 2026-10-22 11:23:51 +04:00
Lain Soykaf
accadd99f3
fix(mailer): make Mua trust the CAStore bundle
Mua (the SMTP adapter in the docs) trusts the OS CA store unless it gets
a CA. Pass the CAStore bundle, but keep a CA that the admin set.
2026-10-22 11:21:50 +04:00
Lain Soykaf
d698b96579
fix(search): make the Qdrant clients trust the CAStore bundle
The Qdrant and OpenAI clients do not go through Pleroma.HTTP, so on
Hackney they trusted certifi. Both send API keys. Give them the
configured adapter with the same TLS options.
2026-10-22 11:21:00 +04:00
Lain Soykaf
8880c502be
Merge branch 'develop' into fix/emudhra-ca-trust
# Conflicts:
#	mix.lock
2026-10-22 11:15:06 +04:00
Lain Soykaf
e0ed6b5d39
Merge branch 'fix/emudhra-ca-trust' (#77) into release/2.11
# Conflicts:
#	lib/pleroma/http/adapter_helper/hackney.ex
#	mix.lock
2026-10-22 10:05:45 +04:00
Lain Soykaf
52762f9770
Merge branch 'audit-mix-audit-report' (#69) into release/2.11 2026-10-22 10:05:33 +04:00
Lain Soykaf
65076aa2f8
fix(http): reject Hackney's certifi partial chains
Hackney 4 merges given ssl_options into its defaults and keeps its own
partial_chain, which trusts any certificate in the peer chain that is in
certifi. A peer could send the emSign ECC Root CA - G3 root in its chain
and pass. Set partial_chain so certifi is never a trust anchor.
2026-10-22 10:05:12 +04:00
Lain Soykaf
92a112cf15
fix(mailer): drop the TLS 1.2 pin
The pin worked around a TLS 1.3 problem with Hackney on OTP 23
(swoosh/swoosh#478). Pleroma no longer supports OTP 23.
2026-10-22 08:48:56 +04:00
Phantasm
bbc53f0fef
Merge branch 'develop' into audit-mix-audit-report 2026-10-21 19:24:32 +02:00
Phantasm
5810ecd16e
Sec: Update Decimal to version 3.2.0
Fixes:
  decimal 3.1.1 - EEF-CVE-2026-97853 (MEDIUM)
    aka: CVE-2026-97853, GHSA-6c27-994x-c52f
    Unbounded allocation in decimal Decimal.round/3 driven by the places argument enables DoS
    https://osv.dev/vulnerability/EEF-CVE-2026-97853
2026-10-21 11:27:55 +02:00
Phantasm
e7b88cd652
Sec: Update mint to version 1.11.0
Fixes:
  mint 1.10.0 - EEF-CVE-2026-92103 (MEDIUM)
    aka: CVE-2026-92103, GHSA-q95c-ccq6-j5j6
    Mint HTTP/2 client buffers oversized frames up to 16 MiB before enforcing max_frame_size
    https://osv.dev/vulnerability/EEF-CVE-2026-92103

  mint 1.10.0 - EEF-CVE-2026-82672 (MEDIUM)
    aka: CVE-2026-82672, GHSA-rj5m-69wp-cxq9
    Unvalidated chunk-size line tail in Mint HTTP/1 client enables response smuggling against strict intermediaries on pooled connections
    https://osv.dev/vulnerability/EEF-CVE-2026-82672

  mint 1.10.0 - EEF-CVE-2026-91043 (HIGH)
    aka: CVE-2026-91043, GHSA-9x8p-qrf4-jq7g
    HPACK-indexed cookie fields in Mint HTTP/2 responses bypass max_header_list_size and exhaust client memory
    https://osv.dev/vulnerability/EEF-CVE-2026-91043

  mint 1.10.0 - EEF-CVE-2026-94194 (MEDIUM)
    aka: CVE-2026-94194, GHSA-gvrc-75rc-7gj9
    Mint HTTP/1 client applies chunked framing when chunked is not the final transfer coding, enabling response smuggling through intermediaries
    https://osv.dev/vulnerability/EEF-CVE-2026-94194
2026-10-21 11:27:55 +02:00
Phantasm
b4a3e8436d
Sec: Update bandit to version 1.13.1
Fixes:
  bandit 1.12.5 - EEF-CVE-2026-105659 (HIGH)
    aka: CVE-2026-105659, GHSA-69m6-wcv4-rwcf
    HTTP/2 blocked-send deadlines reset on every WINDOW_UPDATE, letting a trickle pin stream processes indefinitely in Bandit
    https://osv.dev/vulnerability/EEF-CVE-2026-105659

  bandit 1.12.5 - EEF-CVE-2026-105660 (MEDIUM)
    aka: CVE-2026-105660, GHSA-v53v-9hvg-37jf
    Unvalidated HTTP/1 request-target authority lets a client choose conn.host and conn.port in Bandit
    https://osv.dev/vulnerability/EEF-CVE-2026-105660
2026-10-21 11:27:55 +02:00
Phantasm
235435fc9b
Sec: Bump gun to version 2.7.0 and cowlib to version 2.21.0
Fixes:
  cowlib 2.20.0 - EEF-CVE-2026-43966 (MEDIUM)
    aka: CVE-2026-43966, GHSA-w4f7-4cxr-rv3c
    HTTP Response Splitting via Non-VCHAR Bytes in cow_http_struct_hd:escape_string/2
    https://osv.dev/vulnerability/EEF-CVE-2026-43966

  cowlib 2.20.0 - EEF-CVE-2026-43969 (LOW)
    aka: CVE-2026-43969, GHSA-g2wm-735q-3f56
    Cookie Request Header Injection via Unvalidated Encoder in cow_cookie:cookie/1
    https://osv.dev/vulnerability/EEF-CVE-2026-43969
2026-10-21 11:27:55 +02:00
Lain Soykaf
f4ff7cb1ea
fix(http): make Finch trust the CAStore bundle
Mint trusts the OTP CA store, which comes from the OS. Load the CAStore
bundle into it when Finch starts, so Finch trusts the same roots as the
other adapters.
2026-10-21 08:27:30 +04:00
Lain Soykaf
830455d197
fix(mailer): keep certificate checks for Swoosh HTTP adapters
The TLS version override replaced all Hackney TLS defaults. On OTP 26+
this fails with cacerts: :undefined; on older OTP it skipped certificate
checks. Closes #7962 on the public tracker.
2026-10-21 08:27:30 +04:00
Lain Soykaf
d8c880e494
fix(http): make Hackney trust the CAStore bundle
Hackney trusts its own certifi bundle, which still has the roots of the
compromised eMudhra intermediate CAs. Pass explicit ssl_options with the
CAStore bundle. Hackney drops all its TLS defaults when ssl_options are
given, so keep peer and hostname checks in the options.
2026-10-21 08:21:43 +04:00
Phantasm
6ac6cff5b5
Sec: Bump castore to version 1.0.22
eMudhra Intermediate CA Compromise
2026-10-21 08:17:26 +04:00
Phantasm
bde6bc91a4
Sec: Bump castore to version 1.0.22
eMudhra Intermediate CA Compromise
2026-10-20 23:46:45 +02:00
Phantasm
687fa6e1b2 Merge pull request 'Docs: Clarify that mise builds Erlang from source on Debian' (#8000) from fix-new-elixir-erlang-docs into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/8000
2026-10-04 19:25:25 +00:00
Phantasm
0421e8b2e3
Docs New Elixir/Erlang: Clarify that mise builds Erlang on Debian 2026-10-04 21:20:19 +02:00
Phantasm
1659fd99d3
Docs: Add missing curl dependency for asdf 2026-10-04 21:18:13 +02:00
Phantasm
cfeca7f7af Merge pull request 'Deps: Bump captcha version' (#7996) from captcha-ver-bump into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7996
2026-09-30 13:34:44 +00:00
Phantasm
9c76e0686a
Deps: Bump captcha version
Fixes build on OpenBSD and FreeBSD
2026-09-30 15:21:25 +02:00
Phantasm
1afc236468 Merge pull request 'Docs: Bump maximum datasize limit and mention cap_mkdb when creating login class' (#7995) from openbsd-bump-datasize into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7995
2026-09-29 08:45:42 +00:00
Phantasm
62d4258d79
Docs OpenBSD: Mention running cap_mkdb after making new login class 2026-09-29 10:44:10 +02:00
Phantasm
4515f9139c
Docs OpenBSD: Bump recommended datasize login.conf limit
Under OpenBSD datasize not only includes allocated memory using malloc,
but also anonymous memory mapped via mmap.
2026-09-29 10:44:03 +02:00
Phantasm
5a4cffbb62 Merge pull request 'Docs: Guide on installing newer versions of Elixir/Erlang than what distro ships' (#7992) from erlang-elixir-install-docs into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7992
2026-09-24 18:14:21 +00:00
Phantasm
62b6a24d5d Docs: move paragraph 2026-09-24 20:11:38 +02:00
Phantasm
0061f62355 Docs Newer Elixir/Erlang guide: Recommend upgrading OS release instead 2026-09-24 20:11:38 +02:00
Phantasm
775ae5d504 Docs New Elixir/Erlang versions install: Add maintenance chapter 2026-09-24 20:01:55 +02:00
Phantasm
f61cf55676
Docs: Add newer Elixir/Erlang versions installation documentation
Also splits generic dependencies into elixir/erlang and the rest.
Generic depenencies then import the elixir/erlang snippets to preserve
previous layout.
2026-09-22 22:44:20 +02:00
lain
b68f13b959 Merge pull request 'Woodpecker CI: Pass API deployment parameters separately' (#7990) from fix/woodpecker-trigger-params into develop 2026-09-09 12:18:37 +00:00
Lain Soykaf
239ae3403b
fix(ci): pass API deployment parameters separately 2026-09-09 15:55:18 +04:00
lain
c023727ecd Merge pull request 'Woodpecker CI: Remake docs building pipelines' (#7928) from woodpecker-ci-docs into develop 2026-09-09 11:01:35 +00:00
Lain Soykaf
fd1b15dd58
fix(ci): isolate API documentation channels 2026-09-09 13:21:07 +04:00
Phantasm
d6a5dd2b76
Bump minimum Elixir version to 1.17.
Multiple dependencies require OTP27 and 1.17 is the oldest version
supporting it. Also bumps Docker Erlang version to current newest 27.
2026-09-08 22:39:36 +02:00
Phantasm
72d45a9f8b
Merge branch 'develop' into audit-mix-audit-report 2026-09-08 21:58:23 +02:00
Phantasm
760daad8ef
Sec: Bump swoosh to version 1.28.0
Fixes:
  swoosh 1.16.12 VULNERABLE!
    EEF-CVE-2026-54893 (LOW)
    aka: CVE-2026-54893, GHSA-754j-98wh-57rf
    Email-derived URL path injection in the Swoosh Microsoft Graph adapter
    https://osv.dev/vulnerability/EEF-CVE-2026-54893
2026-09-08 21:53:55 +02:00
Phantasm
47629351f0
Sec: Bump mint to version 1.10.0
Fixes:
  mint 1.9.3 - EEF-CVE-2026-82729 (MEDIUM)
    aka: CVE-2026-82729, GHSA-7p8w-j234-7qc8
    Quadratic chunk-size parsing in Mint.HTTP1.Parse allows CPU-exhaustion DoS
    https://osv.dev/vulnerability/EEF-CVE-2026-82729

  mint 1.9.3 - EEF-CVE-2026-82728 (HIGH)
    aka: CVE-2026-82728, GHSA-g83f-2j6r-q6m4
    Unbounded HTTP/1 status-line and chunk-extension buffering in Mint causes memory-exhaustion DoS
    https://osv.dev/vulnerability/EEF-CVE-2026-82728
2026-09-08 21:53:48 +02:00
Phantasm
bad04ab482
Sec: Bump cowboy to version 2.19.0
Fixes:
  cowboy 2.16.1 - EEF-CVE-2026-65624 (MEDIUM)
    aka: CVE-2026-65624
    Cowboy HTTP/1.1 max_headers Bypass via Duplicate Header Names Enables Memory Exhaustion
    https://osv.dev/vulnerability/EEF-CVE-2026-65624
2026-09-08 21:43:05 +02:00
Phantasm
92b8fba3e3
Sec: Bump cowlib to 2.20.0 and gun to 2.6.0
Fixes:
  cowlib:
    EEF-CVE-2026-43971 (MEDIUM)
    aka: CVE-2026-43971
    Link Header Directive Smuggling via Unescaped target/rel/Attribute Keys in cow_link:link/1
    https://osv.dev/vulnerability/EEF-CVE-2026-43971

    EEF-CVE-2026-43966 (MEDIUM)
    aka: CVE-2026-43966, GHSA-w4f7-4cxr-rv3c
    HTTP Response Splitting via Non-VCHAR Bytes in cow_http_struct_hd:escape_string/2
    https://osv.dev/vulnerability/EEF-CVE-2026-43966

    EEF-CVE-2026-43969 (LOW)
    aka: CVE-2026-43969, GHSA-g2wm-735q-3f56
    Cookie Request Header Injection via Unvalidated Encoder in cow_cookie:cookie/1
    https://osv.dev/vulnerability/EEF-CVE-2026-43969
  gun:
    GHSA-w4f7-4cxr-rv3c (MEDIUM)
    aka: CVE-2026-43966, EEF-CVE-2026-43966
    cowboy and gun affected by an HTTP Request/Response Splitting vulnerability
    https://osv.dev/vulnerability/GHSA-w4f7-4cxr-rv3c
2026-09-08 21:38:55 +02:00
Phantasm
770d827b50
Sec: Bump bandit to version 1.12.5
Fixes:
- EEF-CVE-2026-74836 (HIGH)
  aka: CVE-2026-74836, GHSA-xj8g-532w-jv94
  HTTP/2 connection-window starvation pins Plug processes indefinitely in Bandit
  https://osv.dev/vulnerability/EEF-CVE-2026-74836

- EEF-CVE-2026-75484 (MEDIUM)
  aka: CVE-2026-75484, GHSA-x3gh-xhj4-3vq8
  HTTP/2 header field values containing CR, LF or NUL are passed to the application unvalidated in Bandit
  https://osv.dev/vulnerability/EEF-CVE-2026-75484
2026-09-08 21:28:37 +02:00
Phantasm
b3f95adfb7 Merge pull request 'CI: Fix failing OTP builds, switch to mirrored Woodpecker multi-arch manifest plugin' (#7989) from wpc-ci-bump into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7989
2026-09-08 11:54:49 +00:00
Phantasm
b3007be961
Woodpecker CI: Update Docker multi-arch plugin, fix commit sha mangling
149236e468 would become 1.49236e+09 when passed to the plugin due to
YAML's type coercion (string of a float in scientific -> float).
2026-09-08 13:18:36 +02:00
Phantasm
ef011bb8f9
Woodpecker CI: Ignore failure to stop for services 2026-09-08 13:17:55 +02:00
Phantasm
5452e3f73e
Fix indentation 2026-09-05 21:25:28 +02:00
Phantasm
a881ffe8c2
Woodpecker CI API spec: Update Elixir image to an existing one
Switches from hexpm/elixir-amd64 to hexpm/elixir.
2026-09-05 21:24:35 +02:00
Phantasm
c978fe0b7b
CI: Bump Gitea Package plugin version 2026-09-05 21:24:35 +02:00
Phantasm
a050b00898
Woodpecker CI Docs: Also build docs for develop branch 2026-09-05 21:24:17 +02:00
Phantasm
d549b98f8b
changelog 2026-09-05 20:48:56 +02:00
Phantasm
e35dacf9f9
Woodpecker CI: pass branch to triggered docs workflows 2026-09-05 20:48:56 +02:00
Phantasm
71104db3f6
Woodpecker CI: Deploy docs on stable tag 2026-09-05 20:48:55 +02:00
Phantasm
c596ace9df
Woodpecker CI: Build API specs again 2026-09-05 20:48:55 +02:00
Phantasm
0e1315099f
changelog 2026-09-05 00:47:27 +02:00
Phantasm
407db6c118
CI: Update Gitea Package plugin to 0.5.1 2026-09-05 00:14:19 +02:00
Phantasm
5a7635b943
CI OTP: Update elixir images, switch to hexpm/elixir except armv7 2026-09-05 00:14:19 +02:00
Phantasm
21bb1a98a3
CI: Update woodpecker Docker multi-arch tagger plugin
Also moves it to a git.pleroma.social mirror
2026-09-05 00:14:07 +02:00
Phantasm
149236e468 Merge pull request 'IdempotencyPlug: Fix private function name typo' (#7985) from idempotency-plug-typo into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7985
2026-09-02 22:31:01 +00:00
Phantasm
820e80bdc7
IdempotencyPlug: Fix private function name typo 2026-09-03 00:07:25 +02:00
lain
fa920037f1 Merge pull request 'Fix notification query plan for users with domain blocks' (#7981) from fix/domain-block-subplan-cost into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7981
2026-09-02 13:41:40 +00:00
lain
70d743bff9 Merge branch 'develop' into fix/domain-block-subplan-cost 2026-09-02 10:19:27 +00:00
Lain Soykaf
635bc19ac8
fix(notifications): cost the followed-accounts subselect once
Wrap the followed ap_ids subselect in keep_following_or_not_domain_blocked
in ARRAY(...) so it becomes an InitPlan. As a plain subselect the planner
charged its startup per rescan of the actor lookup, which could make
Notification.for_user pick a plan that scans every active user per row.
2026-09-02 13:42:48 +04:00
Mark Felder
9921e802c9 Delete legacy Postgres sequences
for activities, chats, and users which were converted to Flake IDs
2026-08-24 14:51:58 -07:00
Phantasm
45459c8398
Merge branch 'develop' into audit-mix-audit-report 2026-08-19 22:20:55 +02:00
Phantasm
5709531197
Sec: Bump Phoenix LiveView to 1.2.9
Technically 1.1.33 would also do...

Also updates:
  phoenix_pubsub 2.1.3 => 2.2.0
  ranch 2.2.0 => 2.2.1
  websock_adapter 0.5.8 => 0.5.9
2026-08-19 22:08:21 +02:00
Phantasm
c4ca9b41ad
Sec: Bump Phoenix to version 1.8.11 2026-08-19 22:08:20 +02:00
Phantasm
39d5648447
Sec: Bump bandit to 1.12.4
Also bumps:
  plug 1.20.2 => 1.20.3
  plug_crypto 2.1.1 => 2.2.0
  thousand_island 1.4.3 => 1.5.0
2026-08-19 22:07:22 +02:00
Phantasm
71cb70cd01
Sec: Bump postgrex to 0.22.4 2026-08-19 22:06:26 +02:00
Phantasm
124f4ece1d Merge pull request 'Syslog: set runtime: false to fix OTP releases' (#7966) from syslog-fix-otp into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7966
2026-08-18 20:47:55 +00:00
Phantasm
59042e2793
Syslog: set runtime: false to fix OTP releases
The syslog package is not directly part of the runtime.
2026-08-18 21:41:48 +02:00
Phantasm
0e642d4c90 Merge pull request 'Syslog: Fix intermittent failure to compile due to old pc version' (#7965) from syslog-pc-fix into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7965
2026-08-18 18:53:17 +00:00
Phantasm
16890c051a
Syslog: Fix intermittent failure to compile due to old pc version
Upstream hasn't made a release with the fix, pin fixed commit
for the time being.
2026-08-18 19:54:42 +02:00
Phantasm
3ecfe034d7 Merge pull request 'Oban: Enable Reindexer plugin to reduce oban_jobs index DB bloat' (#7964) from oban-reindex-cron into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7964
2026-08-18 17:51:30 +00:00
Phantasm
6a5ca6213a
Oban: Enable Reindexer plugin to reduce oban_jobs index DB bloat 2026-08-18 19:29:00 +02:00
Phantasm
48d6e9476b Merge pull request 'Revert "Merge pull request 'MediaProxy: detect images when there's no file extension' (#7933) from mediaproxy-content-type into develop"' (#7959) from revert-7933 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7959
2026-08-14 20:24:56 +00:00
Phantasm
96d69bfa0a Merge pull request 'Docs: Fix OTP install links left behind during migration, broke up wall of install commands in OTP guides' (#7960) from fix-otp-guide-links into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7960
2026-08-13 22:01:27 +00:00
Phantasm
5ecc936444
Docs: split wall of install commands, fix downloading OTP bundle
Bundle could not be downloaded, because sudo does not cope with the
previous syntax that double quoted the whole command, and the download
link was single quoted.
2026-08-13 23:52:06 +02:00
Phantasm
2dda6cb52a
Docs: Update OTP bundle links left behind in the Forgejo migration 2026-08-13 23:26:44 +02:00
Phantasm
f672ab337c
Revert "Merge pull request 'MediaProxy: detect images when there's no file extension' (#7933) from mediaproxy-content-type into develop"
A partial revert, keeping the updated @inline_content_types list and
cosmetic changes.

This reverts commit 4fe567064a, reversing
changes made to 9b8aa2e2ee.
2026-08-13 22:02:11 +02:00
Lain Soykaf
656191bd1e
fix(deps): use released Hackney 4.7.3 2026-08-11 14:16:59 +04:00
nicole mikołajczyk
ad7f68b924 Merge pull request 'Embed notificaiton details for grouped move/reaction/chat/report notification' (#7954) from mkljczk/pleroma:grouped-notifs-embed into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7954
2026-08-03 14:07:32 +00:00
nicole mikołajczyk
2876fed876 Embed notificaiton details for grouped move/reaction/chat/report notification
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-08-02 22:13:31 +02:00
lain
f53342775d Merge pull request 'Add rollback migrations for Rebased' (#7685) from gitlab-mr-iid-4308 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7685
2026-07-29 12:31:25 +00:00
Lain Soykaf
478b021fbe
fix(migrations): make Rebased rollback safe 2026-07-29 15:22:13 +04:00
nicole mikołajczyk
e1e853e8f7
Remove migration merged upstream
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-29 15:22:13 +04:00
mkljczk
2e59dcb9f6
style
Signed-off-by: mkljczk <git@mkljczk.pl>
2026-07-29 15:22:13 +04:00
mkljczk
e516c43411
Add docs for Rebased migration
Signed-off-by: mkljczk <git@mkljczk.pl>
2026-07-29 15:22:13 +04:00
mkljczk
253bbb80f5
Add rollback migrations for Rebased
Signed-off-by: mkljczk <git@mkljczk.pl>
2026-07-29 15:22:13 +04:00
lain
e143e645d3 Merge pull request 'OpenRC: Automatically run migrations before starting pleroma' (#7680) from gitlab-mr-iid-4303 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7680
2026-07-29 11:07:32 +00:00
Lain Soykaf
8943ab8140
fix(openrc): preserve migration execution context 2026-07-29 15:02:16 +04:00
lain
1dd6a345bf Merge pull request 'Respect activity restrictions in account statuses' (#7943) from mkljczk/pleroma:issue-2604-restrict-account-statuses into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7943
2026-07-29 06:44:26 +00:00
Lain Soykaf
4a47a756be
fix(mastodon-api): optimize account status restrictions 2026-07-28 16:01:48 +04:00
nicole mikołajczyk
1bb5eca063 Merge pull request 'Fix typo in spec' (#7947) from mkljczk/pleroma:typo into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7947
2026-07-28 10:41:05 +00:00
nicole mikołajczyk
5c85dadb29 Fix typo in spec
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-28 01:06:00 +02:00
lain
a45eb365ed Merge pull request 'Abort incomplete media proxy responses' (#7946) from lambadalambda/pleroma:fix/media-proxy-truncated-cache into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7946
2026-07-27 15:08:06 +00:00
lain
a22062a224 Merge pull request 'OAuth App: Fix test, optimize code.' (#7692) from gitlab-mr-iid-4315 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7692
2026-07-27 14:42:53 +00:00
Lain Soykaf
bc999b3309
fix(media-proxy): abort truncated streams 2026-07-27 16:32:44 +04:00
lain
16581a2f6d Merge pull request 'Update Debian and Ubuntu installation requirements' (#7806) from gitlab-mr-iid-4429 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7806
2026-07-27 12:16:12 +00:00
Lain Soykaf
be5a3957b2
docs: explain libvips dependency 2026-07-27 16:12:21 +04:00
Lain Soykaf
3b71770cfa
docs: update Debian and Ubuntu requirements 2026-07-27 15:31:59 +04:00
Lain Soykaf
030c7e929c
style(oauth): use single alias directives 2026-07-27 12:14:58 +04:00
Lain Soykaf
d30d205f0b
fix(oauth): preserve dependent app cleanup 2026-07-27 11:57:16 +04:00
Lain Soykaf
b4c624911c
Merge branch 'develop' into gitlab-mr-iid-4315 2026-07-27 11:34:31 +04:00
lain
8ab0e046b1 Merge pull request 'Allow creating accounts without a password in the AdminAPI, update docs' (#7937) from mkljczk/pleroma:admin-api-passwordless-user-creation into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7937
2026-07-27 07:17:14 +00:00
Phantasm
ea49ad121e Merge pull request 'Fix missing readme logo' (#7945) from readme-logo-fix into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7945
2026-07-26 19:18:24 +00:00
Phantasm
6132c9e88a
Fix missing readme logo 2026-07-26 21:14:29 +02:00
Lain Soykaf
2b32e3cf1f
fix(mastodon-api): restrict unauthenticated account statuses 2026-07-26 18:12:49 +04:00
lain
fd2074a30d Merge pull request 'Mastodon-compatible webhooks' (#7844) from mkljczk/pleroma:webhooks into develop 2026-07-26 13:10:48 +00:00
Lain Soykaf
03e75d3b3e
fix(webhooks): merge develop and harden delivery 2026-07-26 17:00:14 +04:00
lain
b9ef1e2182 Merge pull request 'Restore preview cards tests and update their OpenAPI description' (#7444) from gitlab-mr-iid-4067 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7444
2026-07-26 10:32:26 +00:00
lain
e210e16a37 Merge pull request 'MastoAPI: /api/{v1,v2}/admin/accounts, /api/v1/admin/reports' (#7842) from mkljczk/pleroma:mastodon-admin-api into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7842
2026-07-26 10:06:01 +00:00
Lain Soykaf
842e6bcaff
Merge remote-tracking branch 'origin/develop' into pr-7444-finish 2026-07-26 13:56:09 +04:00
Lain Soykaf
c591ca518c
fix(mastodon-api): finish preview card compatibility 2026-07-26 13:55:57 +04:00
Lain Soykaf
55c20c0fe8
test(mastodon-api): avoid relationship expiry races 2026-07-26 13:54:28 +04:00
lain
2ce6e5f49d Merge pull request 'Add ParadeDB search backend' (#7849) from paradedb-search-backend into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7849
2026-07-26 09:28:06 +00:00
Lain Soykaf
458a15bf9f
fix(mastodon-api): harden admin moderation actions 2026-07-26 13:24:03 +04:00
Lain Soykaf
7a741736ea
Merge remote-tracking branch 'origin/develop' into pr-7444-finish 2026-07-26 13:20:50 +04:00
lain
3c916891f6 Merge branch 'develop' into paradedb-search-backend 2026-07-26 09:15:42 +00:00
lain
a5aad47cff Merge branch 'develop' into mastodon-admin-api 2026-07-26 08:52:01 +00:00
lain
d115b41848 Merge pull request 'Reject unsupported HTTP Message Signatures' (#7918) from activitypub-bot-federation into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7918
2026-07-26 08:50:16 +00:00
lain
34172673d8 Merge pull request 'Accept more ActivityPub object variants from fedidev.fun samples' (#7805) from gitlab-mr-iid-4428 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7805
2026-07-26 08:50:03 +00:00
Lain Soykaf
6e73d043ef
test(config): exclude static fixture from test loader 2026-07-26 12:40:38 +04:00
lain
f5dd75b1a3 Merge branch 'develop' into activitypub-bot-federation 2026-07-26 08:36:46 +00:00
Lain Soykaf
ce5c1b4c15
fix(activitypub): harden object normalization 2026-07-26 12:35:09 +04:00
Lain Soykaf
cece76e4cb
docs(search): document ParadeDB operations 2026-07-26 12:32:24 +04:00
Lain Soykaf
9df50eea82
fix: reject duplicate Signature headers 2026-07-26 12:26:35 +04:00
Lain Soykaf
f90a52398b
ci(search): gate pinned ParadeDB integration 2026-07-26 12:16:04 +04:00
Lain Soykaf
13c944a04b
fix(search): index remote attachment descriptions 2026-07-26 12:03:24 +04:00
Lain Soykaf
271c3a3dd8
fix(config): keep search startup settings static 2026-07-26 11:58:31 +04:00
Lain Soykaf
acc44a5b6f
Merge remote-tracking branch 'origin/develop' into pr-7805-finish 2026-07-26 11:55:01 +04:00
lain
51349f61f8 Merge branch 'develop' into mastodon-admin-api 2026-07-26 07:46:55 +00:00
Lain Soykaf
91089c93ad
feat(search): add resumable runtime backfill 2026-07-26 11:38:24 +04:00
Lain Soykaf
a9862dcaa0
chore: merge develop into ParadeDB backend 2026-07-26 11:24:37 +04:00
Lain Soykaf
498c9ad532
test(http): cover the Hackney connection leak
Serve a response announcing far more than it sends, then close, and
assert the connection process is gone. Fails against stock 4.7.2 with a
conn parked in `closed` holding the bytes it read; passes against the
pinned fix.

Also stop the Hackney tests trusting proxy environment variables.
`no_proxy_env` is not a Hackney option and never was, so a proxy in the
build environment silently turned these into tunnelled requests, which
are never pooled -- exactly the shape several of them mean to exclude.
`proxy: false` is what Hackney actually reads.

That surfaced a matching bug in the reverse proxy client, which treated
`proxy: false` as "a proxy is set" and so declined to mark a pooled
bodyless response as already released.
2026-07-26 10:15:52 +04:00
Lain Soykaf
42c100605e
fix(deps): pin Hackney to the connection-leak fix
An unpooled Hackney 4 connection that ends without a reusable socket -- a
body cut short by the peer, a bodyless 204/304, or a failed read -- parks
in its `closed` state forever, holding every refc binary it read.
read_full_body/2 hands back `socket = undefined`, so it never reaches the
reuse check; `closed(enter)` arms a stop timer only for pooled
connections; and an unpooled connection is owned by hackney_conn_sup, so
the owner-DOWN cleanup never fires.

Nothing here can work around it. Since 4.0 Hackney ignores `with_body`
and always returns a fully read binary, so a synchronous request never
yields a handle to close, and the short read still reports {:ok, body}.
Media proxy warming hits this whenever an attachment fetch is cut short
and the outbound proxy is used as a CONNECT tunnel, since tunnels are
never pooled.

Pin the fix, a single commit on top of the 4.7.2 tag, until it ships in a
release. While it is a git dep Hackney drops out of `mix deps.audit` and
`mix hex.outdated`, so this wants moving back to hex promptly.

benoitc/hackney#918, benoitc/hackney#919
2026-07-26 10:15:52 +04:00
Lain Soykaf
0615025f92
fix(http): enforce max_body again under Hackney 4
Hackney 4 ignores `with_body` and always returns a fully read binary, so
Tesla.Adapter.Hackney never reaches the clause that honours :max_body and
the option became a no-op. Only the gun path still enforced it, through
Pleroma.HTTP.Adapter.Gun. Pleroma.Web.RelMe relies on nothing else, so it
was fetching remote pages unbounded.

Add a middleware that applies the limit for every adapter: reject when
content-length already announces too much, and reject a body that came
back over the limit. Streaming bodies are left alone -- neither adapter
limits those and the caller owns the lease, so failing here on the header
would strand a connection RichMedia is still expected to release.

On the hackney path this bounds what callers are handed rather than peak
memory, since the adapter has read the body before any middleware runs.
2026-07-26 09:29:36 +04:00
Lain Soykaf
7d1cc56a78
Merge remote-tracking branch 'origin/develop' into audit-mix-audit-report
Conflict resolution notes:

- mix.lock: kept this branch's versions for cowlib, h2, hackney and hpax.
  Those upgrades are the point of the branch; develop is still on hackney
  1.20.1.

- .woodpecker/docker-armv7.yaml: accepted develop's deletion. It only
  existed here to build mdex_native on armv7, and mdex was already
  replaced with earmark again.

- lib/pleroma/reverse_proxy.ex: took develop's version. It independently
  grew request_with_constraints/5 and the invalid-status close, which
  supersede the equivalent fixes on this branch. Re-applied this branch's
  recv_timeout seeding, which develop does not have.

- lib/pleroma/gun/connection_pool/worker.ex and
  lib/pleroma/reverse_proxy/client/tesla.ex: took develop's versions. The
  gun pool rework (#7934) already guards the demonitor and makes
  release_conn/cancel_stream exit-safe, and it splits the Tesla client's
  graceful release from its abort path, so this branch's changes there are
  obsolete.

That leaves the client leak fix as a Hackney-only concern, which
stream_body/1 now handles the same way the gun client does.
2026-07-25 19:54:48 +04:00
Lain Soykaf
f985346fe8
fix(gun): tolerate a missing client monitor on release
A client that is already gone leaves no monitor to demonitor. Crashing
the worker takes the shared connection down with it and fails every
other client using it.
2026-07-25 18:46:44 +04:00
Lain Soykaf
24bdd9774a
fix(reverse_proxy): release the HTTP client on every outcome
Three paths obtained a client ref and abandoned it without closing:
the content-length rejection in call/3, the invalid-status clause in
request/4, and the success branch of response/6.

Under Hackney 4 an abandoned ref is a permanent process leak. A conn
that finishes or errors without an explicit stop parks in `closed` or
`connected` and never terminates: closed/3 only arms its stop timer for
pooled conns, `connected` gets no state_timeout when idle_timeout is
infinity, and a non-pooled conn monitors hackney_conn_sup rather than
its caller, so the owner-DOWN cleanup never fires. Each parked process
pins every refc binary it read, which for proxied video is tens of MB.

Releasing on every outcome means the gun client must stop releasing in
stream_body/1, or the ref would be removed twice.

Hackney does reclaim a pooled connection itself for a response that
cannot carry a body (204, 304, HEAD), so those refs are marked and left
alone; stopping one would evict a connection another request may
already hold. The mark is gated on the request actually being pooled,
since a direct or proxy-tunnelled conn is never checked in and leaks
unless we stop it.
2026-07-25 18:46:43 +04:00
lain
e3d15b1b53 Merge pull request 'Fix account favourites ordering' (#7914) from issue-3379-favourites-order into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7914
2026-07-25 07:04:39 +00:00
Lain Soykaf
99befcaf1b
fix(activitypub): deduplicate account favourites 2026-07-25 10:51:09 +04:00
Lain Soykaf
9a6c2f909d
Merge remote-tracking branch 'origin/develop' into issue-3379-favourites-order 2026-07-25 10:31:57 +04:00
lain
cc1b95c88e Merge pull request 'Rework Gun connection pooling' (#7934) from issue-2263-gun-validation into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7934
2026-07-25 05:44:39 +00:00
lain
4fe567064a Merge pull request 'MediaProxy: detect images when there's no file extension' (#7933) from mediaproxy-content-type into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7933
2026-07-25 05:36:16 +00:00
Lain Soykaf
5ddd337045
chore: remove development log 2026-07-25 09:26:26 +04:00
Lain Soykaf
d5f48101b8
fix(media-proxy): safely detect extensionless images 2026-07-25 00:06:03 +04:00
Lain Soykaf
bb4826d8ae
fix(admin-api): structure reset token failures 2026-07-24 23:24:52 +04:00
Lain Soykaf
465a81ecf2
Merge remote-tracking branch 'origin/develop' into review-pr-7933 2026-07-24 23:24:11 +04:00
Lain Soykaf
d0b5a05075
fix(admin-api): confirm created users 2026-07-24 23:14:03 +04:00
Lain Soykaf
836f4a2d69
fix(admin-api): reject explicit empty passwords 2026-07-24 22:52:23 +04:00
Lain Soykaf
3b3aad6065
fix(api): isolate sensitive idempotency responses 2026-07-24 22:45:50 +04:00
Lain Soykaf
d57fdcc7b0
fix(admin-api): create reset tokens atomically 2026-07-24 22:25:44 +04:00
Lain Soykaf
34d827730a
fix(admin-api): preserve user batch validation 2026-07-24 22:15:14 +04:00
lain
9b8aa2e2ee Merge pull request 'Woodpecker CI: Unify Docker builds, add arm32v7 to multi-arch images' (#7919) from woodpecker-armv7-fixups into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7919
2026-07-24 18:12:46 +00:00
lain
c2b8c67808 Merge pull request 'Improve RSS compliance' (#7932) from fix-rss into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7932
2026-07-24 17:33:40 +00:00
Lain Soykaf
5d8b5a73d2
style(feed): format Atom fallback assertion 2026-07-24 21:05:16 +04:00
Lain Soykaf
c5230d98c5
fix(feed): complete Atom XML metadata 2026-07-24 21:04:35 +04:00
Lain Soykaf
5f71532442
fix(feed): tighten enclosure metadata 2026-07-24 20:59:13 +04:00
Lain Soykaf
2d667e1d2a
style(feed): format enclosure fixtures 2026-07-24 20:44:58 +04:00
Lain Soykaf
c518319349
fix(feed): correct RSS permalink metadata 2026-07-24 20:43:40 +04:00
Lain Soykaf
ecd8a14435
fix(feed): correct Atom entry links 2026-07-24 20:38:25 +04:00
Lain Soykaf
d1a7e915ce
fix(feed): preserve standard media enclosures 2026-07-24 20:34:33 +04:00
Lain Soykaf
96f471a291
fix(feed): format RSS build dates 2026-07-24 20:21:54 +04:00
Lain Soykaf
d0c8ca92a5
fix(feed): escape self link URLs 2026-07-24 20:11:19 +04:00
Lain Soykaf
85a802282e
fix(feed): hide sensitive media previews 2026-07-24 20:08:04 +04:00
Lain Soykaf
a9bf7212d4
fix(feed): escape fallback content 2026-07-24 19:59:18 +04:00
lain
a790e08a6c Merge pull request 'hashtag-timeline-fix' (#7938) from mkljczk/pleroma:hashtag-timeline-fix into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7938
2026-07-24 15:29:25 +00:00
Lain Soykaf
77b5f3f485
test: cover multi-hashtag pagination 2026-07-24 18:49:44 +04:00
Phantasm
ce4e1d4287
ReverseProxy Tesla: Explicitly reformat proxy 2026-07-24 16:40:58 +02:00
Lain Soykaf
b97092a97e
fix: preserve optimized hashtag ordering 2026-07-24 18:36:25 +04:00
lain
ef2b374850 Merge pull request 'Apply architecture review quick wins' (#7941) from architecture-quick-wins into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7941
2026-07-24 14:31:39 +00:00
lain
c20c0ee867 Merge branch 'develop' into architecture-quick-wins 2026-07-24 14:09:44 +00:00
Lain Soykaf
0070b13130
test(workers): replace publisher mocks with Mox 2026-07-24 17:43:02 +04:00
lain
9b1721888e Merge pull request 'Allow disabling Chats feature' (#7940) from mkljczk/pleroma:disabling-chats into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7940
2026-07-24 13:08:43 +00:00
Lain Soykaf
0862c8b88d
fix: advertise Chats feature flags correctly 2026-07-24 16:53:36 +04:00
Lain Soykaf
1a48466b85
style(tests): sort publisher worker aliases 2026-07-24 16:42:45 +04:00
Lain Soykaf
868723e371
fix(streaming): preserve followed domain exception 2026-07-24 16:01:46 +04:00
Lain Soykaf
3abe4ebfbd
perf(oauth): avoid duplicate user preload 2026-07-24 16:01:34 +04:00
Lain Soykaf
08c8571c59
fix(workers): avoid dynamic publisher atoms 2026-07-24 16:01:24 +04:00
Lain Soykaf
c3fe61c2f5
fix(user): correct cache key invalidation 2026-07-24 16:01:13 +04:00
nicole mikołajczyk
97b94a2f43 Update tests
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-24 01:05:12 +02:00
nicole mikołajczyk
6ffa90a42a Include actual accepts_chat_messages in source field
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-24 01:00:46 +02:00
nicole mikołajczyk
efb43a02fa changelog
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-24 00:59:15 +02:00
nicole mikołajczyk
4aa0d5d709 Allow disabling Chats feature
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-24 00:58:58 +02:00
Lain Soykaf
bf477c1a53
fix(deps): replace mdex with earmark parser 2026-07-23 13:02:28 +04:00
Lain Soykaf
d090aeb2bb
fix(http): forward plain HTTP proxy requests 2026-07-22 20:35:51 +04:00
Lain Soykaf
77a7ba3a0e
fix(http): harden Hackney request lifecycle 2026-07-22 16:18:04 +04:00
Lain Soykaf
e63b7d6715
fix(formatter): preserve remote MFM mentions 2026-07-22 14:17:17 +04:00
Lain Soykaf
e6598ae5c7
fix(ci): build mdex native on armv7 2026-07-21 21:23:18 +04:00
nicole mikołajczyk
2a56a02d9a changelog
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-21 01:26:06 +02:00
nicole mikołajczyk
59269cf07a Fix hashtag timeline query
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-21 01:26:06 +02:00
nicole mikołajczyk
c772f9b40a Add a failing test
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-21 01:26:06 +02:00
nicole mikołajczyk
36bc7b116d Allow creating accounts without a password in the AdminAPI, update docs
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-07-20 19:01:02 +02:00
Phantasm
0a076443ad Merge pull request 'Fix preferred frontends' (#7911) from phnt/pleroma:fix-preferred-frontend into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7911
2026-07-20 15:29:02 +00:00
Phantasm
1d6664b1f4 InstanceStaticPlug: Add more override and fallback tests 2026-07-20 15:18:49 +00:00
Phantasm
85a0f2e602 StaticNotFoundPlug: Remove unused plug options 2026-07-20 15:18:49 +00:00
Phantasm
00ca971390 FrontendStaticPlug: Add test for static files outside of index 2026-07-20 15:18:49 +00:00
Phantasm
9129c65795 StaticNotFoundPlug: Move tests out from InstanceStatic 2026-07-20 15:18:49 +00:00
Phantasm
a30298c1eb StaticNotFoundPlug: Move 404 on non-existing static files to a new plug
This fixes preferred frontends not working (returning 404 on most static
frontend files).
2026-07-20 15:18:49 +00:00
Phantasm
103b724962 EnsureHostMatchesPlug: add missing behavior declaration 2026-07-20 15:18:49 +00:00
Phantasm
7ab1901a28
Woodpecker CI Docker: Bump Kaniko plugin version to 2.3.2
Removes the temporary downgrade and updates the plugin to a version that
includes kaniko with a fix for:
https://github.com/osscontainertools/kaniko/issues/745
2026-07-20 16:59:39 +02:00
Phantasm
56105bc18d
Woodpecker CI Docker: fully qualify docker images 2026-07-20 16:49:54 +02:00
Phantasm
8c8c2de1e1
Woodpecker CI Docker: Downgrade to kaniko plugin version 2.2.2
2.3.1 duplicates arch variant in the platform triplet resulting in
podman erroring out when using the image.
2026-07-20 16:49:53 +02:00
Phantasm
f7be7f8018
Woodpecker CI Docker: Switch to kaniko_image anchor everywhere 2026-07-20 16:49:53 +02:00
Phantasm
7d4b53bf48
Woodpecker CI: include armv7 arch in combined Docker tags 2026-07-20 16:49:53 +02:00
Phantasm
631094666c
Woodpecker CI: Unify armv7 workflow with main Docker workflow 2026-07-20 16:49:49 +02:00
Phantasm
6ec04af08c Merge pull request 'Woodpecker CI: Build Vix in unit testing pipelines, do not build arm32v7 images in MRs' (#7936) from ci-build-vix into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7936
2026-07-20 09:28:05 +00:00
Phantasm
ac08f0487f
Woodpecker CI: Do not build arm/v7 in PR, simplify 2026-07-20 11:17:09 +02:00
Phantasm
bde0e3e353
Woodpecker CI: Build Vix in unit test pipelines instead of downloading 2026-07-20 11:16:25 +02:00
Phantasm
a2eea6768c Merge pull request 'Dockerfile: update base images to fix problems with SSL handshakes' (#7930) from eal/pleroma:develop into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7930
2026-07-20 09:08:59 +00:00
Phantasm
38bb47b615 Merge pull request 'Elixir 1.19 support' (#7758) from gitlab-mr-iid-4381 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7758
2026-07-19 22:00:00 +00:00
Phantasm
3ba3319390
Lint 2026-07-19 23:12:20 +02:00
Phantasm
00c9059252
Remove unused require Logger 2026-07-19 22:23:20 +02:00
Phantasm
bbae94ddf3
Revert "Only warn about bad clear_config usage if warned usage is met"
What was I even thinking when writing that?

This reverts commit 3790e6786c.
2026-07-19 22:15:37 +02:00
Phantasm
0448ba5cd2
ApplicationTest: skip test spamming warnings from ExSyslogger
:warn log level is deprecated, but ExSyslogger hasn't been updated yet.
2026-07-19 22:15:20 +02:00
Phantasm
4c27a3c5a0
ConfigDB: Refactor AdminAPI controller config filtering 2026-07-19 22:15:20 +02:00
Phantasm
232cdab753
ConfigDB: Refactor and simplify whitelist/blacklist filter tasks 2026-07-19 22:15:20 +02:00
Phantasm
3ab8cc1eb9
ApplicationTest: make clear_config delete the whole env tree
clear_config([:logger, :backends], [value]) leaves the :logger env key
behind which breaks a ConfigDB test where the :logger key shouldn't be
stored and set.
2026-07-19 22:15:19 +02:00
Phantasm
70cb241173
Elixir 1.19: Fixup unit testing CI name 2026-07-18 23:07:52 +02:00
Phantasm
a9aa19585b
Elixir 1.19: Emit debug logger reconfiguration logs in test env as well 2026-07-18 23:02:38 +02:00
Phantasm
e2f744bd68
Merge branch 'develop' into gitlab-mr-iid-4381 2026-07-18 20:47:03 +02:00
Phantasm
a89fe828d3
ConfigDB: Don't use a different config whitelist globally in tests 2026-07-18 20:45:51 +02:00
Phantasm
41ab347571
Lint 2026-07-18 16:13:39 +02:00
Phantasm
d383c453a1
Elixir 1.19: Added features changelog (ConfigDB blacklist) 2026-07-18 16:10:22 +02:00
Phantasm
c688d09478
ConfigDB: Implement config update blacklist in AdminAPI 2026-07-18 16:06:11 +02:00
Phantasm
2d307c1487
ConfigDB: Support blacklisting whole config groups 2026-07-18 16:05:14 +02:00
eal
7de1affda8 Dockerfile: update base images to fix problems with SSL handshakes
My instance has been having problems with push federation since
updating to v2.10.2, using the docker image. Posts are not reaching
their destination instance until some other interaction initiates a
fetch from source.

The logs contain lots of errors such as:

```
pleroma    | 09:34:40.885 [notice] TLS :client: In state :wait_cert_cr at ssl_handshake.erl:2158 generated CLIENT ALERT: Fatal - Unsupported Certificate
pleroma    |  - {:key_usage_mismatch,
pleroma    |  {{:Extension, {2, 5, 29, 15}, true, [:keyCertSign, :cRLSign]},
pleroma    |   {:Extension, {2, 5, 29, 37}, false, [{1, 3, 6, 1, 5, 5, 7, 3, 1}]}}}
```

This erlang/OTP issue seems to be the same problem:
https://github.com/erlang/otp/issues/9208

The erlang version committed here was selected after discussion in the
merge request.

I have not noticed any federation problems with an image built from
this dockerfile.
2026-07-18 11:15:45 +03:00
Phantasm
ebd5596ba7
Elixir 1.19: Create ConfigDB blacklist and task for :pleroma, :logger
This option was never configurable with ConfigDB when it lived under
:logger, :backends; no need to be configurable now.
2026-07-17 23:32:43 +02:00
Phantasm
1d758dbbf5
Elixir 1.19: Do not restart :logger in ConfigDB TransferTask 2026-07-17 22:39:43 +02:00
Lain Soykaf
b862254889
fix(deps): update Gun to 2.4.1 2026-07-17 21:09:13 +04:00
Lain Soykaf
d0d6c986f4
docs(http): document Gun pool behavior 2026-07-17 20:35:40 +04:00
Lain Soykaf
f11ffca7f5
feat(http): rework Gun connection pooling 2026-07-17 20:35:17 +04:00
moon.eth
b11e8888d1 MediaProxy: improve inline rendering of remote image attachments
Two related changes that ensure remote images render inline in timelines
instead of as clickable links.

reverse_proxy: always force inline for whitelisted content types
  When `inline_content_types` is configured as a whitelist, the upstream's
  `content-disposition` header used to be passed through unchanged. Some
  origins send `content-disposition: attachment` for media we want
  embedded, so the proxy now always emits `content-disposition: inline`
  for whitelisted types (overriding the upstream header), deriving a
  filename from `MIME.extensions/1` (e.g. `inline.gif`) so browsers have
  something to use for "Save As". When the type has no known extension,
  the bare token `inline` is emitted. The `inline_content_types: true`
  (pass-through) and `:false` (always attachment) behaviours are
  unchanged.

ActivityPub: sniff MIME type for extensionless/generic attachments
  Some remotes (e.g. extensionless Cloudflare Images URLs) ship
  attachments with no usable `mediaType` (missing, empty, or
  `application/octet-stream`), which would render as links instead of
  inline images. `AttachmentValidator.fix_media_type/1` now fetches the
  first 8 KB of the URL and runs it through libmagic when the declared
  type carries no real information. Only `image/*` results replace the
  declared type; any other result, non-2xx response, transport error, or
  exception yields `nil` and the original type is kept, so ingestion
  never blocks and non-image media is never reclassified. Present real
  types are left untouched (no fetch is attempted).

The new logic lives in `Pleroma.Web.ActivityPub.ObjectValidators.AttachmentTypeSniffer`.

Tests: comprehensive coverage of both changes, including a dedicated
suite for the sniffer covering happy paths, non-image bodies, HTTP
failures, raised exceptions, guard clauses, and verification that the
Range header is sent; AttachmentValidator tests for mimeType fallback,
empty-string mediaType, multi-URL sniffing, and HTTP-failure fallback;
ReverseProxy tests for inline_content_types `true`/`false`/custom
whitelists, bare-inline for types without a MIME extension, and
attachment_name handling.
2026-07-14 15:04:06 +09:00
Mark Felder
f4b415c54e Improve RSS compliance
The core feed reader issue was that RSS 2.0 <link> is a simple text
element and can only appear once per <item>.
By having multiple <link> tags with rel attributes, parsers were
getting confused and resolving entries to the wrong URL.
Now each item has exactly one <link>: the post URL. Everything else
uses valid <atom:link> or dedicated extension elements where
appropriate.
2026-07-10 16:57:13 +00:00
Lain Soykaf
dc348e8be8
fix(deps): adapt hackney and mdex behavior 2026-07-02 12:48:52 +04:00
Lain Soykaf
9154f6fe3c
fix(deps): replace earmark with mdex 2026-07-02 11:31:54 +04:00
Lain Soykaf
a96fb31a89
fix(deps): update hackney 2026-07-02 10:57:12 +04:00
Lain Soykaf
28d7ac3a43
fix(deps): update decimal 2026-07-02 10:50:20 +04:00
Lain Soykaf
ab49f00090
chore(deps): update jason 2026-07-02 10:48:06 +04:00
Lain Soykaf
604e2ad849
fix(deps): remove unused quic lock 2026-07-02 10:40:53 +04:00
Lain Soykaf
d964737d75
fix(deps): update postgrex 2026-07-02 10:39:34 +04:00
Lain Soykaf
dc28528588
fix(deps): update cowboy 2026-07-02 10:36:53 +04:00
Lain Soykaf
eae1311bc2
fix(deps): update cowlib 2026-07-02 10:35:41 +04:00
Lain Soykaf
4d1044e2d1
fix(deps): update plug_cowboy 2026-07-02 10:33:50 +04:00
Lain Soykaf
dcc5a33f4d
fix(deps): update phoenix 2026-07-02 10:31:34 +04:00
Lain Soykaf
f6cc4c144b
fix(deps): update plug 2026-07-02 10:27:21 +04:00
Lain Soykaf
fa7986a267
fix(deps): update bandit 2026-07-02 10:26:12 +04:00
Lain Soykaf
7e54b0935b
chore(deps): add mix audit 2026-07-02 10:20:59 +04:00
Phantasm
70c79ad512
Merge branch 'develop' into gitlab-mr-iid-4381 2026-06-30 00:31:51 +02:00
Phantasm
7d9175350d
Elixir 1.19: Completely overwrite formatter for default handler
:logger.update_handler_config/3 can merge configs,
:logger.set_handler_config/3 instead resets the rest to defaults.
2026-06-30 00:24:48 +02:00
Phantasm
a020b8a190
Elixir 1.19: Ensure default primary logger configg level is debug
Setting the default handler and ex_syslogger handler levels does not
necessarily have to set the primary logger log level, at least in the
future. Without this, the primary log level might be lower than the
configured console/syslog levels, which would cause some desired logs to
not be logged.
2026-06-29 23:47:43 +02:00
nicole mikołajczyk
b0ae451942 Merge pull request 'Allow attaching emojis to user lists' (#7929) from mkljczk/pleroma:list-emoji into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7929
2026-06-29 12:51:43 +00:00
nicole mikołajczyk
cb0a1d1602 Merge pull request 'Grouped Notifications: Add feature flag to instance view and nodeinfo' (#7926) from phnt/pleroma:grouped-notifs-flag into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7926
2026-06-29 11:28:45 +00:00
Phantasm
964feb1e47
InstanceView: Remove unreachable guards in filter_render 2026-06-28 00:36:43 +02:00
Phantasm
dd07088377
InstanceView: lint tests 2026-06-28 00:19:38 +02:00
Phantasm
3027052b0d
InstanceView: Remove wall of changed configs in show render tests.
Switched to using config defaults instead.
2026-06-28 00:17:28 +02:00
Phantasm
d19a9f768c
InstanceView: Tighten show2.json 2026-06-27 23:39:48 +02:00
Phantasm
6e03f1d789
InstanceView: Tighten show.json render test, rename show2-only funcs 2026-06-27 23:34:01 +02:00
Phantasm
ef321846d4
InstanceView: Use attribute for pleroma_configuration filter in test 2026-06-27 22:44:59 +02:00
Phantasm
6c79cc792f
InstanceView: improve filtering in tests, simplify 2026-06-27 22:42:50 +02:00
Phantasm
9912b2d16a
InstanceView: lint tests 2026-06-26 02:05:55 +02:00
Phantasm
93fe0f5ab5
InstanceView: Filter out *configuration keys from *configuration2 2026-06-26 02:05:50 +02:00
Phantasm
6393fe0a9e
InstanceView: removing unneeded doubling of attributes in tests 2026-06-26 01:59:57 +02:00
Phantasm
0337bd0ae4
InstanceView: Add show2.json render test 2026-06-26 01:59:57 +02:00
Phantasm
c290f022de
InstanceView: Add show.json render test 2026-06-26 01:59:54 +02:00
Phantasm
e45dad2104
InstanceView: Add domain_blocks.json render test 2026-06-23 22:42:15 +02:00
Phantasm
df67f21974
InstanceView: use shorter setup syntax when applicable 2026-06-23 22:24:44 +02:00
Phantasm
86725b5a00
InstanceView: add rule(s), translation render tests 2026-06-23 22:22:49 +02:00
Phantasm
7e9b8a7663
InstanceView: Add federation/0 func test 2026-06-23 18:20:16 +02:00
Phantasm
19a84baf82
InstanceView: Add feature flag tests 2026-06-23 17:26:47 +02:00
nicole mikołajczyk
c6d1cead83 Allow attaching emojis to user lists
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-06-23 16:08:52 +02:00
Phantasm
9782bcf979
Elixir 1.19: Fix most MRFs being absent from AdminAPI suggestions.
Elixir 1.19 started lazily loading modules, so modules only get loaded
when referenced. Thus :code.all_loaded would no longer include all MRF
policies which is used on startup to generate sugestions for AdminAPI.
2026-06-16 20:22:24 +02:00
Phantasm
07c65adb0c
Grouped Notifications: Add feature flag to instance view and nodeinfo 2026-06-10 22:54:06 +02:00
lain
6f5ea55613 Merge pull request 'allow <center> (used by mfm)' (#7920) from mfm-center into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7920
2026-06-05 09:24:15 +00:00
lain
98cd1bbb9a Merge pull request 'Add grouped notifications API' (#7910) from issue-7905-grouped-notifications into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7910
Reviewed-by: Phantasm <phnt@noreply.git.pleroma.social>
2026-06-05 09:23:26 +00:00
Henry Jameson
6165e2f342 Merge branch 'mfm-center' into shigusegubu-new 2026-06-01 22:18:24 +03:00
Henry Jameson
86dd9663fc allow <center> (used by mfm) 2026-06-01 22:06:45 +03:00
Lain Soykaf
e676ec3507
Reject unsupported HTTP Message Signatures 2026-05-29 18:35:15 +04:00
Lain Soykaf
fc9e6793ce
Address grouped notification review cleanup 2026-05-29 12:30:33 +04:00
Lain Soykaf
ab634a4aa3
Harden webhook API input 2026-05-26 15:51:46 +04:00
Lain Soykaf
27090ad7e6
Require admin access for webhooks 2026-05-26 15:51:46 +04:00
Lain Soykaf
9061851fed
Fix webhook read API scope 2026-05-26 15:51:46 +04:00
Lain Soykaf
719b5d65b5
Require webhook events 2026-05-26 15:51:46 +04:00
Lain Soykaf
e44d2329c8
Reject invalid webhook events 2026-05-26 15:51:46 +04:00
lain
f5f63bbc74 Merge branch 'develop' into webhooks 2026-05-26 05:15:23 +00:00
Lain Soykaf
afe33bcc44
Fix webhook create test expectations 2026-05-26 07:57:48 +04:00
Lain Soykaf
cd28c2089c
Fix ParadeDB search indexing 2026-05-25 18:45:47 +04:00
Lain Soykaf
a84d16831f
Fix account favourites ordering 2026-05-25 18:09:18 +04:00
lain
ac05f65fbb Merge branch 'develop' into paradedb-search-backend 2026-05-25 12:53:39 +00:00
lain
aa16f40f09 Merge pull request 'Prepared FE E2E Docker image' (#7884) from docker-e2e-image into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7884
2026-05-25 12:33:27 +00:00
lain
f99ce5b2e6 Merge pull request 'Support paged featured collections' (#7912) from issue-7887-featured-collection into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7912
2026-05-25 12:07:56 +00:00
Lain Soykaf
e023ed7a5a
Seed E2E admin before API readiness 2026-05-25 16:06:21 +04:00
Lain Soykaf
54369c93d7
Use E2E approval config key 2026-05-25 16:05:21 +04:00
Lain Soykaf
8a410bf49f
Quote E2E entrypoint variables 2026-05-25 16:04:53 +04:00
lain
e46365bf2a Merge pull request 'Woodpecker CI: Build armv7 Docker images' (#7882) from ci/build-armv7-docker-images into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7882
2026-05-25 11:34:27 +00:00
lain
33fdc59bc1 Merge branch 'develop' into issue-7887-featured-collection 2026-05-25 11:24:10 +00:00
lain
b1d72e16a3 Merge branch 'develop' into ci/build-armv7-docker-images 2026-05-25 11:21:23 +00:00
lain
c8c1f7d38b Merge pull request 'Fix Websockets streaming API pushing old already federated posts to TL' (#7907) from websockets-necroposts into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7907
2026-05-25 10:25:36 +00:00
Lain Soykaf
cdb0f103a8
Tighten rich media backfill stream test 2026-05-25 14:08:07 +04:00
lain
619db0adca Merge pull request 'Restore streaming WebSocket protocol tokens' (#7908) from issue-3298-websocket-protocol into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7908
2026-05-25 08:06:52 +00:00
Lain Soykaf
4c9fc6287e
Remove unreachable WebSocket protocol match 2026-05-25 09:26:07 +04:00
Phantasm
a5e7145c97 Merge pull request 'Woodpecker CI: Do not trigger on Weblate MRs' (#7903) from phnt/pleroma:weblate-no-ci into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7903
2026-05-24 21:47:18 +00:00
Phantasm
0c0d687330
credo, lint 2026-05-24 23:41:59 +02:00
Phantasm
6ee40cb2eb
RichMedia: Add StatusView backfill streaming tests 2026-05-24 23:41:59 +02:00
Phantasm
ff7927e219
changelog 2026-05-24 23:41:59 +02:00
Phantasm
5f55c9653c
RichMedia: Disable websockets backfill streaming in StatusView 2026-05-24 23:41:59 +02:00
Phantasm
678fe8a064
RichMedia: Add support for disabling wss streaming out on backfill 2026-05-24 23:41:58 +02:00
Phantasm
94a28d1286
RichMedia Backfill: Add cachex positive test 2026-05-24 23:41:58 +02:00
Lain Soykaf
a2a23b327b
Keep cursor cleanup scoped to grouped notifications 2026-05-23 14:27:11 +04:00
Lain Soykaf
1db2b99e48
Run group key migration before notification backfill 2026-05-23 14:00:17 +04:00
Lain Soykaf
add3844c6d
Persist notification group keys 2026-05-23 11:38:33 +04:00
lain
1a83b1d28d Merge pull request 'reorganize mfm attributes in order they're listed in misskey repo, add missing ones' (#7904) from mfm-extended into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7904
2026-05-23 05:54:48 +00:00
hj
cda6738309 Update priv/scrubbers/default.ex 2026-05-22 20:47:52 +00:00
Lain Soykaf
b054c2aa42
Fix paged featured collection fetches 2026-05-22 22:11:37 +04:00
Lain Soykaf
1e65c7a69e
Add grouped notifications API 2026-05-22 20:36:52 +04:00
Lain Soykaf
9dd02ecd50
Fix WebSocket protocol token handshakes 2026-05-22 16:30:14 +04:00
Henry Jameson
49486a4e64 Merge branch 'mfm-extended' into shigusegubu-new 2026-05-18 21:08:45 +03:00
Henry Jameson
46a2808690 extraneous commas 2026-05-18 21:08:24 +03:00
Henry Jameson
c428cf43e8 "changelog" 2026-05-18 21:07:12 +03:00
Henry Jameson
1a13ec539b reorganize mfm attributes in order they're listed in misskey repo, add missing ones 2026-05-18 21:05:46 +03:00
Phantasm
7575b7abc2
Woodpecker CI: Do not trigger on Weblate MRs 2026-05-14 12:34:27 +02:00
lain
57ab0a9302 Merge branch 'develop' into gitlab-mr-iid-4428 2026-05-14 09:39:16 +00:00
lain
4fe802b25a Merge branch 'develop' into gitlab-mr-iid-4381 2026-05-14 09:27:13 +00:00
lain
83dcc4ae36 Merge branch 'develop' into webhooks 2026-05-14 09:25:50 +00:00
lain
be2a5056a3 Merge branch 'develop' into paradedb-search-backend 2026-05-14 09:24:51 +00:00
lain
093b156c65 Merge pull request 'Switch Phoenix back to upstream' (#7803) from gitlab-mr-iid-4426 into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7803
2026-05-14 09:21:36 +00:00
lain
c7c453ca21 Merge branch 'develop' into gitlab-mr-iid-4426 2026-05-14 06:56:57 +00:00
lain
0cf221ba13 Merge pull request 'Translations update from Pleroma Weblate' (#7866) from weblate into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7866
2026-05-14 06:47:36 +00:00
lain
c5737898f5 Merge pull request 'Ensure only requests with Host header set to target instance pass through HTTP signatures.' (#7893) from phnt/pleroma:host-verification into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7893
2026-05-14 06:44:56 +00:00
lain
b90ac6b9c7 Merge branch 'develop' into host-verification 2026-05-14 06:01:31 +00:00
lain
5b63307f85 Merge pull request 'Use upstream remote_ip package' (#7900) from issue-3314-remote-ip-hex into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7900
2026-05-14 05:47:32 +00:00
Lain Soykaf
512b0f67a4
Merge remote-tracking branch 'origin/develop' into issue-3314-remote-ip-hex
# Conflicts:
#	mix.exs
2026-05-14 09:27:18 +04:00
Phantasm
784a319f2e
Woodpecker CI: Bump unit testing pipeline to Elixir 1.19 2026-05-14 00:22:24 +02:00
Phantasm
7651490c50
Merge branch 'develop' into gitlab-mr-iid-4381 2026-05-14 00:14:27 +02:00
Codimp
d7a0d97c36 Translated using Weblate (French)
Currently translated at 10.8% (106 of 974 strings)

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

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

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

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

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

Also adds is_integer guard to voters_count/1 to handle nil safely, and
adds tests for the voters_count clause ordering and edge cases.
2026-05-06 11:33:34 +04:00
Phantasm
3c63877e61
Add custom Docker image for FE E2E tests
Woodpecker cannot do custom commands for services.
2026-05-05 21:34:10 +02:00
lain
86a5213523 Merge pull request 'litepub-0.1.jsonld cleanup' (#7871) from mkljczk/pleroma:context-cleanup into develop
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7871
2026-05-05 08:22:29 +00:00
Yonle
aec0deef8b
poll_view: try to read votersCount first, and then manually count local voters. 2026-05-05 13:50:11 +07:00
nicole mikołajczyk
11281d740a make GET /api/v1/admin/reports match Mastodon admin API
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-05-04 13:49:40 +02:00
Lain Soykaf
394db0dce0
Woodpecker CI: Target armv7 Docker runner 2026-05-04 10:09:44 +04:00
Lain Soykaf
684e9ef247
Woodpecker CI: Isolate armv7 Docker builds 2026-05-04 09:41:06 +04:00
nicole mikołajczyk
ae763ff6cf api/v2/admin/accounts: support 'permissions' param
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-05-04 01:45:29 +02:00
Lain Soykaf
af175fbdfc
Woodpecker CI: Build armv7 Docker images 2026-05-03 21:19:24 +04:00
lain
cd8816ecce Merge pull request 'develop' (#7881) from develop into stable
Reviewed-on: https://git.pleroma.social/pleroma/pleroma/pulls/7881
2026-05-03 16:28:16 +00: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
Mark Felder
3776f13f20 Fix warning 2026-04-01 19:10:29 +00: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
5271876a5f Merge remote-tracking branch 'origin/phnt/oban-digest-queue' into paradedb-search-backend 2026-04-01 19:05:31 +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
Mark Felder
9fdc1a8d3b Merge remote-tracking branch 'origin/develop' into paradedb-search-backend 2026-03-26 21:29:11 +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
Mark Felder
1e3456a160 Ensure the Ecto Repo for ParadeDB is started for the data indexing Mix task 2026-03-25 15:14:35 -07:00
Mark Felder
8ac967518b Switch to the newly relocated Pleroma.Search.object_to_search_data/1 2026-03-25 15:11:45 -07:00
Mark Felder
9c8f653312 Merge remote-tracking branch 'origin/develop' into paradedb-search-backend 2026-03-25 15:09:32 -07:00
Mark Felder
2cc7b8356c Merge remote-tracking branch 'origin/develop' into paradedb-search-backend 2026-03-25 15:09:19 -07: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
Mark Felder
1701c343e1 Merge remote-tracking branch 'origin/develop' into gitlab-mr-iid-4381 2026-03-25 12:42:05 -07: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
Phantasm
08ba0d46d7 ActivityDraft: Add missing __MODULE__ matches and drop unneeded ones 2026-03-25 19:35:46 +00:00
Phantasm
01b139c633 Elixir 1.19: Add note to deprecation warnings test
The Logger does not get reconfigured when running in the test env,
the added and on_exit removed settings below should have no effect
besides triggering the deprecation warnings. To make breakage even less
possible, async is turned off for these tests.

TransferTask test does almost the same with the Application env.
2026-03-25 19:35:46 +00:00
Phantasm
a12f9777f8 Elixir 1.19: Bump Woodpecker test pipeline to 1.19 2026-03-25 19:35:46 +00:00
Phantasm
f44774d387 lint 2026-03-25 19:35:46 +00:00
Phantasm
1743a837dd Elixir 1.19: Set merged logger config correctly, remove log level test 2026-03-25 19:35:46 +00:00
Phantasm
0ac1b65ba9 Elixir 1.19: Fix improper deprecation warning wording 2026-03-25 19:35:46 +00:00
Phantasm
b75f8f7c9a Elixir 1.19: Added changelogs 2026-03-25 19:35:46 +00:00
Phantasm
ad7ab7741b Elixir 1.19: Update dependencies in docs
Elixir 1.15 support only OTP>=24
2026-03-25 19:35:46 +00:00
Phantasm
829a60ce93 Elixir 1.19 MRFTest: Replace matchable_regexes with regexes_match! func 2026-03-25 19:35:46 +00:00
Phantasm
02fc770e36 Elixir 1.19: Improve DeprecationWarningsTest Logger reconfiguration test 2026-03-25 19:35:46 +00:00
Phantasm
0f262bb2f3 Elixir 1.19: Replace ugly old logger config checks with config merging
Side note: Going up in log levels doesn't work properly.
 - debug->info works
 - info->debug does not work
2026-03-25 19:35:46 +00:00
Phantasm
9450fba83d Elixir 1.19: Disable colors for logging in test env
Color ANSI codes end up in the capture_log output and break existing
tests.
2026-03-25 19:35:46 +00:00
Phantasm
bd0c5aa748 Elixir 1.19: Move default configs to the new Logger configuration, again
Trying to do a two-release migration proved to be very hacky and ugly.
Instead this is preparation for moving defaults first and then merging
the old configuration left in admin's config file at runtime while still
emitting a warning. This hopefully will be a lot nicer and less
complicated.
2026-03-25 19:35:46 +00:00
Phantasm
aed0eb3333 Elixir 1.19: Warn on old config of :console Logger and conflicts 2026-03-25 19:35:46 +00:00
Phantasm
a5bd4e0d92 Partially revert "Elixir 1.19: Refactor Logger configuration to what Elixir 1.15+ expects"
This partially reverts commit fc35204cc1d718ba7815cc5e1b08746202b4f7dc.
2026-03-25 19:35:46 +00:00
Phantasm
6ffd25682d Elixir 1.19: Add deprecation warning for :logger, :console configs 2026-03-25 19:35:46 +00:00
Phantasm
a4137243b6 Elixir 1.19: Reword Logger backends deprecation warning 2026-03-25 19:35:46 +00:00
Phantasm
a4c929c527 Lint 2026-03-25 19:35:46 +00:00
Phantasm
c455e02255 Elixir 1.19: Add non-test Elixir files in test/ to mix.exs filter
This is a new filter introduced in 1.19.
2026-03-25 19:35:46 +00:00
Phantasm
2a23eacee8 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 19:35:46 +00:00
Phantasm
17a563aeb4 Elixir 1.19: Inject @compile autoload opt to MRF policies
Descriptions of MRF policies are gathered at compile-time and use
Pleroma.Docs.Generator.list_behaviour_implementations func, which
depends on modules implementing the queried behaviour to be loaded.
Otherwise descriptions for MRFs can be missing from the AdminAPI route.

Note: @behaviour cannot be injected in the __using__ macro. It causes a
loop in the compiler which leads to memory exhaustion. Hence why
@behaviour is still defined separately in the MRFs.
2026-03-25 19:35:46 +00:00
Phantasm
6744a86fe3 Elixir 1.19: Disable logging with colors in test environment
Can break some tests capture logs and then match on an expected text.
Since color codes end up being in the captured log.
2026-03-25 19:35:46 +00:00
Phantasm
39409a784d Elixir 1.19: Warn on adding :console to Logger backends
Logger.Backend.Console still exists, but it is unused in our
configuration. Instead logging to console is done from Logger's
"default_handler".
2026-03-25 19:35:46 +00:00
Phantasm
3ebf4b1056 Elixir 1.19: Move deprecated logger syntax check to DeprecationWarnings 2026-03-25 19:35:46 +00:00
Phantasm
eee54a9226 Elixir 1.19: Add tests for new Logger configuration 2026-03-25 19:35:46 +00:00
Phantasm
3790e6786c Only warn about bad clear_config usage if warned usage is met 2026-03-25 19:35:46 +00:00
Phantasm
0393f0f92d Elixir 1.19: Move Logger backends under a specific config key 2026-03-25 19:35:46 +00:00
Phantasm
926cad3a76 Elixir 1.19: Refactor Logger configuration to what Elixir 1.15+ expects
Elixir 1.19 started emitting warnings about our use of:
config :logger, :console and
config :logger, backends: []
which got split in 1.15 to :default_handler, :default_formatter, and
LoggerBackends.add(backend) respectively.

On Pleroma startup it is now necessary to add the additional logger
backends at runtime (done with configure_logger() in application.ex).
New config key :pleroma, :logger_backends: [] has replaced the older
backends array. The logger_backends package is now used to facilitate
usage of other logger backends besides the default one (:logger_std_h
from OTP).

Warnings were added to Pleroma startup to warn about the usage of
:logger, backends and additional logic was introduced to make addition
of ExSyslogger to admin's configuration less dumb.

Ref: https://hexdocs.pm/logger/1.15.0/Logger.html#module-backends-and-backwards-compatibility
2026-03-25 19:35:46 +00:00
Phantasm
dd2f0a32d8 lint 2026-03-25 19:35:46 +00:00
Phantasm
3c7cd8b043 Elixir 1.19: Fix Mastodon StatusControllerTest DateTime difference 2026-03-25 19:35:46 +00:00
Phantasm
cbacc0f146 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 19:35:46 +00:00
Phantasm
94ea3dffd1 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 19:35:46 +00:00
Phantasm
5777d8e8d3 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 19:35:46 +00:00
Phantasm
d1ce6f9833 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 19:35:46 +00:00
Phantasm
f3d9880f07 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 19:35:46 +00:00
Phantasm
349d858fda 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 19:35:46 +00:00
Phantasm
9040434886 Elixir 1.19: Fix typing violations in ActivityPubTest 2026-03-25 19:35:46 +00:00
Phantasm
6a926bf605 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 19:35:46 +00:00
Phantasm
7c79e4416f 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 19:35:46 +00:00
Phantasm
6f62cb0ed3 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 19:35:46 +00:00
Phantasm
0d3085937c 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 19:35:46 +00:00
Phantasm
0fc046a75e 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 19:35:46 +00:00
Phantasm
a72f9cff23 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 19:35:46 +00: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
nicole mikołajczyk
8626a337f8 Mastodon admin API v2 improvements
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 10:25:33 +00:00
mkljczk
87e6cd3dc0 Adapt tests for v2/admin/accounts from Mastodon
Signed-off-by: mkljczk <git@mkljczk.pl>
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 10:25:33 +00:00
nicole mikołajczyk
2c14121389 MastoAPI: /api/v2/admin/accounts
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 10:25:33 +00:00
nicole mikołajczyk
0e0931a3c2 MastoAPI: /api/v1/admin/accounts, /api/v1/admin/reports
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-03-06 10:25:33 +00: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
lain
865ddfdce0 Merge branch 'develop' into paradedb-search-backend 2026-03-05 15:02:32 +00:00
Lain Soykaf
cae3c9a980
Add ParadeDB fuzzy integration coverage
Extend the docker-backed ParadeDB integration test to verify typo-tolerant status search with fuzzy_distance enabled, covering prepared-parameter fuzzy casts against a real backend.
2026-03-05 17:39:34 +04:00
Lain Soykaf
4e65bbf134
Fix ParadeDB fuzzy query parameter casting
Cast the bound search parameter through text before  so Postgrex can handle the prepared statement type inference. Add matching test assertion updates.
2026-03-05 17:34:48 +04:00
Lain Soykaf
7efd296afb
Add fuzzy matching to ParadeDB search
Make ParadeDB search typo-tolerant via configurable fuzzy distance and document the new setting. Add coverage for fuzzy query SQL and id parsing, and keep integration test stability under connection load.
2026-03-05 17:12:03 +04:00
Lain Soykaf
74da3e01f7
Harden ParadeDB search result safety
Re-apply public Create visibility checks when resolving backend hits, improve UUID/FlakeId id parsing, and add tests for poisoned hits and UUID-string ids. Also tighten the docker integration test with localhost-only binding and config restoration.
2026-03-05 16:36:22 +04:00
Lain Soykaf
dee7ac0415
Fix ParadeDB key field handling
Use uuid ids in the ParadeDB documents table to satisfy BM25 key_field requirements and adjust indexing/search to dump FlakeIds consistently. Also fix the ParadeDB backend unit test to avoid invalid pinning on struct fields.
2026-03-04 13:04:41 +04: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
Lain Soykaf
efb75488d9
Tighten ParadeDB integration test
Remove warnings that break --warnings-as-errors, add TCP readiness check, and start the ParadeDB Repo with a small pool to reduce connection churn while booting the dockerized backend.
2026-03-03 12:49:23 +04:00
Lain Soykaf
911d84411d
Add ParadeDB docker integration test
Add a skipped-by-default integration test that boots a ParadeDB container, creates the BM25 index, indexes posts, and verifies search results against the real backend.
2026-03-03 12:36:01 +04:00
Lain Soykaf
3a87250e15
Add ParadeDB external search backend
Introduce an optional search backend that indexes public/unlisted Notes into a separate ParadeDB (pg_search) Postgres and queries it for status search results.
2026-03-03 12:17:34 +04: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
e4f2f0994c Use encode16(case: :lower)
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-27 23:16:36 +01:00
nicole mikołajczyk
8eca6ca87c webhook fixes
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-02-27 23:16:32 +01: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
nicole mikołajczyk
9901e6367e Fix event type field name
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2026-01-25 19:47:21 +01: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
Dryusdan
ce188baa92
Add more Postgresql-rum version 2026-01-22 08:55:14 +01:00
Dryusdan
177edbbb2c
Add more RUM versions 2026-01-22 08:50:50 +01:00
Dryusdan
145b38b314
Reset deleted double space 2026-01-21 20:18:13 +01:00
Dryusdan
10ab71d157
Improve Debian 13 OTP dependencies and remove Debian 12 in source installation
Cause of Elexir ~> 1.15 (mandatory in Pleroma 2.10) that don't support in Debian 12,
I fix doc and replace Debian 12 by Debian 13
And replace a dependencies in OTP release + add a new dependencies for Debian 13.
2026-01-21 18:03:40 +01:00
Haelwenn (lanodan) Monnier
6f86883cca
Web: remove legacy :set_put_layout plug
Phoenix 1.8 requires a View module with put_layout so can't set it that early.

It was introduced in 2019 with
commit 1097ce6d9f
but nothing seems to provide app.html (anymore?) and it would likely
better be set by something like OAuthController / OAuthView.
2026-01-18 10:40:10 +01:00
Haelwenn (lanodan) Monnier
be327ca982
Switch Phoenix back to upstream
See <https://github.com/phoenixframework/phoenix/pull/6094>
for `:sec_websocket_protocol` -> `:sec_websocket_headers`
2026-01-18 10:40:10 +01:00
Lain Soykaf
a4fb651fac ConfigController: Don't allow whitelist modification. 2026-01-17 13:30:07 +04:00
Lain Soykaf
117b0bd79e Config: Don't crash on falsy whitelist config 2026-01-17 13:03:02 +04:00
Lain Soykaf
49f9ab3034 Cheatsheet: Fix double slash 2026-01-17 13:02:18 +04:00
Lain Soykaf
0b871ff1f2 ConfigController: Don't allow updating the whitelist 2026-01-17 12:32:10 +04:00
Lain Soykaf
77a1d79f92 ConfigTest: Don't crash when whitelist is unset / disabled 2026-01-17 12:31:35 +04:00
Lain Soykaf
3b3e369f83 AP ingest: fill missing attributedTo from activity 2026-01-17 02:46:59 +04:00
Lain Soykaf
9912a9f603 AP ingest: accept Emoji icon variants 2026-01-17 01:33:21 +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
e9e3d255e5 AP ingest: infer tag type when missing 2026-01-17 00:12:48 +04:00
Lain Soykaf
e781a7ef9b AP ingest: harden attachment parsing 2026-01-17 00:08:19 +04:00
Lain Soykaf
bff67650e1 AP ingest: normalize inReplyTo shapes 2026-01-16 23:57:01 +04:00
Lain Soykaf
6701f61990 AP ingest: normalize quoteUrl fields 2026-01-16 23:27:06 +04:00
Lain Soykaf
39817fa547 AP ingest: normalize object url list 2026-01-16 23:25:07 +04:00
Lain Soykaf
0feb23a308 AP ingest: accept object type as list 2026-01-16 23:16:51 +04:00
Lain Soykaf
b9c281a0c3 Add changelog 2026-01-06 16:42:29 +04:00
Lain Soykaf
bd61916270 ConfigDB, RateLimiter, RateLimit: Use new type to parse and cast rate limits. 2026-01-06 16:34:17 +04:00
Lain Soykaf
47f4bde0ea ConfigDBTest: Add failing test for invalid rate limiter values. 2026-01-06 16:06:51 +04:00
Lain Soykaf
958a4581d6 RateLimiter: Ensure that the rate limiter doesn't crash on bad values 2026-01-06 15:54:06 +04:00
Lain Soykaf
9ede9b92d3 RateLimiterTest: Add failing test for invalid values. 2026-01-06 15:32:01 +04:00
Lain Soykaf
3903f12c78 Add changelog 2026-01-01 10:04:55 +04:00
Lain Soykaf
ee17d6413d Merge branch 'develop' of git.pleroma.social:pleroma/pleroma into better-user-search 2026-01-01 10:03:14 +04:00
Lain Soykaf
71f5a493f3 Search: Better sorting for user searches. 2026-01-01 10:02:37 +04:00
marcin mikołajczak
6d46aefb7f Add internal (not managable from API) webhooks
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-09-19 17:09:09 +02:00
nicole mikołajczyk
9d7bdab7a2 Mastodon-compatible webhooks
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-09-19 17:07:23 +02:00
Lain Soykaf
c3c14f5860 OAuth App: Fix test, optimize code. 2025-01-21 16:13:27 +04:00
Haelwenn (lanodan) Monnier
9bbeeeb428
*/init.d/pleroma: run migrations before starting 2024-12-19 21:59:33 +01:00
Haelwenn (lanodan) Monnier
a1247220f3
rel/…/init.d/pleroma: set respawn_max & respawn_period 2024-12-19 21:50:31 +01:00
Haelwenn (lanodan) Monnier
9ce615d3d5
rel/…/init.d/pleroma: tab indent 2024-12-19 21:50:31 +01: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
Haelwenn (lanodan) Monnier
656dce96dc
Status: Fill all MastoAPI required values in preview cards 2024-05-29 06:43:14 +02:00
Haelwenn (lanodan) Monnier
c7119d17d8
StatusControllerTest: Update preview cards tests to current API 2024-05-29 06:36:22 +02:00
Haelwenn (lanodan) Monnier
d860d50597
StatusController: Restore preview cards tests
This is a partial revert of 19002fd6c1

Subsequent commit updating it to cards embedded in statuses required to pass tests.
2024-05-29 06:34:59 +02:00
Haelwenn (lanodan) Monnier
c44cf7d74f
StatusOperation: Remove obsolete card_operation 2024-05-29 06:33:58 +02:00
967 changed files with 27495 additions and 4048 deletions

View file

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

View file

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

3
.gitignore vendored
View file

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

View file

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

View file

@ -0,0 +1,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: &docker_image git.pleroma.social/phnt/wpc-docker-tagger:0.9.1
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, armv7]
tags:
- latest
- develop
- "${CI_COMMIT_SHA:0:8}"
username:
from_secret: pleroma-ci-user
password:
from_secret: pleroma-ci-password
docker-stable-combine:
image: *docker_image
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: *docker_image
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}"

150
.woodpecker/docker.yaml Normal file
View file

@ -0,0 +1,150 @@
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
- linux/arm
# 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
armv7_docker_variables: &armv7_docker_variables
<<: *docker_variables
build_args:
- ELIXIR_IMG=docker.io/arm32v7/elixir
- ELIXIR_TAG=1.17.3-otp-27-alpine
- ALPINE_IMG=docker.io/arm32v7/alpine
- ALPINE_VER=3.20
extra_opts: --custom-platform=linux/arm/v7
kaniko_image: &kaniko_image docker.io/woodpeckerci/plugin-kaniko:2.3.2
steps:
docker-develop-amd64:
image: *kaniko_image
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: *kaniko_image
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-develop-armv7:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
settings:
<<: *armv7_docker_variables
tags:
- latest-armv7
- develop-armv7
- ${CI_COMMIT_SHA:0:8}-armv7
docker-stable-amd64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
settings:
<<: *docker_variables
tags: &amd64_tags
- latest-amd64
- stable-amd64
- ${CI_COMMIT_SHA:0:8}-amd64
# FE workflow runs only on linux/amd64
docker-stable-e2e-amd64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
settings:
<<: *docker_variables
dockerfile: Dockerfile-e2e
tags:
- stable-e2e
docker-stable-tag-amd64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
settings:
<<: *docker_variables
tags:
- <<: *amd64_tags
- ${CI_COMMIT_TAG}-amd64
docker-stable-arm64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
settings:
<<: *docker_variables
tags: &arm64_tags
- latest-arm64
- stable-arm64
- ${CI_COMMIT_SHA:0:8}-arm64
docker-stable-tag-arm64:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
settings:
<<: *docker_variables
tags:
- <<: *arm64_tags
- ${CI_COMMIT_TAG}-arm64
docker-stable-armv7:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
settings:
<<: *armv7_docker_variables
tags: &armv7_tags
- latest-armv7
- stable-armv7
- ${CI_COMMIT_SHA:0:8}-armv7
docker-stable-tag-armv7:
image: *kaniko_image
when:
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
settings:
<<: *armv7_docker_variables
tags:
- <<: *armv7_tags
- ${CI_COMMIT_TAG}-armv7

45
.woodpecker/docs.yaml Normal file
View file

@ -0,0 +1,45 @@
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "docs/**" ]
- event: tag
- event: manual
evaluate: 'CI_COMMIT_BRANCH == "stable" || CI_COMMIT_BRANCH == CI_REPO_DEFAULT_BRANCH'
labels:
platform: linux/amd64
skip_clone: true
variables:
trigger_settings: &trigger_settings
deploy: 'docs'
last-successful: true
token:
from_secret: pleroma-ci-woodpecker-token
repositories:
- pleroma/docs@master
docker_image: &docker_image docker.io/woodpeckerci/plugin-trigger:1.1.1
steps:
trigger-deploy-stable:
when:
- event: tag
- event: manual
branch: stable
image: *docker_image
settings:
<<: *trigger_settings
params:
- BRANCH=stable
trigger-deploy-develop:
when:
- event: push
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
image: *docker_image
settings:
<<: *trigger_settings
params:
- BRANCH=develop

78
.woodpecker/lint.yaml Normal file
View file

@ -0,0 +1,78 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
labels:
platform: linux/amd64
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
mix-format:
image: &elixir-image
docker.io/elixir:1.17-otp-27-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:1.17.3-erlang-27.3.4.17-alpine-3.22.5
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3-alpine
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir:1.17.3-erlang-27.3.4.17-alpine-3.22.5
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.1
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:1.17.3-erlang-27.3.4.17-ubuntu-noble-20260810
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir:1.17.3-erlang-27.3.4.17-ubuntu-noble-20260810
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.1
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,66 @@
when:
- event: pull_request
path:
- ".woodpecker/**"
- "lib/pleroma/search/**"
- "test/pleroma/search/**"
- "mix.exs"
- "mix.lock"
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path:
- ".woodpecker/**"
- "lib/pleroma/search/**"
- "test/pleroma/search/**"
- "mix.exs"
- "mix.lock"
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:
paradedb-integration:
image: docker.io/elixir:1.19-otp-28-alpine
entrypoint: *script_file_entrypoint
environment:
MIX_ENV: test
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
DB_HOST: postgres
DB_PORT: 5432
PARADEDB_TEST_URL: postgres://postgres:postgres@paradedb:5432/paradedb
commands:
- apk add --no-cache build-base cmake exiftool ffmpeg vips-dev file-dev git openssl postgresql-client
- mix local.hex --force
- mix local.rebar --force
- mix deps.get
- timeout 60 sh -c 'until pg_isready -h postgres -U postgres -d pleroma_test; do sleep 1; done' || { echo "PostgreSQL did not become ready" >&2; exit 1; }
- timeout 60 sh -c 'until pg_isready -h paradedb -U postgres -d paradedb; do sleep 1; done' || { echo "ParadeDB did not become ready" >&2; exit 1; }
- mix ecto.create
- mix ecto.migrate
- mix test --include integration test/pleroma/search/parade_db_integration_test.exs
services:
postgres:
image: docker.io/postgres:13-alpine
failure: ignore
environment:
POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
paradedb:
image: docker.io/paradedb/paradedb:0.24.3-pg15@sha256:997d62f6c2605becb7c45878af3f8c8c698f355006b675b3ce212d4eb2d14f2e
failure: ignore
environment:
POSTGRES_DB: paradedb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres

103
.woodpecker/spec-build.yaml Normal file
View file

@ -0,0 +1,103 @@
when:
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path:
- ".woodpecker/spec-build.yaml"
- "mix.exs"
- "mix.lock"
- "lib/pleroma/web.ex"
- "lib/pleroma/web/**/*.ex"
- event: tag
- event: manual
evaluate: 'CI_COMMIT_BRANCH == "stable" || CI_COMMIT_BRANCH == CI_REPO_DEFAULT_BRANCH'
labels:
platform: linux/amd64
concurrency: 1
variables:
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.1
artifacts_uploader_settings: &artifacts_uploader_settings
user:
from_secret: pleroma-ci-user
password:
from_secret: pleroma-ci-password
owner: 'pleroma'
package_name: api-docs-spec
file_source: ./spec.json
file_name: spec.json
update: 'true'
trigger_image: &trigger_image docker.io/woodpeckerci/plugin-trigger:1.1.1
trigger_settings: &trigger_settings
deploy: 'api-docs'
last-successful: true
token:
from_secret: pleroma-ci-woodpecker-token
repositories:
- pleroma/api-docs@master
steps:
build-spec:
image: docker.io/hexpm/elixir:1.17.3-erlang-27.3.4.17-alpine-3.22.5
environment:
MIX_ENV: prod
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
commands:
- apk update --no-cache
- apk add git build-base cmake file-dev openssl vips-dev zip
- mix local.hex --force
- mix local.rebar --force
- mix deps.get --only prod
- mix pleroma.openapi_spec spec.json
- |
printf 'BRANCH=stable\nSPEC_VERSION=%s\n' "$${CI_COMMIT_SHA}" > trigger-deploy-stable.params
printf 'BRANCH=develop\nSPEC_VERSION=%s\n' "$${CI_COMMIT_SHA}" > trigger-deploy-develop.params
upload-spec:
image: *artifacts_uploader_image
settings:
<<: *artifacts_uploader_settings
package_version: ${CI_COMMIT_SHA}
upload-spec-stable:
when:
- event: tag
- event: manual
branch: stable
image: *artifacts_uploader_image
settings:
<<: *artifacts_uploader_settings
package_version: stable
upload-spec-develop:
when:
- event: push
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
image: *artifacts_uploader_image
settings:
<<: *artifacts_uploader_settings
package_version: develop
trigger-deploy-stable:
when:
- event: tag
- event: manual
branch: stable
image: *trigger_image
settings:
<<: *trigger_settings
params:
- trigger-deploy-stable.params
trigger-deploy-develop:
when:
- event: push
- event: manual
branch: ${CI_REPO_DEFAULT_BRANCH}
image: *trigger_image
settings:
<<: *trigger_settings
params:
- trigger-deploy-develop.params

View file

@ -0,0 +1,47 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
depends_on:
- lint
labels:
platform: linux/amd64
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
unit-testing-elixir-1.17:
image: elixir:1.17-otp-27-alpine
entrypoint: *script_file_entrypoint
environment:
MIX_ENV: test
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
DB_HOST: postgres
DB_PORT: 5432
commands:
- apk add --no-cache build-base cmake exiftool ffmpeg vips-dev 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
failure: ignore
environment:
POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres

View file

@ -0,0 +1,47 @@
when:
- event: pull_request
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
- event: push
branch: ${CI_REPO_DEFAULT_BRANCH}
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
depends_on:
- lint
labels:
platform: linux/amd64
variables:
script_file_entrypoint: &script_file_entrypoint
- /bin/sh
- -c
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
steps:
unit-testing-elixir-1.19:
image: elixir:1.19-otp-28-alpine
entrypoint: *script_file_entrypoint
environment:
MIX_ENV: test
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
DB_HOST: postgres
DB_PORT: 5432
commands:
- apk add --no-cache build-base cmake exiftool ffmpeg vips-dev 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
failure: ignore
environment:
POSTGRES_DB: pleroma_test
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres

View file

@ -4,6 +4,169 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2.11.0
### Security
- Update Bandit to address HTTP request handling vulnerabilities.
- Update bandit to version 1.12.4 to fix a DoS vulnerability
- Update bandit to version 1.12.5 to fix an HTTP/2 DoS vulnerability
- Update bandit to version 1.13.1 resolving a stuck process and unvalidated HTTP/1 headers vulnerabilities
- Updated CAStore to version 1.0.22 with updated Mozilla certificate bundle after eMudhra Intermediate CA Compromise
- Update certifi to version 2.18.0, which removes the distrusted emSign roots
- Update plug_crypto to version 2.2.1, so a malformed shout socket token no longer writes the secret key base to the error log
- Update Cowboy to address multipart header parsing denial-of-service vulnerabilities.
- Update cowboy to version 2.19.0 which fixes a memory exhaustion DoS
- Update Cowlib to address HTTP parsing resource exhaustion and injection vulnerabilities.
- Update cowlib to version 2.20.0 which fixes HTTP splitting and header smuggling/injection vulnerabilities
- Update gun to version 2.7.0 and cowlib to version 2.21.0 resolving a header smuggling vulnerability
- Update Decimal and compatible dependencies to address a rounding vulnerability.
- Update Decimal to version 3.2.0 resolving an unbounded allocation vulnerability
- Replace retired Earmark rendering with the maintained EarmarkParser and an attribute-escaping renderer.
- Update gun to version 2.6.0 which fixes an HTTP request/response smuggling vulnerability
- Update Hackney and its HTTPoison integration to address SSRF, timeout, and injection vulnerabilities.
- Hackney, Finch, Mua and the Swoosh HTTP mail adapters now trust only the CAStore certificate bundle, which rejects the compromised eMudhra intermediate CAs
- Ensure Host header is present and matches instance URI
- Update mint to version 1.10.0 which fixes multiple DoS vulnerabilities
- Update mint to version 1.11.0 resolving multiple vulnerabilities
- Update Phoenix LiveView to version 1.2.9 to fix an open redirect vulnerability
- Update Phoenix to version 1.8.11 resolving a process-exhaustion DoS vulnerability
- Update Phoenix to address long-poll NDJSON request denial-of-service vulnerabilities.
- Update Plug to address multipart header parsing denial-of-service vulnerabilities.
- Update Plug.Cowboy to address HTTP/2 scheme atom exhaustion denial-of-service vulnerabilities.
- Update Postgrex to address notification channel-name SQL injection vulnerabilities.
- Update postgrex to version 0.22.4 to fix an SQL injection vulnerability
- Remove unused QUIC lockfile entry with vulnerable TLS verification behavior.
- Update swoosh to version 1.28.0 which fixes a url injection vulnerability for the Microsoft Graph adapter
- Scrubbers: remove links and images whose scheme is hidden with tabs, newlines or leading control characters (for example java<TAB>script:), which browsers still run as javascript:. Custom scrubbers should use Pleroma.HTML.URIAttributes.allow_tag_with_uri_attributes instead of the FastSanitize macro
### Changed
- Update bundled Pleroma-FE to 2.11.4
- Allow disabling Chats feature
- Pleroma now requires Elixir 1.17 or newer and Erlang/OTP 27 or newer
- Update Debian and Ubuntu installation requirements and OTP dependencies
- Update Jason to support Decimal 3 dependency resolution.
- Changed how Logger is configured due to changes in Elixir and added deprecation warnings for the old configuration format
- Update majic to 1.2.0.
- Avoid a duplicate user query during OAuth authentication.
- Use the Hex package for oban_plugins_lazarus.
- Docs OpenBSD: Increased recommended `datasize` from 1536M to 4096M to avoid Pleroma crashes
- OpenRC: Automatically run migrations before starting pleroma
- Optimize orphaned OAuth apps cleanup by deleting records in batches instead of one by one.
- Switch patched Phoenix 1.7.14 back to upstream with Phoenix 1.8.0+
- Add optional migrations for rollback from Rebased
- Use upstream Hex releases for the `remote_ip` dependency and expose client IP ranges for remote IP resolution.
- Improve user search / autocompletion ordering.
- Gun is now the default HTTP client instead of Hackney
### Added
- Allow creating accounts without a password in the AdminAPI
- Implemented ConfigDB configuration blacklist and accompanying Mix task
- Accept more real-world ActivityPub object shapes collected by fedidev.fun
- Add Mastodon-compatible grouped notifications API endpoints under `/api/v2/notifications` for newly-created notifications
- Docs: Added documentation for installing newer version of Elixir/Erlang than what distro ships
- Allow attaching emojis to user lists
- Implement basics of Mastodon admin API
- Add backend support for Misskey Markdown (MFM) posts
- Add mix_audit for dependency vulnerability checks.
- Oban: Enabled the Reindexer plugin to reduce oban_jobs index DB bloat
- Add ParadeDB (pg_search) as an optional external search backend, indexing status content, content warnings, and attachment descriptions.
- Add support for Mastodon-compatible webhooks
### Fixed
- Fixed `/api/v1/pleroma/accounts/:id/favourites` ordering and pagination to use the favourite date.
- Reject unsupported or ambiguous inbox signatures instead of queueing a doomed failed-signature retry, allowing clients to fall back to legacy HTTP Signatures.
- Switch native captcha to the published pleroma_captcha Hex package.
- Updated the captcha package to fix compilation failure on BSD-based systems
- Dockerfile: update base images to fix problems with SSL handshakes
- Docs: Fixed OTP install links in documentation, broke up wall of install commands into smaller chunks, fixed sudo commands execution
- Fix notification and timeline queries for users with domain blocks choosing a plan that scans every active user per row; the followed-accounts exception is now an `ARRAY(SELECT ...)` InitPlan that the planner costs once.
- Fixed Elixir 1.19 warnings
- Support fetching featured collection items from the first collection page
- Fix preferred frontend not working due to InstanceStatic plug prematurely returning HTTP 404
- Fixed Gun HTTP/1 concurrency, forwarding and authenticated proxy handling, tunnel requests, and streamed connection cleanup. Updated Gun and Cowlib for HTTP client security fixes.
- Stop leaking a Hackney connection process, and the response bytes it read, for every unpooled request that ends without a reusable socket
- Fix hashtag timeline query for additional tag handling
- Fix compatibility with timestamped HTTP Signatures used by GoToSocial
- Handle reports with just actor ap id as the object
- Fix Swoosh HTTP mail adapters failing with `cacerts: :undefined` on OTP 26+
- Echo Mastodon-style `Sec-WebSocket-Protocol` tokens in streaming WebSocket handshakes.
- Enforce the max_body HTTP option again, which Hackney 4 silently stopped honouring because it always returns a fully read body
- Abort incomplete media proxy responses instead of caching partial bodies.
- Updated Pleroma-FE build URL after Forgejo migration
- Fix votersCount inflation when same voter picks multiple options
- Status: Fill all MastoAPI required values in preview cards
- Avoid creating atoms from serialized PublisherWorker parameters.
- Reject incoming reports when both the reporter and reported account are remote
- Fixed account status and media views exposing activities restricted from unauthenticated users.
- Stop leaking a Hackney connection process for every media proxy response that was streamed to the end
- Fix RSS feeds to be W3C compliant and render better in RSS readers
- Keep followed users visible in streams when their domain is blocked.
- Fix intermittent failure to compile syslog package
- Fix inconsistent User cache keys and invalidate numeric ID aliases.
- Include arm32v7 in multi-arch Docker images
- RichMedia: Fix backfill causing old posts to show up on timelines by disabling it in MastoAPI StatusView
- Fix Admin-FE installation by downloading builds from the package registry instead of GitLab job artifacts.
- Hackney: use HTTP/1.1 only, so closing one media proxy response no longer breaks other downloads from the same host
- Hackney: use the configured proxy when its host is an IP address; Hackney 4 ignored such proxies and connected directly
## 2.10.2
### Security
- ActivityPub: Fixed failed-signature inbox retry handling and signer identity checks to prevent spoofed remote activities from being processed
## 2.10.1
### Changed
- Move avatar_description and header_description fields to the account object
- Update Bandit to 1.10.4
- No-op code correctness improvements detected by Elixir 1.19 compiler
- Downgrade Hackney to 1.20.1
- Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney
- Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation
- Paginate follow requests
- Moved Phoenix LiveDashboard to /pleroma/live_dashboard
- Add mute/block expiry to the relationship object
- Filter indexable activities before inserting indexing jobs into the queue.
### Added
- Allow assigning users to reports
- Allow fine-grained announce visibilities
- Add immutable tag on cache-control header for several endpoints that's serving the same exact things.
- Add reasonable defaults for :database_config_whitelist
- Support lists `exclusive` param
- Add v1/instance/domain_blocks endpoint
- Add /api/v2/instance profile fields limits info used by Mastodon
- Added Oban Web dashboard located at /pleroma/oban
- Add instructions on how to run a release in docker, to make it easier to run on older distros.
### Fixed
- Fix the daily email digest job which was not executing
- Encode custom emoji URLs in EmojiReact activity tags.
- Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled
- Fix fetching Hubzilla Actors with alsoKnownAs as string
- Fix /phoenix/live_dashboard redirect not working when user added a path segment
- Fix 404 error codes for missing static files
- Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input.
- Correct old migrations for expiring activities and user access tokens.
- Federate `votersCount` correctly
- DB prune: Check if user follows hashtag with no objects before deletion
- Stop the rate limiter from crashing when run with wrong settings.
- Restore embeds route
- ReverseProxy: Recursively follow redirects until redirect_limit is reached
- Fix compilation with vips-8.18.0 with bumping to vix 0.36.0
### Removed
- Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS)
## 2.10 ## 2.10
### Security ### Security

View file

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

61
Dockerfile-e2e Normal file
View file

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

View file

@ -1,4 +1,4 @@
<img src="https://git.pleroma.social/pleroma/pleroma/uploads/8cec84f5a084d887339f57deeb8a293e/pleroma-banner-vector-nopad-notext.svg" width="300px" /> <img src="https://git.pleroma.social/pleroma/pleroma/raw/branch/develop/logo-with-name.png" width="300px" />
## About ## About

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 @@
Delete legacy Postgres sequences for activities, chats, and users which were converted to Flake IDs

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

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

@ -64,9 +64,9 @@ config :pleroma, Pleroma.Upload,
link_name: false, link_name: false,
proxy_remote: false, proxy_remote: false,
filename_display_max_length: 30, filename_display_max_length: 30,
default_description: :filename, default_description: nil,
base_url: nil, base_url: nil,
allowed_mime_types: ["image", "audio", "video", "application"] allowed_mime_types: ["image", "audio", "video"]
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads" config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
@ -133,13 +133,17 @@ config :pleroma, Pleroma.Web.Endpoint,
] ]
# Configures Elixir's Logger # Configures Elixir's Logger
config :logger, backends: [:console] # Primary config
config :logger, level: :debug
config :logger, :console, # Console config
level: :debug, config :logger, :default_handler, level: :debug
config :logger, :default_formatter,
format: "\n$time $metadata[$level] $message\n", format: "\n$time $metadata[$level] $message\n",
metadata: [:actor, :path, :type, :user] metadata: [:actor, :path, :type, :user]
# Syslog config
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :debug, level: :debug,
ident: "pleroma", ident: "pleroma",
@ -157,7 +161,11 @@ config :mime, :types, %{
"image/apng" => ["apng"] "image/apng" => ["apng"]
} }
config :tesla, adapter: Tesla.Adapter.Hackney config :tesla, adapter: Tesla.Adapter.Gun
# Hackney 4 shares one HTTP/2 connection per host, and closing one response
# (as the media proxy does) stops it for every other stream on it.
config :hackney, default_protocols: [:http1]
# Configures http settings, upstream proxy etc. # Configures http settings, upstream proxy etc.
config :pleroma, :http, config :pleroma, :http,
@ -167,18 +175,18 @@ config :pleroma, :http,
adapter: [] adapter: []
config :pleroma, :instance, config :pleroma, :instance,
name: "Shigusegubu", name: "Pleroma",
email: "pleroma@hjkos.com", email: "example@example.com",
notify_email: "pleroma@hjkos.com", notify_email: "noreply@example.com",
description: "SigSegV, a pleroma instance", description: "Pleroma: An efficient and flexible fediverse server",
short_description: "HJ's semi-personal instance", short_description: "",
background_image: "/images/city.jpg", background_image: "/images/city.jpg",
instance_thumbnail: "/instance/thumbnail.jpeg", instance_thumbnail: "/instance/thumbnail.jpeg",
favicon: "/favicon.png", favicon: "/favicon.png",
limit: 5_000, limit: 5_000,
description_limit: 5_000, description_limit: 5_000,
remote_limit: 100_000, remote_limit: 100_000,
upload_limit: 200_000_000, upload_limit: 16_000_000,
avatar_upload_limit: 2_000_000, avatar_upload_limit: 2_000_000,
background_upload_limit: 4_000_000, background_upload_limit: 4_000_000,
banner_upload_limit: 4_000_000, banner_upload_limit: 4_000_000,
@ -196,17 +204,20 @@ config :pleroma, :instance,
federation_incoming_replies_max_depth: 100, federation_incoming_replies_max_depth: 100,
allow_relay: true, allow_relay: true,
public: true, public: true,
quarantined_instances: [{ "pleroma.rareome.ga", "leaks private posts or sumshit i dont rember" }], quarantined_instances: [],
rejected_instances: [], rejected_instances: [],
static_dir: "instance/static/", static_dir: "instance/static/",
allowed_post_formats: [ allowed_post_formats: [
"text/plain", "text/plain",
"text/bbcode" "text/html",
"text/markdown",
"text/bbcode",
"text/x.misskeymarkdown"
], ],
autofollowed_nicknames: [], autofollowed_nicknames: [],
autofollowing_nicknames: [], autofollowing_nicknames: [],
max_pinned_statuses: 1, max_pinned_statuses: 1,
attachment_links: true, attachment_links: false,
max_report_comment_size: 1000, max_report_comment_size: 1000,
report_strip_status: true, report_strip_status: true,
safe_dm_mentions: false, safe_dm_mentions: false,
@ -299,8 +310,8 @@ config :pleroma, :markup,
config :pleroma, :frontend_configurations, config :pleroma, :frontend_configurations,
pleroma_fe: %{ pleroma_fe: %{
alwaysShowSubjectInput: false, alwaysShowSubjectInput: true,
background: "/static/sigsegv_s.png", background: "/images/city.jpg",
collapseMessageWithSubject: false, collapseMessageWithSubject: false,
disableChat: false, disableChat: false,
greentext: false, greentext: false,
@ -314,18 +325,18 @@ config :pleroma, :frontend_configurations,
logo: "/static/logo.svg", logo: "/static/logo.svg",
logoMargin: ".1em", logoMargin: ".1em",
logoMask: true, logoMask: true,
minimalScopesMode: true, minimalScopesMode: false,
noAttachmentLinks: false, noAttachmentLinks: false,
nsfwCensorImage: "", nsfwCensorImage: "",
postContentType: "text/plain", postContentType: "text/plain",
redirectRootLogin: "/main/friends", redirectRootLogin: "/main/friends",
redirectRootNoLogin: "/main/all", redirectRootNoLogin: "/main/all",
scopeCopy: false, scopeCopy: true,
sidebarRight: false, sidebarRight: false,
showFeaturesPanel: true, showFeaturesPanel: true,
showInstanceSpecificPanel: true, showInstanceSpecificPanel: false,
subjectLineBehavior: "noop", subjectLineBehavior: "email",
theme: "sigsegv2", theme: "pleroma-dark",
webPushNotifications: false webPushNotifications: false
} }
@ -384,23 +395,8 @@ config :pleroma, :mrf_hellthread,
config :pleroma, :mrf_simple, config :pleroma, :mrf_simple,
media_removal: [], media_removal: [],
media_nsfw: [ media_nsfw: [],
{ "preteengirls.biz", "pron or even cp" }, federated_timeline_removal: [],
{ "melalandia.tk", "i don't rember" },
{ "pl.smuglo.li", "instance is dead but i still remember, rip smuglo" },
{ "baraag.net", "pron" },
{ "humblr.social", "3dpd pron" },
{ "pawoo.net", "tasteful pron, mostly." },
{ "sinblr.com", "3dpd pron" }
],
federated_timeline_removal: [
{ "preteengirls.biz", "pron or even cp" },
{ "melalandia.tk", "i don't rember" },
{ "baraag.net", "pron" },
{ "humblr.social", "3dpd pron" },
{ "pawoo.net", "tasteful pron, mostly." },
{ "sinblr.com", "3dpd pron" }
],
report_removal: [], report_removal: [],
reject: [], reject: [],
followers_only: [], followers_only: [],
@ -511,7 +507,7 @@ config :pleroma, :media_preview_proxy,
min_content_length: 100 * 1024 min_content_length: 100 * 1024
config :pleroma, :shout, config :pleroma, :shout,
enabled: false, enabled: true,
limit: 5_000 limit: 5_000
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason config :phoenix, :format_encoders, json: Jason, "activity+json": Jason
@ -615,7 +611,11 @@ config :pleroma, Oban,
search_indexing: [limit: 10, paused: true], search_indexing: [limit: 10, paused: true],
slow: 5 slow: 5
], ],
plugins: [Oban.Plugins.Lazarus, {Oban.Plugins.Pruner, max_age: 900}], plugins: [
Oban.Plugins.Lazarus,
{Oban.Plugins.Pruner, max_age: 900},
{Oban.Plugins.Reindexer, schedule: "@weekly"}
],
crontab: [ crontab: [
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker}, {"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}, {"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker},
@ -750,6 +750,7 @@ config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true, min_lifeti
config :pleroma, Pleroma.Web.Plugs.RemoteIp, config :pleroma, Pleroma.Web.Plugs.RemoteIp,
enabled: true, enabled: true,
headers: ["x-forwarded-for"], headers: ["x-forwarded-for"],
clients: [],
proxies: [], proxies: [],
reserved: [ reserved: [
"127.0.0.0/8", "127.0.0.0/8",
@ -788,7 +789,7 @@ config :pleroma, :frontends,
"name" => "pleroma-fe", "name" => "pleroma-fe",
"git" => "https://git.pleroma.social/pleroma/pleroma-fe", "git" => "https://git.pleroma.social/pleroma/pleroma-fe",
"build_url" => "build_url" =>
"https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build", "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-fe-builds/${ref}/latest.zip",
"ref" => "develop" "ref" => "develop"
}, },
"fedi-fe" => %{ "fedi-fe" => %{
@ -805,7 +806,7 @@ config :pleroma, :frontends,
"name" => "admin-fe", "name" => "admin-fe",
"git" => "https://git.pleroma.social/pleroma/admin-fe", "git" => "https://git.pleroma.social/pleroma/admin-fe",
"build_url" => "build_url" =>
"https://git.pleroma.social/pleroma/admin-fe/-/jobs/artifacts/${ref}/download?job=build", "https://git.pleroma.social/api/packages/pleroma/generic/admin-fe-builds/${ref}/latest.zip",
"ref" => "develop" "ref" => "develop"
}, },
"soapbox" => %{ "soapbox" => %{
@ -839,7 +840,7 @@ config :pleroma, :web_cache_ttl,
config :pleroma, :modules, runtime_dir: "instance/modules" config :pleroma, :modules, runtime_dir: "instance/modules"
config :pleroma, configurable_from_database: true config :pleroma, configurable_from_database: false
config :pleroma, Pleroma.Repo, config :pleroma, Pleroma.Repo,
parameters: [gin_fuzzy_search_limit: "500", jit: "off"], parameters: [gin_fuzzy_search_limit: "500", jit: "off"],
@ -912,13 +913,9 @@ config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
config :pleroma, :mrf, config :pleroma, :mrf,
policies: [ policies: [
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
Pleroma.Web.ActivityPub.MRF.HellthreadPolicy,
Pleroma.Web.ActivityPub.MRF.AntiFollowbotPolicy,
Pleroma.Web.ActivityPub.MRF.TagPolicy,
Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy, Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy,
Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy, Pleroma.Web.ActivityPub.MRF.TagPolicy,
Pleroma.Web.ActivityPub.MRF.AntiLinkSpamPolicy Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy
], ],
transparency: true, transparency: true,
transparency_exclusions: [] transparency_exclusions: []
@ -943,7 +940,8 @@ config :pleroma, Pleroma.User.Backup,
timeout: :timer.minutes(30) timeout: :timer.minutes(30)
config :pleroma, ConcurrentLimiter, [ config :pleroma, ConcurrentLimiter, [
{Pleroma.Search, [max_running: 30, max_waiting: 50]} {Pleroma.Search, [max_running: 30, max_waiting: 50]},
{Pleroma.Webhook.Notify, [max_running: 5, max_waiting: 200]}
] ]
config :pleroma, Pleroma.Web.WebFinger, domain: nil, update_nickname_on_user_fetch: true config :pleroma, Pleroma.Web.WebFinger, domain: nil, update_nickname_on_user_fetch: true
@ -955,6 +953,15 @@ config :pleroma, Pleroma.Search.Meilisearch,
private_key: nil, private_key: nil,
initial_indexing_chunk_size: 100_000 initial_indexing_chunk_size: 100_000
config :pleroma, Pleroma.Search.ParadeDB,
url: nil,
table: "pleroma_search_documents",
fuzzy_distance: 0
config :pleroma, Pleroma.Search.ParadeDB.Repo,
pool_size: 2,
prepare: :unnamed
config :pleroma, Pleroma.Application, config :pleroma, Pleroma.Application,
background_migrators: true, background_migrators: true,
internal_fetch: true, internal_fetch: true,
@ -977,6 +984,21 @@ config :pleroma, Pleroma.Search.QdrantSearch,
vectors: %{size: 384, distance: "Cosine"} vectors: %{size: 384, distance: "Cosine"}
} }
config :pleroma, :database_config_blacklist, [
{:pleroma, :logger}
]
config :pleroma, :database_config_whitelist, [
{:pleroma},
{:cors_plug},
{:ex_aws, :s3},
{:mime},
{:prometheus, Pleroma.Web.Endpoint.MetricsExporter},
{:web_push_encryption, :vapid_details}
]
config :pleroma, Pleroma.Chat, enabled: true
# Import environment specific config. This must remain at the bottom # Import environment specific config. This must remain at the bottom
# of this file so it overrides the configuration defined above. # of this file so it overrides the configuration defined above.
import_config "#{Mix.env()}.exs" import_config "#{Mix.env()}.exs"

View file

@ -815,7 +815,8 @@ config :pleroma, :config_description, [
"text/plain", "text/plain",
"text/html", "text/html",
"text/markdown", "text/markdown",
"text/bbcode" "text/bbcode",
"text/x.misskeymarkdown"
] ]
}, },
%{ %{
@ -1394,7 +1395,13 @@ config :pleroma, :config_description, [
label: "Post Content Type", label: "Post Content Type",
type: {:dropdown, :atom}, type: {:dropdown, :atom},
description: "Default post formatting option", description: "Default post formatting option",
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"] suggestions: [
"text/plain",
"text/html",
"text/markdown",
"text/bbcode",
"text/x.misskeymarkdown"
]
}, },
%{ %{
key: :redirectRootNoLogin, key: :redirectRootNoLogin,
@ -2719,6 +2726,19 @@ config :pleroma, :config_description, [
} }
] ]
}, },
%{
group: :pleroma,
key: Pleroma.Chat,
type: :group,
description: "Pleroma Chat settings",
children: [
%{
key: :enabled,
type: :boolean,
description: "Enables the chats API."
}
]
},
%{ %{
group: :pleroma, group: :pleroma,
key: :http, key: :http,
@ -2903,7 +2923,7 @@ config :pleroma, :config_description, [
key: Pleroma.Web.Plugs.RemoteIp, key: Pleroma.Web.Plugs.RemoteIp,
type: :group, type: :group,
description: """ description: """
`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration. `Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://hex.pm/packages/remote_ip) but with runtime configuration.
**If your instance is not behind at least one reverse proxy, you should not enable this plug.** **If your instance is not behind at least one reverse proxy, you should not enable this plug.**
""", """,
children: [ children: [
@ -2919,6 +2939,12 @@ config :pleroma, :config_description, [
A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`. A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`.
""" """
}, },
%{
key: :clients,
type: {:list, :string},
description:
"A list of client IPs or subnets in CIDR notation. These will not be treated as proxies or reserved ranges. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128."
},
%{ %{
key: :proxies, key: :proxies,
type: {:list, :string}, type: {:list, :string},
@ -3072,9 +3098,16 @@ config :pleroma, :config_description, [
%{ %{
key: :max_connections, key: :max_connections,
type: :integer, type: :integer,
description: "Maximum number of connections in the pool. Default: 250 connections.", description:
"Maximum total number of connections in the pool. HTTP/1 origins may use multiple connections within this limit, while HTTP/2 connections are multiplexed. Default: 250 connections.",
suggestions: [250] suggestions: [250]
}, },
%{
key: :max_idle_time,
type: :integer,
description: "Time before an unused connection is closed. Default: 30000ms.",
suggestions: [30_000]
},
%{ %{
key: :connect_timeout, key: :connect_timeout,
type: :integer, type: :integer,
@ -3490,6 +3523,26 @@ config :pleroma, :config_description, [
suggestion: [5] suggestion: [5]
} }
] ]
},
%{
key: Pleroma.Webhook.Notify,
type: :keyword,
description: "Concurrent limits configuration for webhooks.",
suggestions: [max_running: 5, max_waiting: 200],
children: [
%{
key: :max_running,
type: :integer,
description: "Max running concurrently jobs.",
suggestion: [5]
},
%{
key: :max_waiting,
type: :integer,
description: "Max waiting jobs.",
suggestion: [200]
}
]
} }
] ]
}, },
@ -3503,7 +3556,12 @@ config :pleroma, :config_description, [
key: :module, key: :module,
type: :keyword, type: :keyword,
description: "Selected search module.", description: "Selected search module.",
suggestion: [Pleroma.Search.DatabaseSearch, Pleroma.Search.Meilisearch] suggestion: [
Pleroma.Search.DatabaseSearch,
Pleroma.Search.Meilisearch,
Pleroma.Search.QdrantSearch,
Pleroma.Search.ParadeDB
]
} }
] ]
}, },
@ -3536,6 +3594,34 @@ config :pleroma, :config_description, [
} }
] ]
}, },
%{
group: :pleroma,
key: Pleroma.Search.ParadeDB,
type: :group,
description: "ParadeDB settings.",
children: [
%{
key: :url,
type: :string,
description:
"ParadeDB database URL (separate Postgres instance). " <>
"Can also be set via `PARADEDB_DATABASE_URL`.",
suggestion: ["postgres://postgres:postgres@127.0.0.1:5432/paradedb"]
},
%{
key: :table,
type: :string,
description: "Table name used for search documents.",
suggestion: ["pleroma_search_documents"]
},
%{
key: :fuzzy_distance,
type: :integer,
description: "Edit-distance used for fuzzy token matching (0 disables fuzziness, max 2).",
suggestion: [0, 1, 2]
}
]
},
%{ %{
group: :pleroma, group: :pleroma,
key: Pleroma.Language.LanguageDetector, key: Pleroma.Language.LanguageDetector,

View file

@ -36,7 +36,7 @@ config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local
# different ports. # different ports.
# Do not include timestamps in development logs # Do not include timestamps in development logs
config :logger, Logger.Backends.Console, format: "$metadata[$level] $message\n" config :logger, :default_formatter, format: "$metadata[$level] $message\n"
# Set a higher stacktrace during development. Avoid configuring such # Set a higher stacktrace during development. Avoid configuring such
# in production as building large stacktraces may be expensive. # in production as building large stacktraces may be expensive.

View file

@ -20,8 +20,7 @@ config :pleroma, Pleroma.Web.Endpoint,
config :phoenix, serve_endpoints: true config :phoenix, serve_endpoints: true
# Do not print debug messages in production # Do not print debug messages in production
config :logger, Logger.Backends.Console, level: :info config :logger, :default_handler, level: :info
config :logger, :console, level: :info
config :logger, :ex_syslogger, level: :info config :logger, :ex_syslogger, level: :info
# ## SSL Support # ## SSL Support

View file

@ -15,9 +15,11 @@ config :pleroma, Pleroma.Captcha,
method: Pleroma.Captcha.Mock method: Pleroma.Captcha.Mock
# Print only warnings and errors during test # Print only warnings and errors during test
config :logger, :console, config :logger, :default_handler, level: :warning
level: :warning,
format: "\n[$level] $message\n" config :logger, :default_formatter,
format: "\n[$level] $message\n",
colors: [enabled: false]
config :pleroma, :auth, oauth_consumer_strategies: [] config :pleroma, :auth, oauth_consumer_strategies: []

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

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

View file

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

View file

@ -76,6 +76,22 @@ The resulting frontend will always be installed into a folder of this template:
Careful: This folder will be completely replaced on installation. Careful: This folder will be completely replaced on installation.
## Installing Admin-FE
=== "OTP"
```sh
./bin/pleroma_ctl frontend install admin-fe --ref stable
```
=== "From Source"
```sh
mix pleroma.frontend install admin-fe --ref stable
```
Builds come from the package registry: `https://git.pleroma.social/api/packages/pleroma/generic/admin-fe-builds/${ref}/latest.zip`. `stable` and `develop` are published; `stable` is the better choice for production. A release tag works as `--ref` once a build for it is published.
## Example installation for an unknown frontend ## Example installation for an unknown frontend
The installation process is the same, but you will have to give all the needed options on the command line. For example: The installation process is the same, but you will have to give all the needed options on the command line. For example:

View file

@ -56,7 +56,7 @@ Due to aforementioned bug, preferred way of installing frontends in AdminFE is b
![screenshot of admin-fe with instructions on how to install a frontend](../assets/way_to_install_frontends.png) ![screenshot of admin-fe with instructions on how to install a frontend](../assets/way_to_install_frontends.png)
and filling in the fields. Unfortunately AdminFE does not provide the raw data necessary for you to fill those fields, so your best bet is to see what backend returns in browser's devtools or refer to the [source code](https://git.pleroma.social/pleroma/pleroma/-/blob/develop/config/config.exs?ref_type=heads#L742-791). For the most part, only **Name**, **Ref** (i.e. version) and **Build URL** fields are required, although some frontends might also require **Build Directory** to work. and filling in the fields. Unfortunately AdminFE does not provide the raw data necessary for you to fill those fields, so your best bet is to see what backend returns in browser's devtools or refer to the [source code](https://git.pleroma.social/pleroma/pleroma/-/blob/develop/config/config.exs?ref_type=heads#L742-791). For the most part, only **Name**, **Ref** (i.e. version) and **Build URL** fields are required, although some frontends might also require **Build Directory** to work.
For pleroma-fe you can use either `master` or `develop` refs, or potentially any ref in GitLab that has artifacts for `build` job, but that's outside scope of this document. Pleroma-FE and Admin-FE builds come from the package registry, not from GitLab job artifacts. For Admin-FE, set **Name** to `admin-fe`, **Ref** to `stable` (or `develop`) and **Build URL** to `https://git.pleroma.social/api/packages/pleroma/generic/admin-fe-builds/${ref}/latest.zip`. Only refs with a published build can be installed: `stable`, `develop`, and release tags once they are published.
### Selecting default frontend ### Selecting default frontend

View file

@ -541,6 +541,15 @@ Available caches:
## HTTP client ## HTTP client
Pleroma uses [Gun](https://github.com/ninenines/gun) for outgoing HTTP requests. To use Hackney instead:
```elixir
config :tesla, adapter: Tesla.Adapter.Hackney
```
!!! note
With Hackney, size limits for fetched responses (for example for link previews) are checked only after Hackney has read the whole response. They limit what Pleroma keeps, not how much it downloads or holds in memory. Gun stops reading at the limit.
### :http ### :http
* `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`) * `proxy_url`: an upstream proxy to fetch posts and/or media with, (default: `nil`)
@ -550,6 +559,8 @@ Available caches:
### :hackney_pools ### :hackney_pools
*For `hackney` adapter*
Advanced. Tweaks Hackney (http client) connections pools. Advanced. Tweaks Hackney (http client) connections pools.
There's three pools used: There's three pools used:
@ -573,7 +584,8 @@ Settings for HTTP connection pool.
* `:connection_acquisition_wait` - Timeout to acquire a connection from pool.The total max time is this value multiplied by the number of retries. * `:connection_acquisition_wait` - Timeout to acquire a connection from pool.The total max time is this value multiplied by the number of retries.
* `connection_acquisition_retries` - Number of attempts to acquire the connection from the pool if it is overloaded. Each attempt is timed `:connection_acquisition_wait` apart. * `connection_acquisition_retries` - Number of attempts to acquire the connection from the pool if it is overloaded. Each attempt is timed `:connection_acquisition_wait` apart.
* `:max_connections` - Maximum number of connections in the pool. * `:max_connections` - Maximum total number of connections in the pool. HTTP/1 origins may use multiple connections within this limit, while HTTP/2 connections are multiplexed.
* `:max_idle_time` - Time before an unused connection is closed.
* `:connect_timeout` - Timeout to connect to the host. * `:connect_timeout` - Timeout to connect to the host.
* `:reclaim_multiplier` - Multiplied by `:max_connections` this will be the maximum number of idle connections that will be reclaimed in case the pool is overloaded. * `:reclaim_multiplier` - Multiplied by `:max_connections` this will be the maximum number of idle connections that will be reclaimed in case the pool is overloaded.
@ -858,21 +870,23 @@ Web Push Notifications configuration. You can use the mix task `mix web_push.gen
* ``private_key``: VAPID private key * ``private_key``: VAPID private key
## :logger ## :logger
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog * Logging to console/stdout is done by default, use `{ExSyslogger, :ex_syslogger}` to log to syslog
An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed: An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed:
```elixir ```elixir
config :logger, config :pleroma, :logger,
backends: [{ExSyslogger, :ex_syslogger}] backends: [{ExSyslogger, :ex_syslogger}]
config :logger, default_handler: false
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :warning level: :warning
``` ```
Another example, keeping console output and adding the pid to syslog output: Another example, keeping console output and adding the pid to syslog output:
```elixir ```elixir
config :logger, config :pleroma, :logger,
backends: [:console, {ExSyslogger, :ex_syslogger}] backends: [{ExSyslogger, :ex_syslogger}]
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :warning, level: :warning,
@ -883,23 +897,22 @@ See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_s
An example of logging info to local syslog, but debug to console: An example of logging info to local syslog, but debug to console:
```elixir ```elixir
config :logger, config :pleroma, :logger,
backends: [ {ExSyslogger, :ex_syslogger}, :console ], backends: [{ExSyslogger, :ex_syslogger}]
level: :info
config :logger, :ex_syslogger, config :logger, :ex_syslogger,
level: :info, level: :info,
ident: "pleroma", ident: "pleroma",
format: "$metadata[$level] $message" format: "$metadata[$level] $message"
config :logger, :console, config :logger, :default_handler,
level: :debug, level: :debug
config :logger, :default_formatter,
format: "\n$time $metadata[$level] $message\n", format: "\n$time $metadata[$level] $message\n",
metadata: [:request_id] metadata: [:request_id]
``` ```
## Database options ## Database options
### RUM indexing for full text search ### RUM indexing for full text search
@ -1132,8 +1145,9 @@ Boolean, enables/disables in-database configuration. Read [Transferring the conf
List of valid configuration sections which are allowed to be configured from the List of valid configuration sections which are allowed to be configured from the
database. Settings stored in the database before the whitelist is configured are database. Settings stored in the database before the whitelist is configured are
still applied, so it is suggested to only use the whitelist on instances that still applied. Consider running the `mix pleroma.config filter_whitelisted` task
have not migrated the config to the database. after updating the whitelist. Read [Remove non-whitelisted configs from the database](../administration/CLI_tasks/config.md#remove-non-whitelisted-configs-from-the-database)
for more information.
Example: Example:
```elixir ```elixir

View file

@ -10,3 +10,6 @@ The other way to do it, for example, with Tor you would most likely add somethin
``` ```
config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050} config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050}
``` ```
The Gun HTTP adapter supports SOCKS5. SOCKS4 proxies require another HTTP adapter.
For regular HTTP proxies, Gun forwards plain HTTP requests directly and uses CONNECT tunnels for HTTPS requests.

View file

@ -85,7 +85,7 @@ You will see a "Default Admin API Key", this is the key you actually put into yo
### Initial indexing ### Initial indexing
After setting up the configuration, you'll want to index all of your already existing posts. Only public posts are indexed. You'll only After setting up the configuration, you'll want to index all of your already existing posts. Only public/unlisted posts are indexed. You'll only
have to do it one time, but it might take a while, depending on the amount of posts your instance has seen. This is also a fairly RAM have to do it one time, but it might take a while, depending on the amount of posts your instance has seen. This is also a fairly RAM
consuming process for `meilisearch`, and it will take a lot of RAM when running if you have a lot of posts (seems to be around 5G for ~1.2 consuming process for `meilisearch`, and it will take a lot of RAM when running if you have a lot of posts (seems to be around 5G for ~1.2
million posts while idle and up to 7G while indexing initially, but your experience may be different). million posts while idle and up to 7G while indexing initially, but your experience may be different).
@ -145,3 +145,155 @@ This will clear **all** the posts from the search index. Note, that deleted post
there is no need to actually clear the whole index, unless you want **all** of it gone. That said, the index does not hold any information there is no need to actually clear the whole index, unless you want **all** of it gone. That said, the index does not hold any information
that cannot be re-created from the database, it should also generally be a lot smaller than the size of your database. Still, the size that cannot be re-created from the database, it should also generally be a lot smaller than the size of your database. Still, the size
depends on the amount of text in posts. depends on the amount of text in posts.
## ParadeDB
[ParadeDB](https://www.paradedb.com/) is a Postgres extension that provides BM25 full-text search. In Pleroma, it can be used as an
external search backend by pointing Pleroma at a separate Postgres instance with the `pg_search` extension installed.
Pleroma's integration suite tests `pg_search` 0.24.3 on PostgreSQL 15.
Pleroma will maintain a small `pleroma_search_documents` table in that database (via the existing search indexing queue) and run search
queries against it.
The indexed text includes status content, content warnings, and attachment descriptions.
### Configuration
Set the search module to `Pleroma.Search.ParadeDB` and configure the ParadeDB database URL. These settings must be in static/runtime config
(or `PARADEDB_DATABASE_URL`) so the dedicated ParadeDB Repo is started with the application. They cannot be migrated to or changed through
ConfigDB.
> config :pleroma, Pleroma.Search, module: Pleroma.Search.ParadeDB
>
> config :pleroma, Pleroma.Search.ParadeDB,
> url: System.get_env("PARADEDB_DATABASE_URL"),
> table: "pleroma_search_documents",
> fuzzy_distance: 0
>
> config :pleroma, Pleroma.Search.ParadeDB.Repo,
> pool_size: 2,
> prepare: :unnamed
When connecting over an untrusted network, enable certificate verification in the Repo configuration. For example:
> config :pleroma, Pleroma.Search.ParadeDB.Repo,
> ssl: [
> verify: :verify_peer,
> cacertfile: "/etc/pleroma/paradedb-ca.pem",
> server_name_indication: ~c"paradedb.example.com"
> ]
### Initial rollout
The `pg_search` extension must already be available in the database. Creating the extension normally requires an administrative database
role. The Pleroma runtime role requires `CONNECT` on the database, `USAGE` on the table's schema and the `pdb` schema, and `SELECT`, `INSERT`, `UPDATE`,
and `DELETE` on the generated table. For example, after creating the table as its owner:
```sql
GRANT CONNECT ON DATABASE paradedb TO pleroma_search;
GRANT USAGE ON SCHEMA public, pdb TO pleroma_search;
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE pleroma_search_documents TO pleroma_search;
```
Use this rollout order to prevent jobs from being handled by the previous backend:
1. Install `pg_search` 0.24.3 on PostgreSQL 15 or another deliberately tested combination.
2. Stop Pleroma so no search indexing jobs are running.
3. Put the ParadeDB backend in static configuration.
4. Create the table and BM25 index in a one-shot offline process using `PARADEDB_DATABASE_URL` with the table owner/setup credential.
5. Apply the runtime grants shown above, set the service's `PARADEDB_DATABASE_URL` to the restricted runtime credential, and start Pleroma.
6. Start the initial backfill and monitor the `search_indexing` queue.
Create the table and BM25 index once with:
=== "OTP"
```sh
PARADEDB_DATABASE_URL='OWNER_URL' PLEROMA_CTL_RPC_DISABLED=true \
./bin/pleroma_ctl search.indexer create_index
```
=== "From Source"
```sh
PARADEDB_DATABASE_URL='OWNER_URL' mix pleroma.search.indexer create_index
```
The task currently prints an error without returning a failing process status. Verify that it prints `Index created` before continuing.
### Backfill
Run an initial indexing pass to enqueue existing posts:
=== "OTP"
```sh
./bin/pleroma_ctl search.indexer index
```
=== "From Source"
```sh
PARADEDB_DATABASE_URL='RUNTIME_URL' mix pleroma.search.indexer index
```
The command enqueues up to 100,000 Create activities by default. It uses keyset pagination, so new activities arriving during the backfill
do not shift the remaining pages. The defaults and accepted options are:
* `--limit`: maximum activities to enqueue in this run; defaults to `100000` and must be positive
* `--step`: activities fetched from Pleroma's database per page; defaults to `1000` and must be positive
* `--chunk`: jobs inserted into Oban per batch; defaults to `100` and must be positive
* `--before`: resume before the checkpoint activity ID printed by an earlier run; the cursor is exclusive
The checkpoint is the last activity enqueued, not confirmation that its job succeeded. Repeat the command with its last `--before`
checkpoint only when it reports that more activities remain. Inspect failed or discarded Oban jobs as well as waiting jobs before declaring
the backfill complete.
The same operation can be run from an attached console while Pleroma is online:
```elixir
Pleroma.Search.Backfill.run(limit: 10_000)
```
The returned `next_cursor` can be supplied as `before` when `exhausted` is `false`. The attached API also accepts `limit: :infinity`, but this
can create an unbounded number of Oban rows; bounded runs are safer on large instances. An `on_page` callback may be supplied for progress
reporting.
Note: Like the other external search backends, only public/unlisted Notes are indexed.
Set `fuzzy_distance` to `1` or `2` to allow typo-tolerant matching (`2` is the maximum ParadeDB supports).
Indexing runs via the Oban `search_indexing` queue. Its stock configuration starts paused so the search health monitor can control it; verify
that the queue is running before backfilling. A manually managed queue can be enabled with `search_indexing: 10` in the Oban `queues`
configuration instead of a `paused: true` entry.
Monitor that queue until the backfill jobs have drained before evaluating search completeness.
### Rebuilding
Stop Pleroma, or otherwise ensure no search indexing jobs are running, before rebuilding. While ParadeDB is still the configured backend,
run `drop_index`, then `create_index` with the table owner/setup credential. Reapply runtime grants after recreating the table, restart
Pleroma, and begin a new backfill. The DDL tasks print failures without returning a failing process status, so verify the exact `Index
dropped` and `Index created` success messages. Dropping the table removes only derived search data; Pleroma activities remain in the primary
database.
### Rolling back
To retain the ParadeDB table for a later retry, configure `Pleroma.Search.DatabaseSearch` (or the previous external backend) in static config
and restart Pleroma.
Before switching backends, inspect the `search_indexing` queue. Drain or cancel pending backfill jobs, or explicitly accept that they will run
against the newly configured backend after restart.
To remove the table during rollback, first stop Pleroma and run `drop_index` with the table owner/setup credential while ParadeDB is still
configured:
=== "OTP"
```sh
PARADEDB_DATABASE_URL='OWNER_URL' PLEROMA_CTL_RPC_DISABLED=true \
./bin/pleroma_ctl search.indexer drop_index
```
=== "From Source"
```sh
PARADEDB_DATABASE_URL='OWNER_URL' mix pleroma.search.indexer drop_index
```
Verify that the task prints `Index dropped`. Then change the backend in static config and restart. The ParadeDB environment variable may be
removed after removing the ParadeDB stanza or switching to a backend that does not start its Repo. After switching away from ParadeDB, the
generic `drop_index` task targets the newly configured backend and will not remove the ParadeDB table; remove it directly with the database
owner instead.

View file

@ -80,10 +80,24 @@ The `/api/v1/pleroma/admin/*` path is backwards compatible with `/api/pleroma/ad
{ {
`nickname`, `nickname`,
`email`, `email`,
`password` `password` (optional; when provided, must not be empty)
} }
] ]
- Response: Array of user objects - Response: Array of user objects. When a user is created without a `password`, `data`
object contains a `password_reset_link` the user can follow to set their own password.
```json
[
{
"data": {
"nickname": string,
"email": string,
"password_reset_link": string (optional)
},
"type": "success"
}
]
```
## `POST /api/v1/pleroma/admin/users/follow` ## `POST /api/v1/pleroma/admin/users/follow`
@ -665,6 +679,7 @@ Status: 404
- *optional* `limit`: **integer** the number of records to retrieve - *optional* `limit`: **integer** the number of records to retrieve
- *optional* `page`: **integer** page number - *optional* `page`: **integer** page number
- *optional* `page_size`: **integer** number of log entries per page (default is `50`) - *optional* `page_size`: **integer** number of log entries per page (default is `50`)
- *optional* `assigned_account`: **string** assigned account ID
- Response: - Response:
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin - On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
- On success: JSON, returns a list of reports, where: - On success: JSON, returns a list of reports, where:
@ -749,6 +764,7 @@ Status: 404
"url": "https://pleroma.example.org/users/lain", "url": "https://pleroma.example.org/users/lain",
"username": "lain" "username": "lain"
}, },
"assigned_account": null,
"content": "Please delete it", "content": "Please delete it",
"created_at": "2019-04-29T19:48:15.000Z", "created_at": "2019-04-29T19:48:15.000Z",
"id": "9iJGOv1j8hxuw19bcm", "id": "9iJGOv1j8hxuw19bcm",
@ -868,6 +884,37 @@ Status: 404
] ]
``` ```
- Response:
- On failure:
- 400 Bad Request, JSON:
```json
[
{
`id`, // report id
`error` // error message
}
]
```
- On success: `204`, empty response
## `POST /api/v1/pleroma/admin/reports/assign_account`
### Assign account to one or multiple reports
- Params:
```json
`reports`: [
{
`id`, // required, report id
`nickname` // account nickname, use null to unassign account
},
...
]
```
- Response: - Response:
- On failure: - On failure:
- 400 Bad Request, JSON: - 400 Bad Request, JSON:
@ -1806,3 +1853,92 @@ Note that this differs from the Mastodon API variant: Mastodon API only returns
```json ```json
{} {}
``` ```
## `GET /api/v1/pleroma/admin/webhooks`
### List webhooks
- Method: `GET`
- Response:
```json
[
{
"enabled": true,
"id": "2",
"events": ["account.created"],
"url": "https://webhook.example/",
"secret": "eb85d4ccd8510e78f912743949dc354e8146987d",
"updated_at": "2022-10-29T17:44:16.000Z",
"created_at": "2022-10-29T17:44:13.000Z"
}
]
```
## `GET /api/v1/pleroma/admin/webhooks/:id`
### Get an individual webhook
- Method: `GET`
- Params:
- `id`: **string** Webhook ID
- Response: A webhook
## `POST /api/v1/pleroma/admin/webhooks`
### Create a webhook
- Method: `POST`
- Params:
- `url`: **string** Webhook URL
- `events`: **[string]** Types of events to trigger on (`account.created`, `report.created`)
- *optional* `enabled`: **boolean** Whether webhook is enabled
- Response: A webhook
## `PATCH /api/v1/pleroma/admin/webhooks/:id`
### Update a webhook
- Method: `PATCH`
- Params:
- `id`: **string** Webhook ID
- *optional* `url`: **string** Webhook URL
- *optional* `events`: **[string]** Types of events to trigger on (`account.created`, `report.created`)
- *optional* `enabled`: **boolean** Whether webhook is enabled
- Response: A webhook
## `DELETE /api/v1/pleroma/admin/webhooks/:id`
### Delete a webhook
- Method: `DELETE`
- Params:
- `id`: **string** Webhook ID
- Response: A webhook
## `POST /api/v1/pleroma/admin/webhooks/:id/enable`
### Activate a webhook
- Method: `POST`
- Params:
- `id`: **string** Webhook ID
- Response: A webhook
## `POST /api/v1/pleroma/admin/webhooks/:id/disable`
### Deactivate a webhook
- Method: `POST`
- Params:
- `id`: **string** Webhook ID
- Response: A webhook
## `POST /api/v1/pleroma/admin/webhooks/:id/rotate_secret`
### Rotate webhook signing secret
- Method: `POST`
- Params:
- `id`: **string** Webhook ID
- Response: A webhook

View file

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

View file

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

View file

@ -4,7 +4,7 @@
## Installation ## Installation
This guide will assume you are on Debian 12 (“bookworm”) or later. This guide should also work with Ubuntu 22.04 (“jammy”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead. This guide will assume you are on Debian 13 (“Trixie”) or later. This guide should also work with Ubuntu 26.04 (“resolute”) and later. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
{! backend/installation/generic_dependencies.include !} {! backend/installation/generic_dependencies.include !}

View file

@ -14,7 +14,7 @@ Note: This article is potentially outdated because at this time we may not have
- PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください) - PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
- `postgresql-contrib` 11.0以上 (同上) - `postgresql-contrib` 11.0以上 (同上)
- Elixir 1.15 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください) - Elixir 1.17 以上 (Erlang/OTP 27 以上) ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
- `erlang-dev` - `erlang-dev`
- `erlang-nox` - `erlang-nox`
- `git` - `git`

View file

@ -0,0 +1,2 @@
* Elixir >=1.17.0, <1.20
* Erlang OTP >=27.0.0 (supported: <29)

View file

@ -1,8 +1,7 @@
## Required dependencies ## Required dependencies
{! backend/installation/erlang_elixir_requirements.include !}
* PostgreSQL >=11.0 * PostgreSQL >=11.0
* Elixir >=1.15.0 <1.19
* Erlang OTP >=23.0.0 (supported: <28)
* git * git
* file / libmagic * file / libmagic
* gcc or clang * gcc or clang
@ -14,3 +13,7 @@
* ImageMagick * ImageMagick
* FFmpeg * FFmpeg
* exiftool * exiftool
!!!info
**Source installs only**
If your operating system repositories don't meet the Elixir/Erlang requirements, check [Installing newer versions of Elixir and Erlang](./installing_newer_elixir_erlang.md).

View file

@ -0,0 +1,333 @@
# Installing newer versions of Elixir and Erlang
This guide is for installing newer versions of Elixir and/or Erlang on your system when installing Pleroma from source and your operating system repositories don't meet the minimum requirements listed below.
This guide assumes you are using Debian or Ubuntu.
**Using the system repositories is recommended if possible.**
**If a newer release of your operating system would meet the requirements, consider upgrading instead.**
Shell commands prepended with `#` in this page are supposed to be ran with root privileges, commands prepended with `$` are supposed to be ran as the Pleroma user unless noted otherwise.
!!!warning
If you are, or will be, following the source install guide, make sure to switch to the Pleroma user before running any of the `mix` commands using `sudo -Hu pleroma /bin/bash`.
Not doing so will result in `command not found` errors.
!!!info
Most of this guide can be used to install Elixir and/or Erlang on operating systems that don't package either one.
Installing Pleroma on such systems is largely untested.
## Requirements
{! backend/installation/erlang_elixir_requirements.include !}
## Install methods
Choose one of the following install methods:
* User level:
* mise (Erlang and Elixir install methods are built-in[^1])
* asdf (plugin-based, plugins are shells scripts; builds Erlang from source)
* pkgsrc (for advanced users, harder to install and maintain; builds Elixir and Erlang from source)
* System-wide:
* pkgsrc (for advanced users, harder to install and maintain; builds Elixir and Erlang from source)
[^1]: On Ubuntu downloads binaries both for Elixir and Erlang. On Debian builds Erlang from source and downloads binaries for Elixir.
!!!warning
**Ignore if using mise on Ubuntu.**
You are about to compile Erlang on your server.
If you are planning to run Pleroma on a VPS virtual machine, make sure you have enough disk space and system resources available.
At least 2GB of RAM and 8GB of extra free disk space is required to continue in this guide.
If your system does not meet these requirements, use [OTP install](./otp_en.md).
**This will take a considerable amount of time.**
!!!warning
Before proceeding, stop Pleroma if already running and uninstall both Elixir and Erlang.
Keeping Elixir will likely result in a non-functional Elixir install and keeping Erlang can result in various failures to compile Pleroma and its dependencies.
## mise
To start, create the Pleroma user according to the [source install guide](./debian_based_en.md#install-pleromabe).
### Debian-only preparation
If you are using Ubuntu, skip to the [next section](./installing_newer_elixir_erlang.md#elixirerlang-installation).
Install dependencies:
```
# apt install build-essential libssl-dev libncurses-dev git unzip
```
Add Erlang autotools configure options to Pleroma user's shell config (`.bashrc` for bash):
```sh
export KERL_CONFIGURE_OPTIONS="--without-javac --without-odbc --without-wx --without-ssh"
```
### Elixir/Erlang installation
Install curl (for the mise installer):
```
# apt install curl
```
Switch to the Pleroma user using `sudo -Hu pleroma /bin/bash` and follow the install steps from mise documentation and install it for the user Pleroma: [https://mise.jdx.dev/getting-started.html](https://mise.jdx.dev/getting-started.html)
Add the following to activate mise in the Pleroma user's shell configuration (`.bashrc` for bash):
```sh
# Modify to match the mise installation location
eval "$($HOME/.local/bin/mise activate bash)"
```
Source new profile settings:
```sh
$ source ~/.bashrc
```
If installing Pleroma, follow the [Pleroma source installation instructions](./debian_based_en.md) up to the point before downloading mix dependencies for Pleroma, ignoring the instructions to download Elixir/Erlang.
Install Erlang (replace `<version>` with a supported release meeting Pleroma's requirements, OTP 27 is recommended):
```sh
$ mise install erlang@<version>
```
!!!info
To get a list of available versions, use `mise ls-remote <language/tool>`.
Then run the following in the Pleroma source code repository:
```sh
$ mise use erlang@<version>
```
Install Elixir (replace `<version>` with a supported release and `<OTP version>` with the mise-installed Erlang/OTP version, Elixir 1.18 is recommended):
```sh
$ mise install elixir@<version>-otp-<OTP version>
```
Then run the following in the Pleroma source code directory:
```sh
$ mise use elixir@<Elixir version>
```
If installing Pleroma, continue with the source install guide up to the point of installing the systemd Pleroma service.
Don't attempt to start the Pleroma service yet.
Create a `pleroma.sh` startup script owned by the Pleroma user in Pleroma user's home directory:
```sh
#!/bin/bash
# Modify to match the mise installation location
eval "$($HOME/.local/bin/mise activate bash)"
exec mix phx.server
```
Make the startup script executable:
```sh
chmod u+x pleroma.sh
```
Override the Pleroma service file to point to the new start up script, run `# systemctl edit pleroma` and add the following:
```ini
[Service]
ExecStart=
ExecStart=/var/lib/pleroma/pleroma.sh
```
You can now start the Pleroma service.
If you are installing Pleroma, continue with creating your first user according to the install guide.
## asdf
To start, create the Pleroma user according to the [source install guide](./debian_based_en.md#install-pleromabe) and switch to the user using `sudo -Hu pleroma /bin/bash`.
Then follow the install and configure steps from asdf documentation and install it for the user Pleroma: [https://asdf-vm.com/guide/getting-started.html](https://asdf-vm.com/guide/getting-started.html)
Install dependencies:
```
# apt install build-essential libssl-dev libncurses-dev git unzip curl
```
Add Erlang autotools configure options to Pleroma user's shell config (`.bashrc` for bash):
```sh
export KERL_CONFIGURE_OPTIONS="--without-javac --without-odbc --without-wx --without-ssh"
```
Source new profile settings:
```sh
$ source ~/.bashrc
```
Install the Erlang plugin:
```sh
$ asdf plugin add erlang https://github.com/asdf-vm/asdf-erlang.git
```
Build and install Erlang (replace `<version>` with a supported release meeting Pleroma's requirements, OTP 27 is recommended):
```sh
$ asdf install erlang <version>
```
!!!info
To get a list of available versions, use `asdf list all <plugin>`.
Install the Elixir plugin:
```sh
$ asdf plugin add elixir https://github.com/asdf-vm/asdf-elixir.git
```
Install Elixir (replace `<version>` with a supported release and `<OTP version>` with the asdf-installed Erlang/OTP version, Elixir 1.18 is recommended):
```sh
$ asdf install elixir <version>-otp-<OTP version>
```
If installing Pleroma, follow the [Pleroma source installation instructions](./debian_based_en.md) up to the point before downloading mix dependencies for Pleroma, ignoring the instructions to download Elixir/Erlang.
Then run the following in the Pleroma source code directory:
```sh
$ asdf set erlang <OTP version>
$ asdf set elixir <Elixir version>
```
If installing Pleroma, continue with the source install guide up to the point of installing the systemd Pleroma service.
Don't attempt to start the Pleroma service yet.
Create a `pleroma.sh` startup script owned by the Pleroma user in Pleroma user's home directory:
```sh
#!/bin/sh
# Modify to match your .bashrc settings used when installing asdf
export ASDF_DATA_DIR="/your/custom/data/dir"
export PATH="${ASDF_DATA_DIR:-$HOME/.asdf}/shims:$PATH"
exec mix phx.server
```
Make the startup script executable:
```sh
chmod u+x pleroma.sh
```
Override the Pleroma service file to point to the new start up script, run `# systemctl edit pleroma` and add the following:
```ini
[Service]
ExecStart=
ExecStart=/var/lib/pleroma/pleroma.sh
```
You can now start the Pleroma service.
If you are installing Pleroma, continue with creating your first user according to the install guide.
## pkgsrc
pkgsrc is NetBSD's portable package building framework also supporting Linux.
Its use is discouraged for novice users as it is harder to maintain and keep up-to-date compared to other options in this guide.
Before installing pkgsrc, choose which install method (tarball or CVS) you want to use.
Both update differently and have their caveats, check NetBSD documentation for more info: [https://www.netbsd.org/docs/pkgsrc/getting.html#uptodate](https://www.netbsd.org/docs/pkgsrc/getting.html#uptodate)
Using the stable branch is recommended.
!!!warning
For CVS install, `cvs` might not be installed on your system, in which case it can be installed with `# apt install cvs`.
pkgsrc supports both user-level and system-wide package installation, hence why most commands below don't have a `#`/`$` prepended to them.
For user-level installs, use the Pleroma user created in the [source install guide](./debian_based_en.md#install-pleromabe).
To start, follow the `quickstart -> install source packages` portion of the guide to acquire pkgsrc: [https://pkgsrc.org](https://pkgsrc.org)
### Bootstrap
Install build dependencies for bootstrap:
```
# apt install build-essential
```
For user-level installation run:
```sh
$ cd pkgsrc/bootstrap
$ ./bootstrap --prefer-pkgsrc yes --unprivileged --make-jobs <number of CPU cores - 1>
```
For system-wide installation run:
```
# cd pkgsrc/bootstrap
# ./bootstrap --prefer-pkgsrc yes --make-jobs <number of CPU cores - 1>
```
!!!info
For more bootstrap options, run `./bootstrap --help`
Add pgksrc packages to `$PATH` in shell configuration (`.bashrc` for bash), default prefix is `$HOME/pkg` for user-level install and `/usr/pkg` for system-wide install:
```sh
export PATH="$PATH:<prefix for pkgsrc install>/bin"
```
Source new profile settings:
```sh
source ~/.bashrc
```
!!!info
For system-wide installs, both the root user and Pleroma user need to have the `PATH` set accordingly.
### Package installation
Erlang:
```sh
cd pkgsrc/lang/erlang
bmake install clean
```
Elixir:
```sh
cd ../elixir
bmake install clean
```
### Pleroma startup script
If installing Pleroma, first follow the [Pleroma source installation instructions](./debian_based_en.md) up to the point of installing the systemd Pleroma service, ignoring the instruction to install Elixir/Erlang. Don't attempt to start the service yet.
Create a `pleroma.sh` startup script owned by the Pleroma user in Pleroma user's home directory:
```sh
#!/bin/sh
# Modify to match your .bashrc settings used when bootsrapping pkgsrc
export PATH="$PATH:<prefix for pkgsrc install>/bin"
exec mix phx.server
```
Make the startup script executable:
```sh
chmod u+x pleroma.sh
```
Override the Pleroma service file to point to the new start up script, run `# systemctl edit pleroma` and add the following:
```ini
[Service]
ExecStart=
ExecStart=/var/lib/pleroma/pleroma.sh
```
You can now start the Pleroma service.
If you are installing Pleroma, continue with creating your first user according to the install guide.
## Maintenance
Since Elixir and Erlang is no longer managed by the system package manager after following this guide, it is recommended to periodically check their respective websites/repositories for newer versions with security bug fixes.
When updating Elixir, Erlang or both, stop Pleroma before proceeding with the update.
Pleroma will likely need to be recompiled after the update by running the following command in the Pleroma source code directory as the Pleroma user:
```sh
MIX_ENV=prod mix compile
```
If Pleroma fails to compile or start after upgrading Elixir/Erlang, run the following as a troubleshooting step in the Pleroma source code directory as the Pleroma user:
```sh
mix clean
mix deps.clean --all
mix deps.get
MIX_ENV=prod mix compile
```
Then try to start Pleroma again. If that doesn't help, join the Matrix room or IRC channel mentioned below, or make an issue on [git.pleroma.social/pleroma/pleroma](https://git.pleroma.social/pleroma/pleroma/issues/new/choose) explaining the problem.
## Questions
Questions about the guide or it didn’t work as it should, ask in [#pleroma:libera.chat](https://matrix.to/#/#pleroma:libera.chat) via Matrix or **#pleroma** on **libera.chat** via IRC.
<!-- vim ts=4:sw=4 -->

View file

@ -0,0 +1,130 @@
# Migrating from Rebased
Rebased and Pleroma currently have compatible database schemas, so removing
Rebased-specific database objects is optional. The cleanup described below is
destructive and can take a long time on large databases.
This guide requires a Pleroma release that contains this document and native
webhook support. If you are reading the development version of this guide
before those changes reach `stable`, wait for the corresponding stable release.
## Prepare and back up
Plan a maintenance window and read the [backup guide](../administration/backup.md)
before starting.
Stop Rebased and confirm it is stopped before making the backup. Omit the
backup guide's final restart step and leave Rebased stopped:
```bash
sudo systemctl stop pleroma
sudo systemctl is-active pleroma
```
Make a verified database backup and a copy of the Rebased installation,
including its configuration, uploads, and `instance` directory. Also save the
current systemd unit if you plan to replace it.
## Install Pleroma
Do not use `git pull --rebase` to change an existing Rebased checkout into a
Pleroma checkout. The forks use different branches, and rebasing can replay
Rebased-specific commits over Pleroma. Move the old checkout aside and clone a
clean copy of Pleroma instead. The paths below assume the default source
installation in `/opt/pleroma`:
```bash
sudo mv /opt/pleroma /opt/pleroma.rebased
sudo mkdir /opt/pleroma
sudo chown pleroma:pleroma /opt/pleroma
sudo -Hu pleroma git clone -b stable https://git.pleroma.social/pleroma/pleroma /opt/pleroma
```
Restore the instance-specific files from the old checkout. This usually
includes `config/prod.secret.exs`, `config/prod.exported_from_db.secret.exs` if
present, `uploads`, and `instance`. Diff and restore any other non-stock
configuration files, preserve their ownership, and review them before starting
Pleroma.
Pleroma requires Elixir 1.17 or newer and Erlang/OTP 27 or newer. Install supported Erlang and Elixir
versions as described in the installation guide for your distribution before
running any Mix command. Do not reuse Rebased's `.tool-versions`. If you keep
using [`asdf`](https://asdf-vm.com/), select supported versions in the
`pleroma` user's home and use the asdf Mix shim for every command below.
Set `MIX` to the appropriate binary, verify the selected runtime, then install
the dependencies and compile Pleroma:
```bash
cd /opt/pleroma
MIX=/usr/bin/mix
# If using asdf instead:
# MIX=/var/lib/pleroma/.asdf/shims/mix
sudo -Hu pleroma "$MIX" --version
sudo -Hu pleroma MIX_ENV=prod "$MIX" deps.get
sudo -Hu pleroma MIX_ENV=prod "$MIX" compile
```
Compare your existing systemd unit with `installation/pleroma.service`. If you
replace it, review its user, paths, and environment first. Reload systemd after
making changes:
```bash
sudo systemctl daemon-reload
```
Rebased does not bundle a frontend, so `instance/static` may contain an
installed frontend. Pleroma includes a bundled frontend. You can remove the
separately installed frontend after the migration, but do not remove other
instance files such as custom emojis.
## Database migration
Rebased's extra columns, index, and enum labels do not prevent Pleroma from
starting. Existing notifications with Rebased-only event types are not
supported by Pleroma, however, and can cause notification API errors. Run the
cleanup below, or archive and remove those notification rows separately before
starting Pleroma. Skipping the cleanup preserves them if you need to restore
Rebased.
Pleroma supports Rebased's webhook schema. The cleanup does not roll back the
shared webhook migrations, so webhook configuration is preserved.
To perform the optional cleanup, run the rollback while the service is stopped:
```bash
MIX=/usr/bin/mix
# If using asdf instead:
# MIX=/var/lib/pleroma/.asdf/shims/mix
sudo -Hu pleroma MIX_ENV=prod "$MIX" ecto.rollback --migrations-path priv/repo/optional_migrations/rebased_rollbacks --all
```
This deletes the Rebased-only `accepts_email_list`, `location`, and
`last_move_at` user fields and the `pleroma:participation_accepted`,
`pleroma:participation_request`, `pleroma:event_reminder`, and
`pleroma:event_update` notifications. It also rewrites the notifications table
and can require substantial time, free disk space, and write-ahead log capacity
on a large instance.
Apply all pending Pleroma migrations:
```bash
sudo -Hu pleroma MIX_ENV=prod "$MIX" ecto.migrate
```
## Start and verify Pleroma
Start Pleroma and inspect its status and logs:
```bash
sudo systemctl start pleroma
sudo systemctl status pleroma
sudo journalctl -u pleroma -n 100 --no-pager
```
Verify that the instance, uploads, frontend, and webhook configuration work as
expected before deleting `/opt/pleroma.rebased` or the database backup. If the
migration fails, keep the service stopped and restore both the database backup
and the previous checkout before restarting Rebased. If you changed the
systemd unit or runtime setup, restore those too and run
`systemctl daemon-reload` before restarting the service.

View file

@ -76,35 +76,51 @@ $EDITOR /etc/pleroma/config.exs
``` ```
## Installing the release ## Installing the release
Before proceeding, get the flavour from [Detecting flavour](otp_en.md#detecting-flavour) section in OTP installation guide. Before proceeding, get the flavour from [Detecting flavour](otp_en.md#detecting-flavour) section in OTP installation guide.
Delete all files in pleroma user's directory:
```sh ```sh
# Delete all files in pleroma user's directory
rm -r ~pleroma/* rm -r ~pleroma/*
```
# Set the flavour environment variable to the string you got in Detecting flavour section. Set the flavour environment variable to the string you got in Detecting flavour section.
# For example if the flavour is `amd64-musl` the command will be For example if the flavour is `amd64-musl` the command will be:
```sh
export FLAVOUR="amd64-musl" export FLAVOUR="amd64-musl"
```
# Clone the release build into a temporary directory and unpack it Clone the release build into a temporary directory and unpack it.
# Replace `stable` with `unstable` if you want to run the unstable branch Replace `stable` with `develop`, if you want to run the develop branch.
sudo -Hu pleroma "
curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
unzip /tmp/pleroma.zip -d /tmp/
"
# Move the release to the home directory and delete temporary files ```sh
sudo -Hu pleroma " sudo -Hu pleroma curl "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-stable-$FLAVOUR/latest/pleroma.zip" -o /tmp/pleroma.zip
mv /tmp/release/* ~pleroma/ sudo -Hu pleroma unzip /tmp/pleroma.zip -d /tmp/
rmdir /tmp/release ```
rm /tmp/pleroma.zip
"
# Start the instance to verify that everything is working as expected Move the release to the home directory and delete temporary files:
```sh
sudo -Hu pleroma mv /tmp/release/* ~pleroma/
sudo -Hu pleroma rmdir /tmp/release
sudo -Hu pleroma rm /tmp/pleroma.zip
```
Start the instance to verify that everything is working as expected:
```sh
sudo -Hu pleroma "./bin/pleroma daemon" sudo -Hu pleroma "./bin/pleroma daemon"
```
# Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly:
```sh
sleep 20 && curl http://localhost:4000/api/v1/instance sleep 20 && curl http://localhost:4000/api/v1/instance
```
# Stop the instance Stop the instance:
```sh
sudo -Hu pleroma "./bin/pleroma stop" sudo -Hu pleroma "./bin/pleroma stop"
``` ```

View file

@ -67,13 +67,19 @@ Pleroma will be run by a dedicated \_pleroma user. Before creating it, insert th
``` ```
pleroma:\ pleroma:\
:datasize=1536M:\ :datasize=4096M:\
:openfiles-max=4096:\ :openfiles-max=4096:\
:openfiles-cur=1024:\ :openfiles-cur=1024:\
:setenv=LC_ALL=en_US.UTF-8,VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS,MIX_ENV=prod:\ :setenv=LC_ALL=en_US.UTF-8,VIX_COMPILATION_MODE=PLATFORM_PROVIDED_LIBVIPS,MIX_ENV=prod:\
:tc=daemon: :tc=daemon:
``` ```
Then run:
```
# cap_mkdb /etc/login.conf
```
This creates a "pleroma" login class and sets higher values than default for datasize and openfiles (see [login.conf(5)](https://man.openbsd.org/login.conf)), this is required to avoid having Pleroma crash some time after starting. This creates a "pleroma" login class and sets higher values than default for datasize and openfiles (see [login.conf(5)](https://man.openbsd.org/login.conf)), this is required to avoid having Pleroma crash some time after starting.
Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/):

View file

@ -32,6 +32,7 @@ Other than things bundled in the OTP release Pleroma depends on:
* curl (to download the release build) * curl (to download the release build)
* unzip (needed to unpack release builds) * unzip (needed to unpack release builds)
* ncurses (ERTS won't run without it) * ncurses (ERTS won't run without it)
* libvips (for image processing)
* PostgreSQL (also utilizes extensions in postgresql-contrib) * PostgreSQL (also utilizes extensions in postgresql-contrib)
* nginx (could be swapped with another reverse proxy but this guide covers only it) * nginx (could be swapped with another reverse proxy but this guide covers only it)
* certbot (for Let's Encrypt certificates, could be swapped with another ACME client, but this guide covers only it) * certbot (for Let's Encrypt certificates, could be swapped with another ACME client, but this guide covers only it)
@ -46,7 +47,13 @@ Other than things bundled in the OTP release Pleroma depends on:
=== "Debian/Ubuntu" === "Debian/Ubuntu"
``` ```
apt install curl unzip libncurses5 postgresql postgresql-contrib nginx certbot libmagic-dev apt install curl unzip libncurses6 libvips42t64 postgresql postgresql-contrib nginx certbot libmagic-dev
```
On Debian 12 and Ubuntu 22.04, replace `libvips42t64` with `libvips42`:
```
apt install curl unzip libncurses6 libvips42 postgresql postgresql-contrib nginx certbot libmagic-dev
``` ```
### Installing optional packages ### Installing optional packages
@ -88,9 +95,23 @@ RUM indexes are an alternative indexing scheme that is not included in PostgreSQ
``` ```
=== "Debian/Ubuntu" === "Debian/Ubuntu"
Install the package matching your distribution's PostgreSQL version:
``` ```
# Available only on Buster/19.04 # Debian 13
apt install postgresql-11-rum apt install postgresql-17-rum
# Debian 12
apt install postgresql-15-rum
# Ubuntu 26.04
apt install postgresql-18-rum
# Ubuntu 24.04
apt install postgresql-16-rum
# Ubuntu 22.04
apt install postgresql-14-rum
``` ```
#### (Optional) Performance configuration #### (Optional) Performance configuration
@ -109,60 +130,90 @@ Restart PostgreSQL to apply configuration changes:
``` ```
### Installing Pleroma ### Installing Pleroma
Create a Pleroma user:
```sh ```sh
# Create a Pleroma user
adduser --system --shell /bin/false --home /opt/pleroma pleroma adduser --system --shell /bin/false --home /opt/pleroma pleroma
```
# Set the flavour environment variable to the string you got in Detecting flavour section. Set the flavour environment variable to the string you got in Detecting flavour section. For example if the flavour is `amd64-musl` the command will be:
# For example if the flavour is `amd64-musl` the command will be
```sh
export FLAVOUR="amd64-musl" export FLAVOUR="amd64-musl"
```
# Clone the release build into a temporary directory and unpack it Clone the release build into a temporary directory and unpack it:
sudo -Hu pleroma "
curl 'https://git.pleroma.social/api/v4/projects/2/jobs/artifacts/stable/download?job=$FLAVOUR' -o /tmp/pleroma.zip
unzip /tmp/pleroma.zip -d /tmp/
"
# Move the release to the home directory and delete temporary files ```sh
sudo -Hu pleroma " sudo -Hu pleroma curl "https://git.pleroma.social/api/packages/pleroma/generic/pleroma-otp-stable-$FLAVOUR/latest/pleroma.zip" -o /tmp/pleroma.zip
mv /tmp/release/* /opt/pleroma sudo -Hu pleroma unzip /tmp/pleroma.zip -d /tmp/
rmdir /tmp/release ```
rm /tmp/pleroma.zip
"
# Create uploads directory and set proper permissions (skip if planning to use a remote uploader)
# Note: It does not have to be `/var/lib/pleroma/uploads`, the config generator will ask about the upload directory later
Move the release to the home directory and delete temporary files:
```sh
sudo -Hu pleroma mv /tmp/release/* /opt/pleroma
sudo -Hu pleroma rmdir /tmp/release
sudo -Hu pleroma rm /tmp/pleroma.zip
```
Create uploads directory and set proper permissions (skip if planning to use a remote uploader).
Note: It does not have to be `/var/lib/pleroma/uploads`, the config generator will ask about the upload directory later.
```sh
mkdir -p /var/lib/pleroma/uploads mkdir -p /var/lib/pleroma/uploads
chown -R pleroma /var/lib/pleroma chown -R pleroma /var/lib/pleroma
```
# Create custom public files directory (custom emojis, frontend bundle overrides, robots.txt, etc.) Create custom public files directory (custom emojis, frontend bundle overrides, robots.txt, etc.)
# Note: It does not have to be `/var/lib/pleroma/static`, the config generator will ask about the custom public files directory later Note: It does not have to be `/var/lib/pleroma/static`, the config generator will ask about the custom public files directory later.
```sh
mkdir -p /var/lib/pleroma/static mkdir -p /var/lib/pleroma/static
chown -R pleroma /var/lib/pleroma chown -R pleroma /var/lib/pleroma
```
# Create a config directory Create a config directory:
```sh
mkdir -p /etc/pleroma mkdir -p /etc/pleroma
chown -R pleroma /etc/pleroma chown -R pleroma /etc/pleroma
```
# Run the config generator Run the config generator:
```sh
sudo -Hu pleroma "./bin/pleroma_ctl instance gen --output /etc/pleroma/config.exs --output-psql /tmp/setup_db.psql" sudo -Hu pleroma "./bin/pleroma_ctl instance gen --output /etc/pleroma/config.exs --output-psql /tmp/setup_db.psql"
```
# Create the postgres database Create the postgres database and schema:
```sh
sudo -u postgres -s $SHELL -lc "psql -f /tmp/setup_db.psql" sudo -u postgres -s $SHELL -lc "psql -f /tmp/setup_db.psql"
# Create the database schema
sudo -Hu pleroma "./bin/pleroma_ctl migrate" sudo -Hu pleroma "./bin/pleroma_ctl migrate"
```
# If you have installed RUM indexes uncommend and run (Optional) If you have installed RUM indexes:
# sudo -Hu pleroma "./bin/pleroma_ctl migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
# Start the instance to verify that everything is working as expected ```sh
sudo -Hu pleroma "./bin/pleroma_ctl migrate --migrations-path priv/repo/optional_migrations/rum_indexing/"
```
Start the instance to verify that everything is working as expected:
```sh
sudo -Hu pleroma "./bin/pleroma daemon" sudo -Hu pleroma "./bin/pleroma daemon"
```
# Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly Wait for about 20 seconds and query the instance endpoint, if it shows your uri, name and email correctly, you are configured correctly:
```sh
sleep 20 && curl http://localhost:4000/api/v1/instance sleep 20 && curl http://localhost:4000/api/v1/instance
```
# Stop the instance Stop the instance:
```sh
sudo -Hu pleroma "./bin/pleroma stop" sudo -Hu pleroma "./bin/pleroma stop"
``` ```

View file

@ -37,6 +37,13 @@ depend() {
need nginx postgresql need nginx postgresql
} }
start_pre() {
ebegin "running migrations if any"
su -s /bin/sh -c "cd \"${directory}\" && exec /usr/bin/mix ecto.migrate" \
"${command_user%%:*}"
eend "$?"
}
healthcheck() { healthcheck() {
# put pleroma_health=YES in /etc/conf.d/pleroma if you want healthchecking # put pleroma_health=YES in /etc/conf.d/pleroma if you want healthchecking
# and make sure you have curl installed # and make sure you have curl installed

View file

@ -6,7 +6,6 @@ After=network.target postgresql.service
ExecReload=/bin/kill $MAINPID ExecReload=/bin/kill $MAINPID
KillMode=process KillMode=process
Restart=on-failure Restart=on-failure
StandardOutput=journal
; Name of the user that runs the Pleroma service. ; Name of the user that runs the Pleroma service.
User=pleroma User=pleroma
@ -15,11 +14,9 @@ Environment="MIX_ENV=prod"
; Make sure that all paths fit your installation. ; Make sure that all paths fit your installation.
; Path to the home directory of the user running the Pleroma service. ; Path to the home directory of the user running the Pleroma service.
Environment="HOME=/home/pleroma" Environment="HOME=/var/lib/pleroma"
; Path to the folder containing the Pleroma installation. ; Path to the folder containing the Pleroma installation.
WorkingDirectory=/home/pleroma/pleroma WorkingDirectory=/opt/pleroma
; Path to the environment file. the file contains RELEASE_COOKIE and etc
;EnvironmentFile=/opt/pleroma/config/pleroma.env
; Path to the Mix binary. ; Path to the Mix binary.
ExecStart=/usr/bin/mix phx.server ExecStart=/usr/bin/mix phx.server
@ -27,7 +24,7 @@ ExecStart=/usr/bin/mix phx.server
; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops. ; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.
PrivateTmp=true PrivateTmp=true
; The /home, /root, and /run/user folders can not be accessed by this service anymore. If your Pleroma user has its home folder in one of the restricted places, or use one of these folders as its working directory, you have to set this to false. ; The /home, /root, and /run/user folders can not be accessed by this service anymore. If your Pleroma user has its home folder in one of the restricted places, or use one of these folders as its working directory, you have to set this to false.
ProtectHome=false ProtectHome=true
; Mount /usr, /boot, and /etc as read-only for processes invoked by this service. ; Mount /usr, /boot, and /etc as read-only for processes invoked by this service.
ProtectSystem=full ProtectSystem=full
; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi. ; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi.

View file

@ -62,7 +62,8 @@ defmodule Mix.Pleroma do
{Majic.Pool, {Majic.Pool,
[name: Pleroma.MajicPool, pool_size: Pleroma.Config.get([:majic_pool, :size], 2)]} [name: Pleroma.MajicPool, pool_size: Pleroma.Config.get([:majic_pool, :size], 2)]}
] ++ ] ++
http_children(adapter) http_children(adapter) ++
search_children()
cachex_children = Enum.map(@cachex_children, &Pleroma.Application.build_cachex(&1, [])) cachex_children = Enum.map(@cachex_children, &Pleroma.Application.build_cachex(&1, []))
@ -141,4 +142,14 @@ defmodule Mix.Pleroma do
end end
defp http_children(_), do: [] defp http_children(_), do: []
defp search_children do
case Pleroma.Config.get([Pleroma.Search, :module]) do
Pleroma.Search.ParadeDB ->
[Pleroma.Search.ParadeDB.Repo]
_ ->
[]
end
end
end end

View file

@ -234,6 +234,64 @@ defmodule Mix.Tasks.Pleroma.Config do
end) end)
end end
# Removes non-whitelisted configuration sections
def run(["filter_whitelisted" | rest]) do
{options, [], []} =
OptionParser.parse(
rest,
strict: [force: :boolean],
aliases: [f: :force]
)
force = Keyword.get(options, :force, false)
start_pleroma()
whitelisted_configs = Pleroma.Config.get(:database_config_whitelist)
if whitelisted_configs in [nil, false] do
shell_error("No unwanted settings in ConfigDB. No changes made.")
else
{whitelisted_groups, whitelisted_keys} = get_filters(whitelisted_configs)
filtered =
from(c in ConfigDB)
|> Repo.all()
|> Enum.filter(&not_whitelisted?(&1, whitelisted_groups, whitelisted_keys))
do_config_filter(filtered, force)
end
end
# Removes blacklisted configuration sections
def run(["filter_blacklisted" | rest]) do
{options, [], []} =
OptionParser.parse(
rest,
strict: [force: :boolean],
aliases: [f: :force]
)
force = Keyword.get(options, :force, false)
start_pleroma()
blacklisted_configs = Pleroma.Config.get(:database_config_blacklist)
if blacklisted_configs in [nil, false] do
shell_error("No unwanted settings in ConfigDB. No changes made.")
else
{blacklisted_groups, blacklisted_keys} = get_filters(blacklisted_configs)
filtered =
from(c in ConfigDB)
|> Repo.all()
|> Enum.filter(&blacklisted?(&1, blacklisted_groups, blacklisted_keys))
do_config_filter(filtered, force)
end
end
@spec migrate_to_db(Path.t() | nil) :: any() @spec migrate_to_db(Path.t() | nil) :: any()
def migrate_to_db(file_path \\ nil) do def migrate_to_db(file_path \\ nil) do
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
@ -277,9 +335,12 @@ defmodule Mix.Tasks.Pleroma.Config do
group group
|> Pleroma.Config.Loader.filter_group(settings) |> Pleroma.Config.Loader.filter_group(settings)
|> Enum.each(fn {key, value} -> |> Enum.each(fn {key, value} ->
if ConfigDB.static?(group, key) do
shell_info("Settings for key #{key} must remain in static configuration; skipping.")
else
{:ok, _} = ConfigDB.update_or_create(%{group: group, key: key, value: value}) {:ok, _} = ConfigDB.update_or_create(%{group: group, key: key, value: value})
shell_info("Settings for key #{key} migrated.") shell_info("Settings for key #{key} migrated.")
end
end) end)
shell_info("Settings for group #{inspect(group)} migrated.") shell_info("Settings for group #{inspect(group)} migrated.")
@ -434,4 +495,50 @@ defmodule Mix.Tasks.Pleroma.Config do
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;") Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;") Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
end end
defp get_filters(config) do
groups =
config
|> Enum.filter(fn
{_group} -> true
_ -> false
end)
|> Enum.map(fn {group} -> group end)
keys =
config
|> Enum.filter(fn
{_group, _key} -> true
_ -> false
end)
{groups, keys}
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
defp blacklisted?(%{group: group, key: key}, blacklisted_groups, blacklisted_keys) do
Enum.member?(blacklisted_groups, group) or
Enum.member?(blacklisted_keys, {group, key})
end
defp do_config_filter(filtered, _) when length(filtered) == 0 do
shell_error("No unwanted settings in ConfigDB. No changes made.")
end
defp do_config_filter(filtered, force) 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
end
end end

View file

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

View file

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

View file

@ -4,9 +4,6 @@
defmodule Mix.Tasks.Pleroma.Search.Indexer do defmodule Mix.Tasks.Pleroma.Search.Indexer do
import Mix.Pleroma import Mix.Pleroma
import Ecto.Query
alias Pleroma.Workers.SearchIndexingWorker
def run(["create_index"]) do def run(["create_index"]) do
start_pleroma() start_pleroma()
@ -35,49 +32,30 @@ defmodule Mix.Tasks.Pleroma.Search.Indexer do
strict: [ strict: [
chunk: :integer, chunk: :integer,
limit: :integer, limit: :integer,
step: :integer step: :integer,
before: :string
] ]
) )
start_pleroma() start_pleroma()
chunk_size = Keyword.get(options, :chunk, 100) result =
limit = Keyword.get(options, :limit, 100_000) Pleroma.Search.Backfill.run(
per_step = Keyword.get(options, :step, 1000) options ++
[
chunks = max(div(limit, per_step), 1) on_page: fn page ->
IO.puts(
1..chunks "Queued #{page.count} activities (#{page.enqueued} total), " <>
|> Enum.each(fn step -> "checkpoint: #{page.next_cursor}"
q = )
from(a in Pleroma.Activity, end
limit: ^per_step, ]
offset: ^per_step * (^step - 1),
select: [:id],
order_by: [desc: :id]
) )
{:ok, ids} = IO.puts("Queued #{result.enqueued} activities")
Pleroma.Repo.transaction(fn ->
Pleroma.Repo.stream(q, timeout: :infinity)
|> Enum.map(fn a ->
a.id
end)
end)
IO.puts("Got #{length(ids)} activities, adding to indexer") unless result.exhausted do
IO.puts("Resume with --before #{result.next_cursor}")
ids end
|> Enum.chunk_every(chunk_size)
|> Enum.each(fn chunk ->
IO.puts("Adding #{length(chunk)} activities to indexing queue")
chunk
|> Enum.map(fn id ->
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => id})
end)
|> Oban.insert_all()
end)
end)
end end
end end

View file

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

View file

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

View file

@ -43,6 +43,7 @@ defmodule Pleroma.Application do
# every time the application is restarted, so we disable module # every time the application is restarted, so we disable module
# conflicts at runtime # conflicts at runtime
Code.compiler_options(ignore_module_conflict: true) Code.compiler_options(ignore_module_conflict: true)
configure_logger()
Pleroma.Telemetry.Logger.attach() Pleroma.Telemetry.Logger.attach()
Config.Holder.save_default() Config.Holder.save_default()
Pleroma.HTML.compile_scrubbers() Pleroma.HTML.compile_scrubbers()
@ -65,6 +66,7 @@ defmodule Pleroma.Application do
if match?({Tesla.Adapter.Finch, _}, adapter) do if match?({Tesla.Adapter.Finch, _}, adapter) do
Logger.info("Starting Finch") Logger.info("Starting Finch")
use_castore_for_otp_cacerts()
Finch.start_link(name: MyFinch) Finch.start_link(name: MyFinch)
end end
@ -80,7 +82,10 @@ defmodule Pleroma.Application do
[ [
Pleroma.PromEx, Pleroma.PromEx,
Pleroma.LDAP, Pleroma.LDAP,
Pleroma.Repo, Pleroma.Repo
] ++
search_children() ++
[
Config.TransferTask, Config.TransferTask,
Pleroma.Emoji, Pleroma.Emoji,
Pleroma.Web.Plugs.RateLimiter.Supervisor, Pleroma.Web.Plugs.RateLimiter.Supervisor,
@ -113,6 +118,44 @@ defmodule Pleroma.Application do
Supervisor.start_link(children, opts) Supervisor.start_link(children, opts)
end end
def configure_logger do
Config.get([:logger, :backends], [])
|> Enum.each(fn backend ->
case backend_to_logger(backend) do
{:ok, logger} ->
add_logger(logger)
{:error, :console_backend} ->
Logger.warning(":console is no longer considered a backend and is enabled by default")
end
end)
end
defp add_logger(backend) do
case LoggerBackends.add(backend) do
{:ok, _} ->
Logger.debug("Successfully added logger backend: #{inspect(backend)}")
{:error, :already_present} ->
:ok
{:error, reason} ->
Logger.error("Failed to add logger backend #{inspect(backend)}: #{inspect(reason)}")
end
end
defp backend_to_logger(:console), do: {:error, :console_backend}
defp backend_to_logger({:ex_syslogger = backend, name}) do
Logger.warning(
"Configuration {:#{backend}, :#{name}} is incorrect. Use {ExSyslogger, :#{name}} instead!"
)
{:ok, {ExSyslogger, name}}
end
defp backend_to_logger(backend), do: {:ok, backend}
def load_custom_modules do def load_custom_modules do
dir = Config.get([:modules, :runtime_dir]) dir = Config.get([:modules, :runtime_dir])
@ -159,6 +202,16 @@ defmodule Pleroma.Application do
] ]
end end
defp search_children do
case Config.get([Pleroma.Search, :module]) do
Pleroma.Search.ParadeDB ->
[Pleroma.Search.ParadeDB.Repo]
_ ->
[]
end
end
defp emoji_packs_expiration, defp emoji_packs_expiration,
do: expiration(default: :timer.seconds(5 * 60), interval: :timer.seconds(60)) do: expiration(default: :timer.seconds(5 * 60), interval: :timer.seconds(60))
@ -252,6 +305,15 @@ defmodule Pleroma.Application do
end end
end end
# Mint trusts the OTP CA store, which comes from the OS. Make it the CAStore
# bundle that the other adapters trust. OTP 24 has no such store and Mint
# uses CAStore there anyway.
defp use_castore_for_otp_cacerts do
if Code.ensure_loaded?(:public_key) and function_exported?(:public_key, :cacerts_load, 1) do
:ok = :public_key.cacerts_load(CAStore.file_path())
end
end
defp http_children_hackney do defp http_children_hackney do
pools = [:federation, :media] pools = [:federation, :media]
@ -278,7 +340,8 @@ defmodule Pleroma.Application do
config = Config.get(ConcurrentLimiter, []) config = Config.get(ConcurrentLimiter, [])
[ [
Pleroma.Search Pleroma.Search,
Pleroma.Webhook.Notify
] ]
|> Enum.each(fn module -> |> Enum.each(fn module ->
mod_config = Keyword.get(config, module, []) mod_config = Keyword.get(config, module, [])

View file

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

View file

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

View file

@ -108,4 +108,6 @@ defmodule Pleroma.Chat do
|> cast(%{pinned: false}, [:pinned]) |> cast(%{pinned: false}, [:pinned])
|> Repo.update() |> Repo.update()
end end
def enabled?, do: Pleroma.Config.get([__MODULE__, :enabled], true)
end end

View file

@ -20,6 +20,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
"\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"} "\n* `config :pleroma, :instance, mrf_transparency_exclusions` is now `config :pleroma, :mrf, transparency_exclusions`"}
] ]
@logger_formatter_config_knobs [:colors, :format, :metadata, :truncate, :utc_log]
def check_exiftool_filter do def check_exiftool_filter do
filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, []) filters = Config.get([Pleroma.Upload]) |> Keyword.get(:filters, [])
@ -218,7 +220,8 @@ defmodule Pleroma.Config.DeprecationWarnings do
check_quarantined_instances_tuples(), check_quarantined_instances_tuples(),
check_transparency_exclusions_tuples(), check_transparency_exclusions_tuples(),
check_simple_policy_tuples(), check_simple_policy_tuples(),
check_exiftool_filter() check_exiftool_filter(),
check_deprecated_logger_config()
] ]
|> Enum.reduce(:ok, fn |> Enum.reduce(:ok, fn
:ok, :ok -> :ok :ok, :ok -> :ok
@ -415,4 +418,72 @@ defmodule Pleroma.Config.DeprecationWarnings do
:ok :ok
end end
end end
defp merge_deprecated_logger_config(config) do
formatter_config = Enum.filter(config, fn {k, _} -> k in @logger_formatter_config_knobs end)
formatter = Logger.default_formatter(formatter_config)
log_level = Keyword.get(config, :level, nil)
Logger.debug("""
Reconfiguring console Logger with deprecated configuration syntax.
Handler configuration:
log_level: #{inspect(log_level)}
Formatter:
#{inspect(formatter)}
""")
if Config.get(:env) != :test do
if log_level, do: :logger.update_handler_config(:default, :level, log_level)
:logger.set_handler_config(:default, :formatter, formatter)
end
end
@spec check_deprecated_logger_config() :: :ok | :error
def check_deprecated_logger_config do
backends_config = Application.get_env(:logger, :backends)
console_config = Application.get_env(:logger, :console)
# NOTE: No need to merge the old backends config since it still works.
# And new configuration will just add new Logger backends.
backend =
if backends_config do
Logger.warning("""
!!!DEPRECATION WARNING!!!
Your configuration is using deprecated syntax for configuring backends of Elixir's logger.
`config :logger, backends: [...]` is deprecated syntax due to changes in Elixir.
Please update your configuration at your earliest convenience to use:
`config :pleroma, :logger, backends: [...]`
Note: `:console` is no longer considered a backend and is used by default, you can disable it using:
`config :logger, :default_handler: false`
""")
true
else
false
end
console =
if console_config do
Logger.warning("""
!!!DEPRECATION WARNING!!!
Your configuration is using deprecated syntax for configuring logging to console.
`config :logger, :console` is deprecated syntax due to changes in Elixir.
Please update your configuration at your earliest convenience to use
`config :logger, default_handler` and `config :logger, :default_formatter`.
Note: `:default_handler` is used only for the `level` setting. All other configurations go under
`:default_formatter`. For more info visit: https://hexdocs.pm/logger/Logger.html#module-backends-and-backwards-compatibility
""")
merge_deprecated_logger_config(console_config)
true
else
false
end
if backend or console, do: :error, else: :ok
end
end end

View file

@ -47,11 +47,12 @@ defmodule Pleroma.Config.TransferTask do
settings = settings =
(Repo.all(ConfigDB) ++ deleted_settings) (Repo.all(ConfigDB) ++ deleted_settings)
|> Enum.reject(&ConfigDB.static?/1)
|> Enum.map(&merge_with_default/1) |> Enum.map(&merge_with_default/1)
started_applications = Application.started_applications() started_applications = Application.started_applications()
reject = [nil, :postgrex] reject = [nil, :postgrex, :logger]
reject = reject =
if restart_pleroma? do if restart_pleroma? do

View file

@ -10,6 +10,7 @@ defmodule Pleroma.ConfigDB do
import Pleroma.Web.Gettext import Pleroma.Web.Gettext
alias __MODULE__ alias __MODULE__
alias Pleroma.EctoType.Config.RateLimit
alias Pleroma.Repo alias Pleroma.Repo
@type t :: %__MODULE__{} @type t :: %__MODULE__{}
@ -22,6 +23,12 @@ defmodule Pleroma.ConfigDB do
{:pleroma, :mrf_keyword, :replace} {:pleroma, :mrf_keyword, :replace}
] ]
@static_config [
{:pleroma, Pleroma.Search},
{:pleroma, Pleroma.Search.ParadeDB},
{:pleroma, Pleroma.Search.ParadeDB.Repo}
]
schema "config" do schema "config" do
field(:key, Pleroma.EctoType.Config.Atom) field(:key, Pleroma.EctoType.Config.Atom)
field(:group, Pleroma.EctoType.Config.Atom) field(:group, Pleroma.EctoType.Config.Atom)
@ -31,16 +38,32 @@ defmodule Pleroma.ConfigDB do
timestamps() timestamps()
end end
@spec get_all_as_keyword() :: keyword() @spec get_all_as_keyword(keyword()) :: keyword()
def get_all_as_keyword do def get_all_as_keyword(options \\ []) do
ConfigDB ConfigDB
|> select([c], {c.group, c.key, c.value}) |> select([c], {c.group, c.key, c.value})
|> Repo.all() |> Repo.all()
|> maybe_reject_static(options)
|> Enum.reduce([], fn {group, key, value}, acc -> |> Enum.reduce([], fn {group, key, value}, acc ->
Keyword.update(acc, group, [{key, value}], &Keyword.merge(&1, [{key, value}])) Keyword.update(acc, group, [{key, value}], &Keyword.merge(&1, [{key, value}]))
end) end)
end end
def static?(%{group: group, key: key}), do: static?(group, key)
def static?(_config), do: false
def static?(group, key) do
Enum.any?(@static_config, fn {static_group, static_key} ->
group in [static_group, inspect(static_group)] and key in [static_key, inspect(static_key)]
end)
end
defp maybe_reject_static(configs, exclude_static: true) do
Enum.reject(configs, fn {group, key, _value} -> static?(group, key) end)
end
defp maybe_reject_static(configs, _options), do: configs
@spec get_all_by_group(atom() | String.t()) :: [t()] @spec get_all_by_group(atom() | String.t()) :: [t()]
def get_all_by_group(group) do def get_all_by_group(group) do
from(c in ConfigDB, where: c.group == ^group) |> Repo.all() from(c in ConfigDB, where: c.group == ^group) |> Repo.all()
@ -60,8 +83,59 @@ defmodule Pleroma.ConfigDB do
|> cast(params, [:key, :group, :value]) |> cast(params, [:key, :group, :value])
|> validate_required([:key, :group, :value]) |> validate_required([:key, :group, :value])
|> unique_constraint(:key, name: :config_group_key_index) |> unique_constraint(:key, name: :config_group_key_index)
|> validate_rate_limit()
end end
defp validate_rate_limit(changeset) do
group = get_field(changeset, :group)
key = get_field(changeset, :key)
if group == :pleroma and key == :rate_limit do
value = get_field(changeset, :value)
case normalize_rate_limit(value) do
{:ok, normalized_value} ->
put_change(changeset, :value, normalized_value)
{:error, {limiter_name, reason}} ->
add_error(
changeset,
:value,
"invalid :rate_limit value for #{inspect(limiter_name)}: #{reason}"
)
end
else
changeset
end
end
defp normalize_rate_limit(nil), do: {:ok, nil}
defp normalize_rate_limit(%{} = value), do: normalize_rate_limit(Map.to_list(value))
defp normalize_rate_limit(value) when is_list(value) do
if Keyword.keyword?(value) do
value
|> Enum.reduce_while({:ok, []}, fn {limiter_name, limiter_value}, {:ok, acc} ->
case RateLimit.cast_with_error(limiter_value) do
{:ok, normalized_limiter_value} ->
{:cont, {:ok, [{limiter_name, normalized_limiter_value} | acc]}}
{:error, reason} ->
{:halt, {:error, {limiter_name, reason}}}
end
end)
|> case do
{:ok, acc} -> {:ok, Enum.reverse(acc)}
{:error, _} = error -> error
end
else
{:error, {:rate_limit, "must be a keyword list"}}
end
end
defp normalize_rate_limit(_), do: {:error, {:rate_limit, "must be a keyword list"}}
defp create(params) do defp create(params) do
%ConfigDB{} %ConfigDB{}
|> changeset(params) |> changeset(params)

View file

@ -22,13 +22,14 @@ defmodule Pleroma.Constants do
"generator", "generator",
"rules", "rules",
"language", "language",
"voters" "voters",
"assigned_account"
] ]
) )
const(static_only_files, const(static_only_files,
do: do:
~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js favicon.png schemas doc embed.js embed.css) ~w(index.html robots.txt static static-fe finmoji emoji packs sounds images instance sw.js sw-pleroma.js schemas doc embed.js embed.css)
) )
const(status_updatable_fields, const(status_updatable_fields,
@ -133,15 +134,11 @@ defmodule Pleroma.Constants do
do: ~r/^[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+\/[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+(; .*)?$/ do: ~r/^[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+\/[^[:cntrl:] ()<>@,;:\\"\/\[\]?=]+(; .*)?$/
) )
const(activity_json_canonical_mime_type, # List of allowed chars in the path segment of a URI
do: "application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"" # unreserved, sub-delims, ":", "@" and "/" allowed as the separator in path
) # https://datatracker.ietf.org/doc/html/rfc3986
const(uri_path_allowed_reserved_chars,
const(activity_json_mime_types, do: ~c"!$&'()*+,;=/:@"
do: [
"application/ld+json; profile=\"https://www.w3.org/ns/activitystreams\"",
"application/activity+json"
]
) )
const(upload_object_types, do: ["Document", "Image"]) const(upload_object_types, do: ["Document", "Image"])

View file

@ -12,13 +12,27 @@ defmodule Pleroma.Docs.Generator do
@spec list_behaviour_implementations(behaviour :: module()) :: [module()] @spec list_behaviour_implementations(behaviour :: module()) :: [module()]
def list_behaviour_implementations(behaviour) do def list_behaviour_implementations(behaviour) do
:code.all_loaded() # :code.all_loaded() no longer works on Elixir 1.19 and newer since modules are lazily loaded.
|> Enum.filter(fn {module, _} -> # We can get a complete list of modules by interating over Application.loaded_applications/0,
# but that includes every module which would get loaded below, which is very slow.
# Hardcode the Pleroma application instead, which is good enough for now.
application_modules =
case :application.get_key(:pleroma, :modules) do
{:ok, modules} -> modules
:undefined -> []
end
# At compile time or in tests some modules will be outside of the Pleroma application.
loaded_modules = Enum.map(:code.all_loaded(), fn {module, _} -> module end)
(application_modules ++ loaded_modules)
|> Enum.uniq()
|> Enum.filter(fn module ->
Code.ensure_loaded(module)
# This shouldn't be needed as all modules are expected to have module_info/1, # This shouldn't be needed as all modules are expected to have module_info/1,
# but in test environments some transient modules `:elixir_compiler_XX` # but in test environments some transient modules `:elixir_compiler_XX`
# are loaded for some reason (where XX is a random integer). # are loaded for some reason (where XX is a random integer).
Code.ensure_loaded(module)
if function_exported?(module, :module_info, 1) do if function_exported?(module, :module_info, 1) do
module.module_info(:attributes) module.module_info(:attributes)
|> Keyword.get_values(:behaviour) |> Keyword.get_values(:behaviour)
@ -26,7 +40,6 @@ defmodule Pleroma.Docs.Generator do
|> Enum.member?(behaviour) |> Enum.member?(behaviour)
end end
end) end)
|> Enum.map(fn {module, _} -> module end)
end end
@doc """ @doc """

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

@ -36,10 +36,11 @@ defmodule Pleroma.Emails.Mailer do
def deliver(email, config \\ []) def deliver(email, config \\ [])
def deliver(email, config) do def deliver(email, config) do
# temporary hackney fix until hackney max_connections bug is fixed # Swoosh HTTP adapters use Hackney, which otherwise trusts its certifi bundle
# https://git.pleroma.social/pleroma/pleroma/-/issues/2101
email = email =
Swoosh.Email.put_private(email, :hackney_options, ssl_options: [versions: [:"tlsv1.2"]]) Swoosh.Email.put_private(email, :hackney_options,
ssl_options: Pleroma.HTTP.TLS.ssl_options()
)
case enabled?() do case enabled?() do
true -> Swoosh.Mailer.deliver(email, parse_config(config)) true -> Swoosh.Mailer.deliver(email, parse_config(config))
@ -67,6 +68,21 @@ defmodule Pleroma.Emails.Mailer do
end end
defp parse_config(config) do defp parse_config(config) do
Swoosh.Mailer.parse_config(@otp_app, __MODULE__, @mailer_config, config) @otp_app
|> Swoosh.Mailer.parse_config(__MODULE__, @mailer_config, config)
|> put_mua_cacertfile()
end
@doc false
# Mua otherwise trusts the OS CA store. Keep a CA that the admin set.
def put_mua_cacertfile(config) do
ssl = Keyword.get(config, :ssl, [])
if config[:adapter] == Swoosh.Adapters.Mua and
not Keyword.has_key?(ssl, :cacertfile) and not Keyword.has_key?(ssl, :cacerts) do
Keyword.put(config, :ssl, [{:cacertfile, CAStore.file_path()} | ssl])
else
config
end
end end
end end

View file

@ -258,10 +258,16 @@ defmodule Pleroma.FollowingRelationship do
[_, user_actor: user_actor], [_, user_actor: user_actor],
fragment( fragment(
# "(actor's domain NOT in domain_blocks) OR (actor IS in followed AP IDs)" # "(actor's domain NOT in domain_blocks) OR (actor IS in followed AP IDs)"
#
# ARRAY(SELECT ...) makes the followed-ids lookup an InitPlan that is
# evaluated and costed once. As a plain subselect, the planner charges
# its startup cost on every rescan of the actor lookup, which inflates
# the cost of the index plan enough for it to lose to a plan that
# materialises every active user for every candidate row.
""" """
NOT (substring(? from '.*://([^/]*)') = ANY(?)) OR NOT (substring(? from '.*://([^/]*)') = ANY(?)) OR
? = ANY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr ? = ANY(ARRAY(SELECT ap_id FROM users AS u INNER JOIN following_relationships AS fr
ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = ?) ON u.id = fr.following_id WHERE fr.follower_id = ? AND fr.state = ?))
""", """,
user_actor.ap_id, user_actor.ap_id,
^user.domain_blocks, ^user.domain_blocks,

View file

@ -3,12 +3,15 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Formatter do defmodule Pleroma.Formatter do
require Logger
alias Pleroma.HTML alias Pleroma.HTML
alias Pleroma.User alias Pleroma.User
@safe_mention_regex ~r/^(\s*(?<mentions>(@.+?\s+){1,})+)(?<rest>.*)/s @safe_mention_regex ~r/^(\s*(?<mentions>(@.+?\s+){1,})+)(?<rest>.*)/s
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui @link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/ @markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
@markdown_void_elements ~w[area base br col command embed hr img input keygen link meta param source track wbr]
defp linkify_opts do defp linkify_opts do
Pleroma.Config.get(Pleroma.Formatter) ++ Pleroma.Config.get(Pleroma.Formatter) ++
@ -124,7 +127,157 @@ defmodule Pleroma.Formatter do
end end
def markdown_to_html(text) do def markdown_to_html(text) do
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false}) markdown_to_html(text, %{})
end
def markdown_to_html(text, opts) do
breaks = Map.get(opts, :breaks, false)
{_status, ast, _messages} = EarmarkParser.as_ast(text, breaks: breaks, gfm: true)
ast
|> maybe_strip_email_autolinks(text, Map.get(opts, :strip_email_autolinks, false))
|> render_markdown_nodes(false)
|> IO.iodata_to_binary()
rescue
exception ->
Logger.warning("Markdown rendering failed in #{inspect(exception.__struct__)}")
markdown_fallback(text, Map.get(opts, :breaks, false))
end
defp markdown_fallback(text, breaks) do
text = text |> escape_html() |> IO.iodata_to_binary() |> maybe_render_markdown_breaks(breaks)
"<p>" <> text <> "</p>"
end
defp maybe_render_markdown_breaks(text, true), do: String.replace(text, ~r/\r?\n/, "<br>")
defp maybe_render_markdown_breaks(text, false), do: text
defp maybe_strip_email_autolinks(ast, _text, false), do: ast
defp maybe_strip_email_autolinks(ast, text, true) do
emails =
~r/<([^ >]+@[^ >]+)>/
|> Regex.scan(text, capture: :all_but_first)
|> Enum.map(fn [email] -> String.replace_prefix(email, "mailto:", "") end)
|> MapSet.new()
strip_email_autolinks(ast, emails)
end
defp strip_email_autolinks(nodes, emails) when is_list(nodes) do
Enum.map(nodes, &strip_email_autolinks(&1, emails))
end
defp strip_email_autolinks({"a", attrs, [email], _meta} = node, emails)
when is_binary(email) do
if List.keyfind(attrs, "href", 0) == {"href", "mailto:" <> email} and
MapSet.member?(emails, email) do
email
else
node
end
end
defp strip_email_autolinks({tag, attrs, children, meta}, emails) do
{tag, attrs, strip_email_autolinks(children, emails), meta}
end
defp strip_email_autolinks(text, _emails) when is_binary(text), do: text
defp render_markdown_nodes(nodes, verbatim) when is_list(nodes) do
Enum.map(nodes, &render_markdown_node(&1, verbatim))
end
defp render_markdown_node(text, true) when is_binary(text),
do: strip_dangerous_markdown_html(text)
defp render_markdown_node(text, false) when is_binary(text) do
text
|> strip_dangerous_markdown_html()
|> escape_markdown_html()
end
defp render_markdown_node({:comment, _attrs, _children, _meta}, _verbatim), do: []
defp render_markdown_node({tag, attrs, children, meta}, verbatim) do
normalized_tag = String.downcase(tag)
if normalized_tag in ["script", "style"] do
[]
else
render_markdown_element(tag, normalized_tag, attrs, children, meta, verbatim)
end
end
defp render_markdown_element("code", _normalized_tag, attrs, children, _meta, _verbatim) do
["<code", render_markdown_attrs(attrs), ">", render_markdown_code(children), "</code>"]
end
defp render_markdown_element(tag, "hr", attrs, _children, _meta, _verbatim) do
attrs = Enum.reject(attrs, &match?({"class", class} when class in ~w[thin medium thick], &1))
["<", tag, render_markdown_attrs(attrs), "/>"]
end
defp render_markdown_element(tag, normalized_tag, attrs, _children, _meta, _verbatim)
when normalized_tag in @markdown_void_elements do
["<", tag, render_markdown_attrs(attrs), ">"]
end
defp render_markdown_element(tag, _normalized_tag, attrs, children, meta, verbatim) do
children_verbatim = verbatim or Map.get(meta, :verbatim, false)
[
"<",
tag,
render_markdown_attrs(attrs),
">",
render_markdown_nodes(children, children_verbatim),
"</",
tag,
">"
]
end
defp render_markdown_attrs(attrs) do
Enum.map(attrs, fn {name, value} ->
value = value |> to_string() |> HtmlEntities.decode() |> sanitize_markdown_url(name)
[" ", to_string(name), "=\"", escape_html(value), "\""]
end)
end
defp render_markdown_code(children) do
Enum.map(children, &escape_html/1)
end
defp sanitize_markdown_url(value, name) do
if String.downcase(to_string(name)) in ["href", "src"] do
# Percent-encode instead of cutting, so the URL stays complete but can
# never close the attribute or open a tag.
String.replace(value, ~r/["<>]/, fn
"\"" -> "%22"
"<" -> "%3C"
">" -> "%3E"
end)
else
value
end
end
defp strip_dangerous_markdown_html(text) do
text
|> String.replace(~r/<(script|style)\b[^>]*>.*?<\/\1>/is, "")
|> String.replace(~r/<(script|style)\b[^>]*>.*\z/is, "")
|> String.replace(~r/<\/(script|style)\s*>/i, "")
end
defp escape_markdown_html(text) do
text |> HtmlEntities.decode() |> escape_html()
end
defp escape_html(text) do
{:safe, escaped} = Phoenix.HTML.html_escape(text)
escaped
end end
def html_escape({text, mentions, hashtags}, type) do def html_escape({text, mentions, hashtags}, type) do
@ -135,6 +288,10 @@ defmodule Pleroma.Formatter do
HTML.filter_tags(text) HTML.filter_tags(text)
end end
def html_escape(text, "text/x.misskeymarkdown") do
HTML.filter_tags(text)
end
def html_escape(text, "text/plain") do def html_escape(text, "text/plain") do
Regex.split(@link_regex, text, include_captures: true) Regex.split(@link_regex, text, include_captures: true)
|> Enum.map_every(2, fn chunk -> |> Enum.map_every(2, fn chunk ->

View file

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

View file

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

View file

@ -6,9 +6,12 @@ defmodule Pleroma.Gun do
@callback open(charlist(), pos_integer(), map()) :: {:ok, pid()} @callback open(charlist(), pos_integer(), map()) :: {:ok, pid()}
@callback info(pid()) :: map() @callback info(pid()) :: map()
@callback close(pid()) :: :ok @callback close(pid()) :: :ok
@callback cancel(pid(), reference() | [reference()]) :: :ok
@callback await_up(pid, pos_integer()) :: {:ok, atom()} | {:error, atom()} @callback await_up(pid, pos_integer()) :: {:ok, atom()} | {:error, atom()}
@callback connect(pid(), map()) :: reference() @callback connect(pid(), map()) :: reference()
@callback await(pid(), reference()) :: {:response, :fin, 200, []} @callback await(pid(), reference()) :: tuple()
@callback await_tunnel_up(pid(), reference() | :undefined, timeout()) ::
{:ok, atom()} | {:error, term()}
@callback set_owner(pid(), pid()) :: :ok @callback set_owner(pid(), pid()) :: :ok
defp api, do: Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API) defp api, do: Pleroma.Config.get([Pleroma.Gun], Pleroma.Gun.API)
@ -19,11 +22,15 @@ defmodule Pleroma.Gun do
def close(pid), do: api().close(pid) def close(pid), do: api().close(pid)
def cancel(pid, stream), do: api().cancel(pid, stream)
def await_up(pid, timeout \\ 5_000), do: api().await_up(pid, timeout) def await_up(pid, timeout \\ 5_000), do: api().await_up(pid, timeout)
def connect(pid, opts), do: api().connect(pid, opts) def connect(pid, opts), do: api().connect(pid, opts)
def await(pid, ref), do: api().await(pid, ref) def await(pid, ref), do: api().await(pid, ref)
def await_tunnel_up(pid, ref, timeout), do: api().await_tunnel_up(pid, ref, timeout)
def set_owner(pid, owner), do: api().set_owner(pid, owner) def set_owner(pid, owner), do: api().set_owner(pid, owner)
end end

View file

@ -32,6 +32,9 @@ defmodule Pleroma.Gun.API do
@impl Gun @impl Gun
defdelegate close(pid), to: :gun defdelegate close(pid), to: :gun
@impl Gun
defdelegate cancel(pid, stream), to: :gun
@impl Gun @impl Gun
defdelegate await_up(pid, timeout \\ 5_000), to: :gun defdelegate await_up(pid, timeout \\ 5_000), to: :gun
@ -41,6 +44,28 @@ defmodule Pleroma.Gun.API do
@impl Gun @impl Gun
defdelegate await(pid, ref), to: :gun defdelegate await(pid, ref), to: :gun
@impl Gun
def await_tunnel_up(pid, stream_ref, timeout) do
monitor_ref = Process.monitor(pid)
result =
receive do
{:gun_tunnel_up, ^pid, ^stream_ref, protocol} ->
{:ok, protocol}
{:gun_down, ^pid, _protocol, reason, _killed_streams} ->
{:error, {:down, reason}}
{:DOWN, ^monitor_ref, :process, ^pid, reason} ->
{:error, {:down, reason}}
after
timeout -> {:error, :timeout}
end
Process.demonitor(monitor_ref, [:flush])
result
end
@impl Gun @impl Gun
defdelegate set_owner(pid, owner), to: :gun defdelegate set_owner(pid, owner), to: :gun
end end

View file

@ -42,18 +42,34 @@ defmodule Pleroma.Gun.Conn do
Map.put(opts, :tls_opts, tls_opts) Map.put(opts, :tls_opts, tls_opts)
end end
defp do_open(%URI{scheme: "http"} = uri, %{proxy: {proxy_host, proxy_port}} = opts) do
with open_opts <- opts |> proxy_open_opts() |> Map.put(:protocols, [:http]),
{:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
{:ok, protocol} <- await_up(conn, opts[:connect_timeout]) do
{:ok, conn, protocol, :forward_proxy}
else
error ->
Logger.warning(
"Opening forward proxy connection to #{compose_uri_log(uri)} failed with error #{inspect(error)}"
)
error
end
end
defp do_open(uri, %{proxy: {proxy_host, proxy_port}} = opts) do defp do_open(uri, %{proxy: {proxy_host, proxy_port}} = opts) do
connect_opts = connect_opts =
uri uri
|> destination_opts() |> destination_opts()
|> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, [])) |> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
|> add_proxy_auth(opts)
with open_opts <- Map.delete(opts, :tls_opts), with open_opts <- proxy_open_opts(opts),
{:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts), {:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]), {:ok, _proxy_protocol} <- await_up(conn, opts[:connect_timeout]) do
stream <- Gun.connect(conn, connect_opts), conn
{:response, :fin, 200, _} <- Gun.await(conn, stream) do |> Gun.connect(connect_opts)
{:ok, conn, protocol} |> await_proxy_connect(conn, opts[:connect_timeout])
else else
error -> error ->
Logger.warning( Logger.warning(
@ -64,30 +80,29 @@ defmodule Pleroma.Gun.Conn do
end end
end end
defp do_open(uri, %{proxy: {proxy_type, proxy_host, proxy_port}} = opts) do defp do_open(_uri, %{proxy: {:socks4, _proxy_host, _proxy_port}}) do
version = {:error, :socks4_unsupported}
proxy_type
|> to_string()
|> String.last()
|> case do
"4" -> 4
_ -> 5
end end
defp do_open(uri, %{proxy: {proxy_type, proxy_host, proxy_port}} = opts)
when proxy_type in [:socks, :socks5] do
socks_opts = socks_opts =
uri uri
|> destination_opts() |> destination_opts()
|> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, [])) |> add_http2_opts(uri.scheme, Map.get(opts, :tls_opts, []))
|> Map.put(:version, version) |> Map.put(:version, 5)
|> add_socks_proxy_auth(opts)
opts = open_opts =
opts opts
|> proxy_open_opts()
|> Map.put(:protocols, [:socks]) |> Map.put(:protocols, [:socks])
|> Map.put(:socks_opts, socks_opts) |> Map.put(:socks_opts, socks_opts)
with {:ok, conn} <- Gun.open(proxy_host, proxy_port, opts), with {:ok, conn} <- Gun.open(proxy_host, proxy_port, open_opts),
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do {:ok, :socks} <- await_up(conn, opts[:connect_timeout]),
{:ok, conn, protocol} {:ok, protocol} <- await_tunnel_up(conn, :undefined, opts[:connect_timeout]) do
{:ok, conn, protocol, nil}
else else
error -> error ->
Logger.warning( Logger.warning(
@ -100,10 +115,11 @@ defmodule Pleroma.Gun.Conn do
defp do_open(%URI{host: host, port: port} = uri, opts) do defp do_open(%URI{host: host, port: port} = uri, opts) do
host = Pleroma.HTTP.AdapterHelper.parse_host(host) host = Pleroma.HTTP.AdapterHelper.parse_host(host)
opts = Map.put(opts, :transport, transport(uri.scheme))
with {:ok, conn} <- Gun.open(host, port, opts), with {:ok, conn} <- Gun.open(host, port, opts),
{:ok, protocol} <- Gun.await_up(conn, opts[:connect_timeout]) do {:ok, protocol} <- await_up(conn, opts[:connect_timeout]) do
{:ok, conn, protocol} {:ok, conn, protocol, nil}
else else
error -> error ->
Logger.warning( Logger.warning(
@ -120,11 +136,90 @@ defmodule Pleroma.Gun.Conn do
end end
defp add_http2_opts(opts, "https", tls_opts) do defp add_http2_opts(opts, "https", tls_opts) do
Map.merge(opts, %{protocols: [:http2], transport: :tls, tls_opts: tls_opts}) Map.merge(opts, %{protocols: [:http2, :http], transport: :tls, tls_opts: tls_opts})
end end
defp add_http2_opts(opts, _, _), do: opts defp add_http2_opts(opts, _, _), do: opts
defp add_proxy_auth(connect_opts, %{proxy_auth: {username, password}})
when is_binary(username) and is_binary(password) do
Map.merge(connect_opts, %{username: username, password: password})
end
defp add_proxy_auth(connect_opts, _), do: connect_opts
defp add_socks_proxy_auth(socks_opts, %{proxy_auth: {username, password}})
when is_binary(username) and is_binary(password) do
Map.put(socks_opts, :auth, [{:username_password, username, password}])
end
defp add_socks_proxy_auth(socks_opts, _), do: socks_opts
defp proxy_open_opts(opts) do
proxy_tls_opts = Map.get(opts, :proxy_tls_opts, [])
opts =
opts
|> Map.drop([:proxy, :proxy_auth, :proxy_tls_opts, :tls_opts])
|> Map.put_new(:transport, :tcp)
if opts.transport == :tls do
Map.put(opts, :tls_opts, proxy_tls_opts)
else
opts
end
end
defp await_up(conn, timeout) do
case Gun.await_up(conn, timeout) do
{:ok, protocol} ->
{:ok, protocol}
error ->
Gun.close(conn)
error
end
end
defp await_proxy_connect(stream, conn, timeout) do
case Gun.await(conn, stream) do
{:response, :fin, 200, _headers} ->
case await_tunnel_up(conn, stream, timeout) do
{:ok, protocol} -> {:ok, conn, protocol, stream}
error -> error
end
{:response, _fin, 407, _headers} ->
close_with_error(conn, :proxy_auth_failed)
{:response, _fin, status, _headers} when is_integer(status) ->
close_with_error(conn, {:proxy_connect_failed, status})
error ->
Gun.close(conn)
error
end
end
defp close_with_error(conn, reason) do
Gun.close(conn)
{:error, reason}
end
defp transport("https"), do: :tls
defp transport(_), do: :tcp
defp await_tunnel_up(conn, stream_ref, timeout) do
case Gun.await_tunnel_up(conn, stream_ref, timeout) do
{:ok, protocol} ->
{:ok, protocol}
error ->
Gun.close(conn)
error
end
end
def compose_uri_log(%URI{scheme: scheme, host: host, path: path}) do def compose_uri_log(%URI{scheme: scheme, host: host, path: path}) do
"#{scheme}://#{host}#{path}" "#{scheme}://#{host}#{path}"
end end

View file

@ -4,53 +4,132 @@
defmodule Pleroma.Gun.ConnectionPool do defmodule Pleroma.Gun.ConnectionPool do
@registry __MODULE__ @registry __MODULE__
@locks Module.concat(__MODULE__, Locks)
alias Pleroma.Gun.ConnectionPool.WorkerSupervisor alias Pleroma.Gun.ConnectionPool.WorkerSupervisor
@connection_option_keys [
:http_opts,
:http2_opts,
:protocols,
:proxy,
:proxy_auth,
:proxy_tls_opts,
:retry,
:retry_timeout,
:socks_opts,
:tcp_opts,
:tls_opts,
:transport,
:ws_opts
]
def children do def children do
[ [
{Registry, keys: :unique, name: @registry}, {Registry, keys: :duplicate, name: @registry},
{Registry, keys: :unique, name: @locks},
Pleroma.Gun.ConnectionPool.WorkerSupervisor Pleroma.Gun.ConnectionPool.WorkerSupervisor
] ]
end end
@spec get_conn(URI.t(), keyword()) :: {:ok, pid()} | {:error, term()} @spec get_conn(URI.t(), keyword()) :: {:ok, pid()} | {:error, term()}
def get_conn(uri, opts) do def get_conn(uri, opts) do
key = "#{uri.scheme}:#{uri.host}:#{uri.port}" key = connection_key(uri, opts)
case Registry.lookup(@registry, key) do case checkout_existing(Registry.lookup(@registry, key)) do
# The key has already been registered, but connection is not up yet {:ok, gun_pid} ->
[{worker_pid, nil}] ->
get_gun_pid_from_worker(worker_pid, true)
[{worker_pid, {gun_pid, _used_by, _crf, _last_reference}}] ->
GenServer.call(worker_pid, :add_client)
{:ok, gun_pid} {:ok, gun_pid}
[] -> :not_available ->
# :gun.set_owner fails in :connected state for whatevever reason, provision_or_wait(key, uri, opts)
# so we open the connection in the process directly and send it's pid back end
# We trust gun to handle timeouts by itself end
defp checkout_existing([]), do: :not_available
defp checkout_existing([{_worker_pid, nil} | workers]), do: checkout_existing(workers)
defp checkout_existing([{worker_pid, _value} | workers]) do
case checkout_worker(worker_pid) do
{:ok, gun_pid} -> {:ok, gun_pid}
_ -> checkout_existing(workers)
end
end
defp provision_or_wait(key, uri, opts) do
result = with_route_lock(key, fn -> provision(key, uri, opts) end)
case result do
{:ok, gun_pid} ->
{:ok, gun_pid}
{:new, worker_pid} ->
get_gun_pid_from_worker(worker_pid)
{:wait, worker_pid} ->
case checkout_worker(worker_pid, :infinity) do
{:ok, gun_pid} -> {:ok, gun_pid}
:busy -> get_conn(uri, opts)
error -> error
end
error ->
error
end
end
defp provision(key, uri, opts) do
workers = Registry.lookup(@registry, key)
case checkout_existing(workers) do
{:ok, gun_pid} ->
{:ok, gun_pid}
:not_available ->
case Enum.find(workers, fn {_worker_pid, value} -> is_nil(value) end) do
{worker_pid, nil} -> {:wait, worker_pid}
nil -> start_worker(key, uri, opts)
end
end
end
defp with_route_lock(key, function) do
case Registry.register(@locks, key, nil) do
{:ok, _} ->
try do
function.()
after
Registry.unregister(@locks, key)
end
{:error, {:already_registered, _owner}} ->
Process.sleep(1)
with_route_lock(key, function)
end
end
defp start_worker(key, uri, opts) do
# :gun.set_owner fails in :connected state, so the worker opens the connection itself.
case WorkerSupervisor.start_worker([key, uri, opts, self()]) do case WorkerSupervisor.start_worker([key, uri, opts, self()]) do
{:ok, worker_pid} -> {:ok, worker_pid} -> {:new, worker_pid}
get_gun_pid_from_worker(worker_pid, false) error -> error
{:error, {:already_started, worker_pid}} ->
get_gun_pid_from_worker(worker_pid, true)
err ->
err
end
end end
end end
defp get_gun_pid_from_worker(worker_pid, register) do defp checkout_worker(worker_pid, timeout \\ 5_000) do
try do
GenServer.call(worker_pid, :checkout, timeout)
catch
:exit, reason -> {:error, {:connection_worker_exit, reason}}
end
end
defp get_gun_pid_from_worker(worker_pid) do
# GenServer.call will block the process for timeout length if # GenServer.call will block the process for timeout length if
# the server crashes on startup (which will happen if gun fails to connect) # the server crashes on startup (which will happen if gun fails to connect)
# so instead we use cast + monitor # so instead we use cast + monitor
ref = Process.monitor(worker_pid) ref = Process.monitor(worker_pid)
if register, do: GenServer.cast(worker_pid, {:add_client, self()})
receive do receive do
{:conn_pid, pid} -> {:conn_pid, pid} ->
@ -68,19 +147,115 @@ defmodule Pleroma.Gun.ConnectionPool do
@spec release_conn(pid()) :: :ok @spec release_conn(pid()) :: :ok
def release_conn(conn_pid) do def release_conn(conn_pid) do
# :ets.fun2ms(fn {_, {worker_pid, {gun_pid, _, _, _}}} when gun_pid == conn_pid -> # :ets.fun2ms(fn {_, {worker_pid, {gun_pid, _tunnel}}} when gun_pid == conn_pid ->
# worker_pid end) # worker_pid end)
query_result = query_result =
Registry.select(@registry, [ Registry.select(@registry, [
{{:_, :"$1", {:"$2", :_, :_, :_}}, [{:==, :"$2", conn_pid}], [:"$1"]} {{:_, :"$1", {:"$2", :_}}, [{:==, :"$2", conn_pid}], [:"$1"]}
]) ])
case query_result do case query_result do
[worker_pid] -> [worker_pid] ->
try do
GenServer.call(worker_pid, :remove_client) GenServer.call(worker_pid, :remove_client)
catch
:exit, _ -> :ok
end
[] -> [] ->
:ok :ok
end end
end end
@spec discard_conn(pid()) :: :ok
def discard_conn(conn_pid) do
case worker_for_conn(conn_pid) do
nil ->
:ok
worker_pid ->
DynamicSupervisor.terminate_child(WorkerSupervisor, worker_pid)
end
end
@spec cancel_stream(pid(), reference() | [reference()]) :: :ok
def cancel_stream(conn_pid, stream) do
case worker_for_conn(conn_pid) do
nil ->
Pleroma.Gun.cancel(conn_pid, stream)
worker_pid ->
try do
GenServer.call(worker_pid, {:cancel_stream, stream})
catch
:exit, _ -> :ok
end
end
end
@spec register_stream(pid(), reference() | [reference()]) :: :ok
def register_stream(conn_pid, stream) do
call_worker(conn_pid, {:register_stream, stream})
end
@spec finish_stream(pid(), reference() | [reference()]) :: :ok
def finish_stream(conn_pid, stream) do
call_worker(conn_pid, {:finish_stream, stream})
end
@spec release_stream(pid(), reference() | [reference()]) :: :ok
def release_stream(conn_pid, stream) do
call_worker(conn_pid, {:release_stream, stream})
end
@spec tunnel_ref(pid()) :: reference() | :forward_proxy | nil
def tunnel_ref(conn_pid) do
case Registry.select(@registry, [
{{:_, :_, {:"$1", :"$2"}}, [{:==, :"$1", conn_pid}], [:"$2"]}
]) do
[tunnel] -> tunnel
[] -> nil
end
end
defp worker_for_conn(conn_pid) do
case Registry.select(@registry, [
{{:_, :"$1", {:"$2", :_}}, [{:==, :"$2", conn_pid}], [:"$1"]}
]) do
[worker_pid] -> worker_pid
[] -> nil
end
end
defp call_worker(conn_pid, message) do
case worker_for_conn(conn_pid) do
nil ->
:ok
worker_pid ->
try do
GenServer.call(worker_pid, message)
catch
:exit, _ -> :ok
end
end
end
defp connection_key(uri, opts) do
origin = {
String.downcase(uri.scheme),
String.downcase(uri.host),
uri.port
}
profile =
opts
|> Map.new()
|> Map.take(@connection_option_keys)
|> :erlang.term_to_binary()
|> then(&:crypto.hash(:sha256, &1))
fingerprint = Base.encode16(profile, case: :lower)
Enum.join(Tuple.to_list(origin) ++ [fingerprint], ":")
end
end end

View file

@ -9,11 +9,11 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
def start_monitor do def start_monitor do
pid = pid =
case GenServer.start(__MODULE__, [], name: {:via, Registry, {registry(), "reclaimer"}}) do case GenServer.start(__MODULE__, [], name: __MODULE__) do
{:ok, pid} -> {:ok, pid} ->
pid pid
{:error, {:already_registered, pid}} -> {:error, {:already_started, pid}} ->
pid pid
end end
@ -41,17 +41,16 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
reclaim_max: reclaim_max reclaim_max: reclaim_max
}) })
# :ets.fun2ms( workers =
# fn {_, {worker_pid, {_, used_by, crf, last_reference}}} when used_by == [] ->
# {worker_pid, crf, last_reference} end)
unused_conns =
Registry.select( Registry.select(
registry(), registry(),
[ [
{{:_, :"$1", {:_, :"$2", :"$3", :"$4"}}, [{:==, :"$2", []}], [{{:"$1", :"$3", :"$4"}}]} {{:_, :"$1", {:_, :_}}, [], [:"$1"]}
] ]
) )
unused_conns = Enum.flat_map(workers, &reclaim_info/1)
case unused_conns do case unused_conns do
[] -> [] ->
:telemetry.execute( :telemetry.execute(
@ -65,25 +64,43 @@ defmodule Pleroma.Gun.ConnectionPool.Reclaimer do
{:stop, :no_unused_conns, nil} {:stop, :no_unused_conns, nil}
unused_conns -> unused_conns ->
reclaimed = reclaimed_count =
unused_conns unused_conns
|> Enum.sort(fn {_pid1, crf1, last_reference1}, {_pid2, crf2, last_reference2} -> |> Enum.sort_by(fn {_pid, crf, last_reference} -> {crf, last_reference} end)
crf1 <= crf2 and last_reference1 <= last_reference2 |> Enum.reduce_while(0, fn
end) _worker, count when count == reclaim_max ->
|> Enum.take(reclaim_max) {:halt, count}
reclaimed {worker_pid, _crf, _last_reference}, count ->
|> Enum.each(fn {pid, _, _} -> if reclaim(worker_pid), do: {:cont, count + 1}, else: {:cont, count}
DynamicSupervisor.terminate_child(Pleroma.Gun.ConnectionPool.WorkerSupervisor, pid)
end) end)
:telemetry.execute( :telemetry.execute(
[:pleroma, :connection_pool, :reclaim, :stop], [:pleroma, :connection_pool, :reclaim, :stop],
%{reclaimed_count: Enum.count(reclaimed)}, %{reclaimed_count: reclaimed_count},
%{max_connections: max_connections} %{max_connections: max_connections}
) )
{:stop, :normal, nil} {:stop, :normal, nil}
end end
end end
defp reclaim_info(worker_pid) do
try do
case GenServer.call(worker_pid, :reclaim_info) do
{:idle, crf, last_reference} -> [{worker_pid, crf, last_reference}]
:in_use -> []
end
catch
:exit, _ -> []
end
end
defp reclaim(worker_pid) do
try do
GenServer.call(worker_pid, :reclaim) == :reclaimed
catch
:exit, _ -> false
end
end
end end

View file

@ -8,25 +8,22 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
defp registry, do: Pleroma.Gun.ConnectionPool defp registry, do: Pleroma.Gun.ConnectionPool
def start_link([key | _] = opts) do def start_link(opts), do: GenServer.start_link(__MODULE__, opts)
GenServer.start_link(__MODULE__, opts, name: {:via, Registry, {registry(), key}})
end
@impl true @impl true
def init([_key, _uri, _opts, _client_pid] = opts) do def init([key, _uri, _opts, _client_pid] = opts) do
{:ok, _} = Registry.register(registry(), key, nil)
{:ok, nil, {:continue, {:connect, opts}}} {:ok, nil, {:continue, {:connect, opts}}}
end end
@impl true @impl true
def handle_continue({:connect, [key, uri, opts, client_pid]}, _) do def handle_continue({:connect, [key, uri, opts, client_pid]}, _) do
with {:ok, conn_pid, protocol} <- Gun.Conn.open(uri, opts), with {:ok, conn_pid, protocol, tunnel} <- Gun.Conn.open(uri, opts),
Process.link(conn_pid) do Process.link(conn_pid) do
time = :erlang.monotonic_time(:millisecond) time = :erlang.monotonic_time(:millisecond)
{_, _} = Registry.unregister(registry(), key)
Registry.update_value(registry(), key, fn _ -> {:ok, _} = Registry.register(registry(), key, {conn_pid, tunnel})
{conn_pid, [client_pid], 1, time}
end)
send(client_pid, {:conn_pid, conn_pid}) send(client_pid, {:conn_pid, conn_pid})
@ -34,8 +31,14 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
%{ %{
key: key, key: key,
timer: nil, timer: nil,
client_monitors: %{client_pid => Process.monitor(client_pid)}, clients: %{
protocol: protocol client_pid => %{count: 1, monitor: Process.monitor(client_pid), streams: MapSet.new()}
},
conn_pid: conn_pid,
protocol: protocol,
proxied: Keyword.has_key?(opts, :proxy),
crf: 1,
last_reference: time
}, :hibernate} }, :hibernate}
else else
err -> err ->
@ -44,106 +47,225 @@ defmodule Pleroma.Gun.ConnectionPool.Worker do
end end
@impl true @impl true
def handle_cast({:add_client, client_pid}, state) do def handle_call(:checkout, _from, %{protocol: protocol, clients: clients} = state)
case handle_call(:add_client, {client_pid, nil}, state) do when protocol != :http2 and map_size(clients) > 0 do
{:reply, conn_pid, state, :hibernate} -> {:reply, :busy, state, :hibernate}
send(client_pid, {:conn_pid, conn_pid})
{:noreply, state, :hibernate}
end
end end
@impl true def handle_call(:checkout, {client_pid, _}, %{key: key, protocol: protocol} = state) do
def handle_cast({:remove_client, client_pid}, state) do
case handle_call(:remove_client, {client_pid, nil}, state) do
{:reply, _, state, :hibernate} ->
{:noreply, state, :hibernate}
end
end
@impl true
def handle_call(:add_client, {client_pid, _}, %{key: key, protocol: protocol} = state) do
time = :erlang.monotonic_time(:millisecond) time = :erlang.monotonic_time(:millisecond)
{{conn_pid, used_by, _, _}, _} = [{_, {conn_pid, _tunnel}}] =
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} -> Registry.lookup(registry(), key) |> Enum.filter(&(elem(&1, 0) == self()))
{conn_pid, [client_pid | used_by], crf(time - last_reference, crf), time}
end) state =
state
|> cancel_idle_timer()
|> add_client(client_pid)
|> Map.put(:crf, crf(time - state.last_reference, state.crf))
|> Map.put(:last_reference, time)
:telemetry.execute( :telemetry.execute(
[:pleroma, :connection_pool, :client, :add], [:pleroma, :connection_pool, :client, :add],
%{client_pid: client_pid, clients: used_by}, %{client_pid: client_pid, clients: Map.keys(state.clients)},
%{key: state.key, protocol: protocol} %{key: state.key, protocol: protocol}
) )
state = {:reply, {:ok, conn_pid}, state, :hibernate}
if state.timer != nil do
Process.cancel_timer(state[:timer])
%{state | timer: nil}
else
state
end
ref = Process.monitor(client_pid)
state = put_in(state.client_monitors[client_pid], ref)
{:reply, conn_pid, state, :hibernate}
end end
@impl true @impl true
def handle_call(:remove_client, {client_pid, _}, %{key: key} = state) do def handle_call(:remove_client, {client_pid, _}, state) do
{{_conn_pid, used_by, _crf, _last_reference}, _} = case active_streams(state, client_pid) do
Registry.update_value(registry(), key, fn {conn_pid, used_by, crf, last_reference} -> [] ->
{conn_pid, List.delete(used_by, client_pid), crf, last_reference} {:reply, :ok, remove_client(state, client_pid, true), :hibernate}
end)
{ref, state} = pop_in(state.client_monitors[client_pid]) streams when state.protocol == :http2 ->
Enum.each(streams, &(:ok = Gun.cancel(state.conn_pid, &1)))
{:reply, :ok, remove_client(state, client_pid, true), :hibernate}
Process.demonitor(ref, [:flush]) _streams ->
{:stop, :normal, :ok, state}
timer = end
if used_by == [] do
max_idle = Pleroma.Config.get([:connections_pool, :max_idle_time], 30_000)
Process.send_after(self(), :idle_close, max_idle)
else
nil
end end
{:reply, :ok, %{state | timer: timer}, :hibernate} def handle_call({:register_stream, stream}, {client_pid, _}, state) do
{:reply, :ok, update_stream(state, client_pid, &MapSet.put(&1, stream)), :hibernate}
end
def handle_call({:finish_stream, stream}, {client_pid, _}, state) do
{:reply, :ok, update_stream(state, client_pid, &MapSet.delete(&1, stream)), :hibernate}
end
def handle_call({:release_stream, stream}, {client_pid, _}, state) do
state = update_stream(state, client_pid, &MapSet.delete(&1, stream))
{:reply, :ok, remove_client(state, client_pid, true), :hibernate}
end
def handle_call({:cancel_stream, stream}, {client_pid, _}, %{protocol: :http2} = state) do
:ok = Gun.cancel(state.conn_pid, stream)
state = update_stream(state, client_pid, &MapSet.delete(&1, stream))
{:reply, :ok, remove_client(state, client_pid, true), :hibernate}
end
def handle_call({:cancel_stream, _stream}, _from, state) do
{:stop, :normal, :ok, state}
end
def handle_call(:reclaim_info, _from, %{clients: clients} = state)
when map_size(clients) == 0 do
{:reply, {:idle, state.crf, state.last_reference}, state, :hibernate}
end
def handle_call(:reclaim_info, _from, state) do
{:reply, :in_use, state, :hibernate}
end
def handle_call(:reclaim, _from, %{clients: clients} = state) when map_size(clients) == 0 do
{:stop, :normal, :reclaimed, state}
end
def handle_call(:reclaim, _from, state) do
{:reply, :in_use, state, :hibernate}
end end
@impl true @impl true
def handle_info(:idle_close, state) do def handle_info({:idle_close, token}, %{timer: {_timer_ref, token}, clients: clients} = state)
# Gun monitors the owner process, and will close the connection automatically when map_size(clients) == 0 do
# when it's terminated # Gun monitors the owner process, and will close the connection automatically.
{:stop, :normal, state} {:stop, :normal, state}
end end
def handle_info({:idle_close, _token}, state), do: {:noreply, state, :hibernate}
@impl true @impl true
def handle_info({:gun_up, _pid, _protocol}, state) do def handle_info({:gun_up, _pid, protocol}, state) do
{:noreply, state, :hibernate} {:noreply, %{state | protocol: protocol}, :hibernate}
end end
# Gracefully shutdown if the connection got closed without any streams left @impl true
def handle_info({:gun_error, conn_pid, stream, _reason}, %{conn_pid: conn_pid} = state) do
{:noreply, delete_stream(state, stream), :hibernate}
end
@impl true
def handle_info({:gun_down, _pid, _protocol, _reason, _streams}, %{proxied: true} = state) do
{:stop, :normal, state}
end
# Gracefully shutdown if the connection got closed without any streams left.
@impl true @impl true
def handle_info({:gun_down, _pid, _protocol, _reason, []}, state) do def handle_info({:gun_down, _pid, _protocol, _reason, []}, state) do
{:stop, :normal, state} {:stop, :normal, state}
end end
# Otherwise, wait for retry # Otherwise, wait for retry.
@impl true @impl true
def handle_info({:gun_down, _pid, _protocol, _reason, _killed_streams}, state) do def handle_info({:gun_down, _pid, _protocol, _reason, _killed_streams}, state) do
{:noreply, state, :hibernate} {:noreply, state, :hibernate}
end end
@impl true @impl true
def handle_info({:DOWN, _ref, :process, pid, reason}, state) do def handle_info({:DOWN, ref, :process, pid, reason}, state) do
case state.clients[pid] do
%{monitor: ^ref} ->
:telemetry.execute( :telemetry.execute(
[:pleroma, :connection_pool, :client, :dead], [:pleroma, :connection_pool, :client, :dead],
%{client_pid: pid, reason: reason}, %{client_pid: pid, reason: reason},
%{key: state.key} %{key: state.key}
) )
handle_cast({:remove_client, pid}, state) handle_dead_client(state, pid)
_ ->
{:noreply, state, :hibernate}
end
end
defp add_client(state, client_pid) do
update_in(state.clients, fn clients ->
case clients[client_pid] do
nil ->
Map.put(clients, client_pid, %{
count: 1,
monitor: Process.monitor(client_pid),
streams: MapSet.new()
})
client ->
Map.put(clients, client_pid, %{client | count: client.count + 1})
end
end)
end
defp handle_dead_client(state, client_pid) do
streams = active_streams(state, client_pid)
cond do
streams == [] ->
{:noreply, remove_client(state, client_pid, false, true), :hibernate}
state.protocol == :http2 ->
Enum.each(streams, &(:ok = Gun.cancel(state.conn_pid, &1)))
{:noreply, remove_client(state, client_pid, false, true), :hibernate}
true ->
{:stop, :normal, state}
end
end
defp active_streams(state, client_pid) do
case state.clients[client_pid] do
%{streams: streams} -> MapSet.to_list(streams)
nil -> []
end
end
defp update_stream(state, client_pid, update) do
update_in(state.clients, fn clients ->
case clients[client_pid] do
nil -> clients
client -> Map.put(clients, client_pid, %{client | streams: update.(client.streams)})
end
end)
end
defp delete_stream(state, stream) do
update_in(state.clients, fn clients ->
Map.new(clients, fn {pid, client} ->
{pid, %{client | streams: MapSet.delete(client.streams, stream)}}
end)
end)
end
defp remove_client(state, client_pid, demonitor, all \\ false) do
case state.clients[client_pid] do
nil ->
state
%{count: count} = client when count > 1 and not all ->
put_in(state.clients[client_pid], %{client | count: count - 1})
client ->
if demonitor, do: Process.demonitor(client.monitor, [:flush])
state = update_in(state.clients, &Map.delete(&1, client_pid))
if map_size(state.clients) == 0, do: schedule_idle_close(state), else: state
end
end
defp cancel_idle_timer(%{timer: nil} = state), do: state
defp cancel_idle_timer(%{timer: {timer_ref, _token}} = state) do
Process.cancel_timer(timer_ref)
%{state | timer: nil}
end
defp schedule_idle_close(state) do
max_idle = Pleroma.Config.get([:connections_pool, :max_idle_time], 30_000)
token = make_ref()
timer_ref = Process.send_after(self(), {:idle_close, token}, max_idle)
%{state | timer: {timer_ref, token}}
end end
# LRFU policy: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.1478 # LRFU policy: https://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.1478

View file

@ -26,7 +26,14 @@ defmodule Pleroma.Gun.ConnectionPool.WorkerSupervisor do
def start_worker(opts, true) do def start_worker(opts, true) do
case DynamicSupervisor.start_child(__MODULE__, {Worker, opts}) do case DynamicSupervisor.start_child(__MODULE__, {Worker, opts}) do
{:error, :max_children} -> {:error, :max_children} ->
:telemetry.execute([:pleroma, :connection_pool, :provision_failure], %{opts: opts}) [key | _] = opts
:telemetry.execute(
[:pleroma, :connection_pool, :provision_failure],
%{},
%{key: key}
)
{:error, :pool_full} {:error, :pool_full}
res -> res ->

View file

@ -0,0 +1,40 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTML.URIAttributes do
@moduledoc """
URI attribute rules for the scrubbers.
Works like `FastSanitize.Sanitizer.Meta.allow_tag_with_uri_attributes/3`, but
checks the scheme the way a browser reads the URL. Browsers remove tabs and
newlines anywhere and control characters and spaces at the start, so
`java\\tscript:` is a `javascript:` link to them, while `URI.parse/1` finds no
scheme and FastSanitize lets it through.
"""
defmacro allow_tag_with_uri_attributes(tag, attrs, valid_schemes) do
for attr <- attrs do
quote do
def scrub_attribute(unquote(tag), {unquote(attr), "&" <> _}), do: nil
def scrub_attribute(unquote(tag), {unquote(attr), uri} = attribute) do
if Pleroma.HTML.URIAttributes.valid_scheme?(uri, unquote(valid_schemes)),
do: attribute
end
end
end
end
@spec valid_scheme?(String.t(), [String.t()]) :: boolean()
def valid_scheme?(uri, valid_schemes) do
scheme =
uri
|> String.replace(~r/[\t\n\r]/, "")
|> String.replace(~r/\A[\x00-\x20]+/, "")
|> URI.parse()
|> Map.get(:scheme)
is_nil(scheme) or scheme in valid_schemes
end
end

View file

@ -130,5 +130,9 @@ defmodule Pleroma.HTTP do
end end
defp default_middleware, defp default_middleware,
do: [Tesla.Middleware.FollowRedirects, Pleroma.Tesla.Middleware.EncodeUrl] do: [
Tesla.Middleware.FollowRedirects,
Pleroma.Tesla.Middleware.EncodeUrl,
Pleroma.Tesla.Middleware.MaxBody
]
end end

View file

@ -0,0 +1,253 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.Adapter.Gun do
@behaviour Tesla.Adapter
alias Pleroma.Gun.ConnectionPool
alias Tesla.Multipart
@default_timeout 1_000
@impl Tesla.Adapter
def call(env, opts) do
adapter_opts =
Tesla.Adapter.opts(
[close_conn: true, body_as: :plain, send_body: :at_once, receive: true],
env,
opts
)
|> Map.new()
with {:ok, status, headers, body} <- request(env, adapter_opts) do
env = %{env | status: status, headers: format_headers(headers), body: body}
{:ok, put_stream_ref(env, body)}
end
end
defp put_stream_ref(env, %{stream: stream}) do
put_in(env.opts[:adapter][:stream], stream)
end
defp put_stream_ref(env, _body), do: env
defp request(env, %{conn: conn, tunnel: tunnel} = opts) do
uri = URI.parse(Tesla.build_url(env))
path = request_path(uri, tunnel)
method = Tesla.Adapter.Shared.format_method(env.method)
{headers, body, send_body} = prepare_body(env.headers, env.body, opts[:send_body])
headers = maybe_add_forward_proxy_headers(headers, uri, opts, tunnel)
request_opts = request_opts(opts[:reply_to], tunnel)
stream =
try do
open_stream(conn, method, path, headers, body, request_opts, send_body)
catch
kind, reason ->
ConnectionPool.release_conn(conn)
:erlang.raise(kind, reason, __STACKTRACE__)
end
:ok = ConnectionPool.register_stream(conn, stream)
:ok = send_stream_body(conn, stream, body, send_body)
response = read_response(conn, stream, opts)
case response do
{:error, reason} ->
ConnectionPool.cancel_stream(conn, stream)
{:error, {:gun_stream_error, reason}}
response ->
unless opts[:body_as] in [:chunks, :stream] and stream_body?(response) do
:ok = ConnectionPool.finish_stream(conn, stream)
end
if opts[:close_conn] and opts[:body_as] not in [:stream, :chunks] do
:gun.close(conn)
end
response
end
end
defp stream_body?({:ok, _status, _headers, %{stream: _stream}}), do: true
defp stream_body?({:ok, _status, _headers, %Stream{}}), do: true
defp stream_body?(_response), do: false
defp request_opts(reply_to, nil), do: %{reply_to: reply_to || self()}
defp request_opts(reply_to, :forward_proxy), do: %{reply_to: reply_to || self()}
defp request_opts(reply_to, tunnel) do
%{reply_to: reply_to || self(), tunnel: tunnel}
end
defp request_path(uri, :forward_proxy) do
path = Tesla.Adapter.Shared.prepare_path(uri.path, uri.query)
"#{uri.scheme}://#{authority(uri)}#{path}"
end
defp request_path(uri, _tunnel) do
Tesla.Adapter.Shared.prepare_path(uri.path, uri.query)
end
defp maybe_add_forward_proxy_headers(headers, uri, opts, :forward_proxy) do
headers
|> put_header_new("host", authority(uri))
|> maybe_put_proxy_authorization(opts)
end
defp maybe_add_forward_proxy_headers(headers, _uri, _opts, _tunnel), do: headers
defp maybe_put_proxy_authorization(headers, %{proxy_auth: {username, password}})
when is_binary(username) and is_binary(password) do
value = "Basic " <> Base.encode64(username <> ":" <> password)
List.keystore(headers, "proxy-authorization", 0, {"proxy-authorization", value})
end
defp maybe_put_proxy_authorization(headers, _opts), do: headers
defp put_header_new(headers, name, value) do
if List.keymember?(headers, name, 0), do: headers, else: [{name, value} | headers]
end
defp authority(%URI{scheme: scheme, host: host, port: port}) do
host = if String.contains?(host, ":"), do: "[#{host}]", else: host
if port == URI.default_port(scheme), do: host, else: "#{host}:#{port}"
end
defp prepare_body(headers, %Multipart{} = multipart, _send_body) do
{format_headers(headers ++ Multipart.headers(multipart)), Multipart.body(multipart), :stream}
end
defp prepare_body(headers, body, _send_body)
when is_struct(body, Stream) or is_function(body) do
{format_headers(headers), body, :stream}
end
defp prepare_body(headers, body, send_body) do
{format_headers(headers), body || "", send_body}
end
defp open_stream(conn, method, path, headers, _body, request_opts, :stream) do
:gun.headers(conn, method, path, headers, request_opts)
end
defp open_stream(conn, method, path, headers, body, request_opts, :at_once) do
:gun.request(conn, method, path, headers, body, request_opts)
end
defp send_stream_body(conn, stream, body, :stream) do
try do
for data <- body, do: :ok = :gun.data(conn, stream, :nofin, data)
:gun.data(conn, stream, :fin, "")
catch
kind, reason ->
ConnectionPool.cancel_stream(conn, stream)
:erlang.raise(kind, reason, __STACKTRACE__)
end
end
defp send_stream_body(_conn, _stream, _body, :at_once), do: :ok
defp read_response(conn, stream, opts) do
receive? = opts[:receive]
receive do
{:gun_response, ^conn, ^stream, :fin, status, headers} ->
{:ok, status, headers, ""}
{:gun_response, ^conn, ^stream, :nofin, status, headers} ->
format_response(conn, stream, opts, status, headers, opts[:body_as])
{:gun_up, ^conn, _protocol} when receive? ->
read_response(conn, stream, opts)
{:gun_error, ^conn, reason} ->
{:error, reason}
{:gun_error, ^conn, ^stream, reason} ->
{:error, reason}
{:gun_down, ^conn, _protocol, _reason, _killed_streams} when receive? ->
read_response(conn, stream, opts)
{:DOWN, _ref, :process, ^conn, reason} ->
{:error, reason}
after
opts[:timeout] || @default_timeout -> {:error, :recv_response_timeout}
end
end
defp format_response(conn, stream, opts, status, headers, :plain) do
case read_body(conn, stream, opts) do
{:ok, body} ->
{:ok, status, headers, body}
{:error, error} ->
:ok = :gun.flush(stream)
{:error, error}
end
end
defp format_response(conn, stream, opts, status, headers, :stream) do
body =
Stream.resource(
fn -> :reading end,
fn
:reading ->
case Tesla.Adapter.Gun.read_chunk(conn, stream, opts) do
{:nofin, part} -> {[part], :reading}
{:fin, part} -> {[part], :done}
{:error, reason} -> raise "Gun stream failed: #{inspect(reason)}"
end
:done ->
{:halt, :done}
end,
fn _state ->
if opts[:close_conn], do: :gun.close(conn)
end
)
{:ok, status, headers, body}
end
defp format_response(conn, stream, opts, status, headers, :chunks) do
{:ok, status, headers, %{pid: conn, stream: stream, opts: Map.to_list(opts)}}
end
defp read_body(conn, stream, opts, acc \\ "") do
receive do
{:gun_data, ^conn, ^stream, :fin, body} ->
append_body(acc, body, opts[:max_body])
{:gun_data, ^conn, ^stream, :nofin, body} ->
with {:ok, acc} <- append_body(acc, body, opts[:max_body]) do
read_body(conn, stream, opts, acc)
end
{:gun_error, ^conn, ^stream, reason} ->
{:error, reason}
{:DOWN, _ref, :process, ^conn, reason} ->
{:error, reason}
after
opts[:timeout] || @default_timeout -> {:error, :recv_body_timeout}
end
end
defp append_body(acc, part, nil), do: {:ok, acc <> part}
defp append_body(acc, part, limit) do
body = acc <> part
if byte_size(body) <= limit, do: {:ok, body}, else: {:error, :body_too_large}
end
defp format_headers(headers) do
Enum.map(headers, fn {key, value} ->
{String.downcase(to_string(key)), to_string(value)}
end)
end
end

View file

@ -46,6 +46,24 @@ defmodule Pleroma.HTTP.AdapterHelper do
|> adapter_helper().options(uri) |> adapter_helper().options(uri)
end end
@doc """
The configured Tesla adapter with TLS options, for clients that do not go
through `Pleroma.HTTP`.
"""
@spec tesla_adapter() :: module() | {module(), keyword()}
def tesla_adapter do
case adapter() do
Tesla.Adapter.Hackney ->
{Tesla.Adapter.Hackney, Pleroma.HTTP.TLS.put_hackney_ssl_options([])}
Tesla.Adapter.Gun ->
{Tesla.Adapter.Gun, certificates_verification: true}
adapter ->
adapter
end
end
defp adapter, do: Application.get_env(:tesla, :adapter) defp adapter, do: Application.get_env(:tesla, :adapter)
defp adapter_helper do defp adapter_helper do

View file

@ -28,6 +28,7 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
@defaults @defaults
|> Keyword.merge(config_opts) |> Keyword.merge(config_opts)
|> ssl_options_to_tls_opts()
|> add_scheme_opts(uri) |> add_scheme_opts(uri)
|> AdapterHelper.maybe_add_proxy(proxy) |> AdapterHelper.maybe_add_proxy(proxy)
|> Keyword.merge(incoming_opts) |> Keyword.merge(incoming_opts)
@ -35,6 +36,15 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
|> maybe_stream() |> maybe_stream()
end end
# [:http, :adapter, :ssl_options] is Hackney's name for the TLS options,
# and AdminFE sets it. Keep those settings when Gun is the adapter.
defp ssl_options_to_tls_opts(opts) do
case Keyword.pop(opts, :ssl_options) do
{nil, opts} -> opts
{ssl_options, opts} -> Keyword.put_new(opts, :tls_opts, ssl_options)
end
end
defp add_scheme_opts(opts, %{scheme: "http"}), do: opts defp add_scheme_opts(opts, %{scheme: "http"}), do: opts
defp add_scheme_opts(opts, %{scheme: "https"}) do defp add_scheme_opts(opts, %{scheme: "https"}) do
@ -48,10 +58,10 @@ defmodule Pleroma.HTTP.AdapterHelper.Gun do
Keyword.put(opts, :timeout, recv_timeout) Keyword.put(opts, :timeout, recv_timeout)
end end
# Gun uses [body_as: :stream] # Keep the pool lease until the caller consumes or cancels the body.
defp maybe_stream(opts) do defp maybe_stream(opts) do
case Keyword.pop(opts, :stream, nil) do case Keyword.pop(opts, :stream, nil) do
{true, opts} -> Keyword.put(opts, :body_as, :stream) {true, opts} -> Keyword.put(opts, :body_as, :chunks)
{_, opts} -> opts {_, opts} -> opts
end end
end end

View file

@ -28,6 +28,25 @@ defmodule Pleroma.HTTP.AdapterHelper.Hackney do
|> add_scheme_opts(uri) |> add_scheme_opts(uri)
|> maybe_add_with_body() |> maybe_add_with_body()
|> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy) |> Pleroma.HTTP.AdapterHelper.maybe_add_proxy(proxy)
|> Keyword.put(:follow_redirect, false)
|> Keyword.put(:force_redirect, false)
|> normalize_proxy()
|> Pleroma.HTTP.TLS.put_hackney_ssl_options()
end
# Hackney 4 ignores a proxy whose host is an IP tuple and connects directly.
# ConfigDB turns a numeric proxy host into such a tuple.
defp normalize_proxy(opts) do
case Keyword.fetch(opts, :proxy) do
{:ok, {type, host, port}} when is_tuple(host) ->
Keyword.put(opts, :proxy, {type, :inet.ntoa(host), port})
{:ok, {host, port}} when is_tuple(host) ->
Keyword.put(opts, :proxy, {:inet.ntoa(host), port})
_ ->
opts
end
end end
defp add_scheme_opts(opts, _), do: opts defp add_scheme_opts(opts, _), do: opts

49
lib/pleroma/http/tls.ex Normal file
View file

@ -0,0 +1,49 @@
# Pleroma: A lightweight social networking server
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
# SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.HTTP.TLS do
@moduledoc """
TLS client options that trust the CAStore bundle.
Hackney otherwise trusts its own certifi bundle. Hackney 1 drops all of its
default TLS options, including certificate checks, as soon as any
`ssl_options` are given. Hackney 4 merges them, so `partial_chain` must be
set too: Hackney's own one trusts any certificate in the peer chain that is
in certifi.
"""
@spec ssl_options(keyword()) :: keyword()
def ssl_options(overrides \\ []) do
defaults = [
verify: :verify_peer,
cacertfile: CAStore.file_path(),
depth: 20,
customize_hostname_check: [match_fun: :public_key.pkix_verify_hostname_match_fun(:https)],
partial_chain: &__MODULE__.reject_partial_chain/1
]
# Hackney drops cacerts when cacertfile is set too.
defaults =
if Keyword.has_key?(overrides, :cacerts),
do: Keyword.delete(defaults, :cacertfile),
else: defaults
Keyword.merge(defaults, overrides)
end
@doc false
def reject_partial_chain(_certs), do: :unknown_ca
@spec put_hackney_ssl_options(keyword()) :: keyword()
def put_hackney_ssl_options(opts) do
# Hackney 4 ignores the top-level insecure option and falls back to certifi.
if opts[:insecure] do
Keyword.put(opts, :ssl_options, verify: :verify_none)
else
opts
|> Keyword.put(:ssl_options, ssl_options(Keyword.get(opts, :ssl_options, [])))
|> Keyword.put(:proxy_ssl_options, ssl_options(Keyword.get(opts, :proxy_ssl_options, [])))
end
end
end

View file

@ -9,6 +9,7 @@ defmodule Pleroma.List do
import Ecto.Changeset import Ecto.Changeset
alias Pleroma.Activity alias Pleroma.Activity
alias Pleroma.Emoji
alias Pleroma.Repo alias Pleroma.Repo
alias Pleroma.User alias Pleroma.User
@ -17,14 +18,54 @@ defmodule Pleroma.List do
field(:title, :string) field(:title, :string)
field(:following, {:array, :string}, default: []) field(:following, {:array, :string}, default: [])
field(:ap_id, :string) field(:ap_id, :string)
field(:exclusive, :boolean, default: false)
field(:emoji, :string)
timestamps() timestamps()
end end
def title_changeset(list, attrs \\ %{}) do def update_changeset(list, attrs \\ %{}) do
list list
|> cast(attrs, [:title]) |> cast(attrs, [:title, :exclusive, :emoji])
|> validate_required([:title]) |> validate_required([:title])
|> fix_emoji()
|> validate_emoji()
end
defp fix_emoji(changeset) do
with {:emoji_field, emoji} when is_binary(emoji) <-
{:emoji_field, get_field(changeset, :emoji)},
{:fixed_emoji, emoji} <-
{:fixed_emoji,
emoji
|> Emoji.fully_qualify_emoji()
|> Emoji.maybe_quote()} do
put_change(changeset, :emoji, emoji)
else
{:emoji_field, _} -> changeset
end
end
defp validate_emoji(changeset) do
validate_change(changeset, :emoji, fn
:emoji, nil ->
[]
:emoji, emoji ->
if Emoji.unicode?(emoji) or valid_local_custom_emoji?(emoji) do
[]
else
[emoji: "Invalid emoji"]
end
end)
end
defp valid_local_custom_emoji?(emoji) do
with %{file: _path} <- Emoji.get(emoji) do
true
else
_ -> false
end
end end
def follow_changeset(list, attrs \\ %{}) do def follow_changeset(list, attrs \\ %{}) do
@ -91,14 +132,14 @@ defmodule Pleroma.List do
|> Repo.all() |> Repo.all()
end end
def rename(%Pleroma.List{} = list, title) do def update(%Pleroma.List{} = list, params) do
list list
|> title_changeset(%{title: title}) |> update_changeset(params)
|> Repo.update() |> Repo.update()
end end
def create(title, %User{} = creator) do def create(params, %User{} = creator) do
changeset = title_changeset(%Pleroma.List{user_id: creator.id}, %{title: title}) changeset = update_changeset(%Pleroma.List{user_id: creator.id}, params)
if changeset.valid? do if changeset.valid? do
Repo.transaction(fn -> Repo.transaction(fn ->
@ -149,4 +190,14 @@ defmodule Pleroma.List do
end end
def member?(_, _), do: false def member?(_, _), do: false
def get_exclusive_list_members(%User{id: user_id}) do
Pleroma.List
|> where([l], l.user_id == ^user_id)
|> where([l], l.exclusive == true)
|> select([l], l.following)
|> Repo.all()
|> List.flatten()
|> Enum.uniq()
end
end end

View file

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

View file

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

View file

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

View file

@ -35,6 +35,7 @@ defmodule Pleroma.Notification do
# remember to add a migration to add it to the `notifications_type` enum # remember to add a migration to add it to the `notifications_type` enum
# as well. # as well.
field(:type, :string) field(:type, :string)
field(:group_key, :string)
belongs_to(:user, User, type: FlakeId.Ecto.CompatType) belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType) belongs_to(:activity, Activity, type: FlakeId.Ecto.CompatType)
@ -49,7 +50,7 @@ defmodule Pleroma.Notification do
|> type_from_activity() |> type_from_activity()
notification notification
|> changeset(%{type: type}) |> changeset(%{type: type, group_key: grouped_notification_key(type, activity)})
|> Repo.update() |> Repo.update()
end end
end end
@ -62,6 +63,99 @@ defmodule Pleroma.Notification do
|> Repo.aggregate(:count, :id) |> Repo.aggregate(:count, :id)
end end
@groupable_notification_types ~w{favourite follow reblog}
@group_bucket_seconds 12 * 60 * 60
def groupable_notification_types, do: @groupable_notification_types
def group_notifications(notifications, grouped_types \\ nil) do
grouped_types = normalize_grouped_types(grouped_types)
{group_keys, grouped_notifications} =
Enum.reduce(notifications, {[], %{}}, fn notification,
{group_keys, grouped_notifications} ->
group_key = group_key(notification, grouped_types)
if Map.has_key?(grouped_notifications, group_key) do
{group_keys, Map.update!(grouped_notifications, group_key, &[notification | &1])}
else
{[group_key | group_keys], Map.put(grouped_notifications, group_key, [notification])}
end
end)
group_keys
|> Enum.reverse()
|> Enum.map(fn group_key ->
grouped_notifications
|> Map.fetch!(group_key)
|> Enum.reverse()
end)
end
def group_key(notification, grouped_types \\ nil)
def group_key(%Notification{} = notification, nil) do
group_key(notification, @groupable_notification_types)
end
def group_key(%Notification{type: type, group_key: group_key} = notification, grouped_types) do
grouped_types = normalize_grouped_types(grouped_types)
if type in @groupable_notification_types and type in grouped_types and is_binary(group_key) do
group_key
else
ungrouped_group_key(notification)
end
end
def normalize_grouped_types(nil), do: @groupable_notification_types
def normalize_grouped_types(types) when is_list(types), do: types
def normalize_grouped_types(type), do: [type]
defp ungrouped_group_key(%Notification{id: id}), do: "ungrouped-#{id}"
defp grouped_notification_key(type, activity) when type in @groupable_notification_types do
with target_id when is_binary(target_id) <- group_target_id(type, activity) do
# Mastodon uses Redis to reuse a recent bucket for rolling 12h windows. Pleroma keeps keys
# deterministic and self-contained; clients must treat group_key as opaque either way.
"#{type}-#{target_id}-#{group_time_bucket(activity)}"
end
end
defp grouped_notification_key(_type, _activity), do: nil
defp group_time_bucket(%Activity{inserted_at: inserted_at}) when not is_nil(inserted_at) do
inserted_at
|> NaiveDateTime.to_erl()
|> :calendar.datetime_to_gregorian_seconds()
|> div(@group_bucket_seconds)
end
defp group_time_bucket(_),
do: group_time_bucket(%Activity{inserted_at: NaiveDateTime.utc_now()})
defp group_target_id(type, activity) when type in ["favourite", "reblog"] do
with object_id when is_binary(object_id) <- object_id_for(activity),
%Activity{id: id} <- Activity.get_create_by_object_ap_id(object_id) do
to_string(id)
else
_ -> nil
end
end
defp group_target_id("follow", %{data: %{"object" => ap_id}}) do
case User.get_cached_by_ap_id(ap_id) do
%User{id: id} -> to_string(id)
_ -> nil
end
end
defp group_target_id(_, _), do: nil
defp object_id_for(%{data: %{"object" => %{"id" => id}}}) when is_binary(id), do: id
defp object_id_for(%{data: %{"object" => id}}) when is_binary(id), do: id
defp object_id_for(_), do: nil
@notification_types ~w{ @notification_types ~w{
favourite favourite
follow follow
@ -79,7 +173,7 @@ defmodule Pleroma.Notification do
def changeset(%Notification{} = notification, attrs) do def changeset(%Notification{} = notification, attrs) do
notification notification
|> cast(attrs, [:seen, :type]) |> cast(attrs, [:seen, :type, :group_key])
|> validate_inclusion(:type, @notification_types) |> validate_inclusion(:type, @notification_types)
end end
@ -458,7 +552,8 @@ defmodule Pleroma.Notification do
user_id: user.id, user_id: user.id,
activity: activity, activity: activity,
seen: mark_as_read?(activity, user), seen: mark_as_read?(activity, user),
type: type type: type,
group_key: grouped_notification_key(type, activity)
}) })
|> Marker.multi_set_last_read_id(user, "notifications") |> Marker.multi_set_last_read_id(user, "notifications")
|> Repo.transaction() |> Repo.transaction()

View file

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

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