Compare commits
330 commits
shigusegub
...
shigusegub
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6165e2f342 | ||
|
|
86dd9663fc | ||
|
|
aa16f40f09 | ||
|
|
f99ce5b2e6 | ||
|
|
e023ed7a5a |
||
|
|
54369c93d7 |
||
|
|
8a410bf49f |
||
|
|
e46365bf2a | ||
|
|
33fdc59bc1 | ||
|
|
b1d72e16a3 | ||
|
|
c8c1f7d38b | ||
|
|
cdb0f103a8 |
||
|
|
619db0adca | ||
|
|
4c9fc6287e |
||
|
|
a5e7145c97 | ||
|
|
0c0d687330 |
||
|
|
6ee40cb2eb |
||
|
|
ff7927e219 |
||
|
|
5f55c9653c |
||
|
|
678fe8a064 |
||
|
|
94a28d1286 |
||
|
|
1a83b1d28d | ||
|
|
cda6738309 | ||
|
|
b054c2aa42 |
||
|
|
9dd02ecd50 |
||
|
|
49486a4e64 | ||
|
|
46a2808690 | ||
|
|
c428cf43e8 | ||
|
|
1a13ec539b | ||
|
|
7575b7abc2 |
||
|
|
093b156c65 | ||
|
|
c7c453ca21 | ||
|
|
0cf221ba13 | ||
|
|
c5737898f5 | ||
|
|
b90ac6b9c7 | ||
|
|
5b63307f85 | ||
|
|
512b0f67a4 |
||
|
|
d7a0d97c36 | ||
|
|
086c15b5cd | ||
|
|
4fef910303 | ||
|
|
526365364b | ||
|
|
7fff19cbe4 | ||
|
|
143f426e84 | ||
|
|
e4ad3ab322 | ||
|
|
9ae1249ccb |
||
|
|
7ab9e2c7ce |
||
|
|
c92d233233 |
||
|
|
2db3a9c04d | ||
|
|
d0c2d04356 |
||
|
|
d8e3ea69b1 |
||
|
|
9e16332d9d |
||
|
|
4810d2536e |
||
|
|
47ca427497 | ||
|
|
ffff2098f0 | ||
|
|
e211b72924 | ||
|
|
68e4bb53a2 |
||
|
|
d8e9affded | ||
|
|
4d3aea1fce | ||
|
|
2b3ac2d7fe |
||
|
|
95eef879d7 |
||
|
|
c19bdf3814 |
||
|
|
95b15190de |
||
|
|
6c2d8209c9 |
||
|
|
6f415cf3fc |
||
|
|
0cf865f025 |
||
|
|
35b5447f3f |
||
|
|
90e390e45b |
||
|
|
d6d0ce7260 |
||
|
|
ea886dc36b |
||
|
|
9b331d648b | ||
|
|
71afba4825 |
||
|
|
8a56cf5c0f |
||
|
|
8e72f4cd17 | ||
|
|
960c730706 | ||
|
|
ab9fd33762 |
||
|
|
216a00f73f |
||
|
|
7f4890b6a9 |
||
|
|
61feb3dfcd |
||
|
|
f579dc099c | ||
|
|
592be493c8 |
||
|
|
6ff1d10e22 | ||
|
|
e1b2e788d9 | ||
|
|
47021b5aba |
||
|
|
c780298ce7 |
||
|
|
6b86e31e5d |
||
|
|
ebcc7684c1 | ||
|
|
4873991983 | ||
|
|
2082bf729a | ||
|
|
c62d191986 |
||
|
|
f1249d830f | ||
|
|
727e9e7749 |
||
|
|
3c63877e61 |
||
|
|
86a5213523 | ||
|
|
aec0deef8b |
||
|
|
394db0dce0 |
||
|
|
684e9ef247 |
||
|
|
af175fbdfc |
||
|
|
4230887d7e | ||
|
|
8ccdd98914 |
||
|
|
78aef1b875 |
||
|
|
78a41dfdcd |
||
|
|
621d86a31d |
||
|
|
2c7095d300 | ||
|
|
6553ba24aa |
||
|
|
1a8d585cbf |
||
|
|
6ae02d71bd |
||
|
|
00dd1b5103 |
||
|
|
4acd8c4e72 |
||
|
|
50651284a2 |
||
|
|
9fdad779b5 |
||
|
|
a1f7413832 |
||
|
|
ee18feef7c |
||
|
|
93c155e4fa | ||
|
|
47e6dbfade |
||
|
|
da9cbc8e2f |
||
|
|
3dbc570471 |
||
|
|
a35aa6551e |
||
|
|
99b614a52e |
||
|
|
4337e0eb1b |
||
|
|
7756f491d5 |
||
|
|
bd45704dba |
||
|
|
9c540995b4 |
||
|
|
80e72b79f5 |
||
|
|
42683e79df |
||
|
|
da28a4c441 |
||
|
|
af6d12c0a5 |
||
|
|
cb2271978e |
||
|
|
db7bca8945 | ||
|
|
e4632eced3 |
||
|
|
a996d25b84 |
||
|
|
25e543d44d |
||
|
|
cafd75b072 |
||
|
|
95a33855d1 |
||
|
|
7f97e21910 |
||
|
|
209b9c0a1e |
||
|
|
16b7a95c48 |
||
|
|
2e968890de |
||
|
|
5229e8ae65 |
||
|
|
d8b8cbbb8d |
||
|
|
89a78d765c |
||
|
|
dd29b9c11b |
||
|
|
eea01b54b7 |
||
|
|
42eb9706a5 |
||
|
|
97a2e8c764 |
||
|
|
e002650e23 |
||
|
|
f00c13602d |
||
|
|
13d6246ed9 |
||
|
|
67e7f788c9 |
||
|
|
e2adc796c4 |
||
|
|
d2f7c9252f |
||
|
|
5351cd4ce9 |
||
|
|
fc5aea73ff |
||
|
|
c2fb145c5f | ||
|
|
683ab39160 | ||
|
|
7582b71f46 | ||
|
|
ebfa0d88df | ||
|
|
00265751cc | ||
|
|
a3404e91bc | ||
|
|
01ced6bea2 | ||
|
|
1405f5dc8b | ||
|
|
fd7b809c54 | ||
|
|
096c4ea980 | ||
|
|
072dc39d83 | ||
|
|
7bba485397 | ||
|
|
1fe0970b64 | ||
|
|
08bf6c8fed | ||
|
|
cdcc432f31 | ||
|
|
b0de9bd3cd | ||
|
|
56a25202b9 | ||
|
|
265d3eeebc | ||
|
|
b224a2dacc | ||
|
|
8640fcef22 | ||
|
|
0fd544722f | ||
|
|
b67d7c1106 | ||
|
|
6f8233d780 | ||
|
|
2880aac617 | ||
|
|
4493d0d187 | ||
|
|
1a0af1c0c0 | ||
|
|
88a349f3ab | ||
|
|
a9fe2fe4d8 | ||
|
|
f138423814 | ||
|
|
eb69576154 |
||
|
|
c8baad165b |
||
|
|
799199f6b5 | ||
|
|
9db47790bb | ||
|
|
9e22baa66a | ||
|
|
5aa3c8a06e | ||
|
|
9af26e5fb5 | ||
|
|
f06a0eab50 | ||
|
|
ea78e76837 | ||
|
|
1d819195b6 | ||
|
|
711b33d81c | ||
|
|
7cc9ba6f06 | ||
|
|
63c9c7ea92 | ||
|
|
d1bd24ba64 | ||
|
|
106a52eb2e | ||
|
|
eabfb2bd47 | ||
|
|
876913d2af | ||
|
|
93d05efdb1 | ||
|
|
85d311adcf | ||
|
|
cbb715b978 | ||
|
|
dc7bd82968 | ||
|
|
e1a1e5c726 | ||
|
|
f3f72048ac | ||
|
|
2937bb68b1 | ||
|
|
750266f2e3 | ||
|
|
645211812e | ||
|
|
ee55764501 | ||
|
|
a9ad6297b7 | ||
|
|
6a3b5b3218 | ||
|
|
bf86768e88 | ||
|
|
531041041a | ||
|
|
f60a317c2f | ||
|
|
f4c28392e1 | ||
|
|
ec294b30c1 | ||
|
|
b8a66c22b3 | ||
|
|
93e8f9d7d1 | ||
|
|
8417629b4b | ||
|
|
958d250fe5 | ||
|
|
19e05b4a7b | ||
|
|
5b6af83e86 | ||
|
|
1b9cd83d88 | ||
|
|
dfaabb48ef | ||
|
|
6bbfba7f6e | ||
|
|
23cc812366 | ||
|
|
d0ef58a59d | ||
|
|
37cb2f9273 | ||
|
|
70de4491c2 | ||
|
|
d1787966a6 | ||
|
|
a0131ff733 |
||
|
|
bceb28b941 | ||
|
|
4e1ba489ec | ||
|
|
d95d7f6eba | ||
|
|
0592f111f6 | ||
|
|
40bc79e5ce | ||
|
|
87b4e3f3ff | ||
|
|
a1bb81bddb | ||
|
|
499b2ed118 | ||
|
|
3760480813 | ||
|
|
5f321b0b5b | ||
|
|
d0db1f00c3 | ||
|
|
662c9f36ac | ||
|
|
2388964b14 | ||
|
|
d03ae43ee0 | ||
|
|
8975129680 | ||
|
|
96f252023e | ||
|
|
0879dd3950 | ||
|
|
4bc0b26abe | ||
|
|
8abd25950a | ||
|
|
970e0f9044 | ||
|
|
848b3f5d5b | ||
|
|
222306ff27 | ||
|
|
ca38217898 |
||
|
|
19025563e2 | ||
|
|
65c7d0c7b9 | ||
|
|
490cd33bc9 | ||
|
|
8921dbfffd | ||
|
|
b645643cfb | ||
|
|
68de463392 | ||
|
|
1b182b07dc | ||
|
|
2086561fbd | ||
|
|
3620726ff3 | ||
|
|
37041aae60 | ||
|
|
c3b779036d | ||
|
|
36a79ab58e | ||
|
|
d389359ec3 | ||
|
|
6405a2e682 | ||
|
|
38c30d50b4 | ||
|
|
9040f97cea | ||
|
|
120719f28c | ||
|
|
a9b5a28c26 | ||
|
|
938ee4cb01 | ||
|
|
c392b21db1 | ||
|
|
588bc656f2 | ||
|
|
ef7be0a1e5 | ||
|
|
e32ab8aef2 | ||
|
|
95c8b4732f |
||
|
|
cbc2ea3315 | ||
|
|
23a4d68c97 | ||
|
|
0b950f6253 | ||
|
|
699a7e57e8 |
||
|
|
3d9ac413af | ||
|
|
eed4f4bba8 |
||
|
|
f80c5744b1 |
||
|
|
1c685ea41a | ||
|
|
b798f7d6e9 | ||
|
|
2e80c786bb | ||
|
|
ec6ffa4fdf | ||
|
|
4693dc837b | ||
|
|
feda4d0718 | ||
|
|
cb78699a3b | ||
|
|
833e9829ba | ||
|
|
bd30d461b0 | ||
|
|
5001fb3a78 | ||
|
|
bc0c7fb310 | ||
|
|
c1e33bfadb | ||
|
|
e7a4d5ea66 | ||
|
|
6fac6ff7f1 | ||
|
|
055242f438 | ||
|
|
80ede85f75 | ||
|
|
26234c032d | ||
|
|
9b5afe9cd4 |
||
|
|
56b3db71ff |
||
|
|
ddc1a86f40 |
||
|
|
6f86883cca |
||
|
|
be327ca982 |
||
|
|
a4fb651fac | ||
|
|
117b0bd79e | ||
|
|
49f9ab3034 | ||
|
|
0b871ff1f2 | ||
|
|
77a1d79f92 | ||
|
|
49985b1614 | ||
|
|
92fd157cd8 | ||
|
|
b66b93a94a | ||
|
|
f0669997d3 | ||
|
|
57a3b1f6d0 | ||
|
|
1af8997462 | ||
|
|
b9c281a0c3 | ||
|
|
bd61916270 | ||
|
|
47f4bde0ea | ||
|
|
958a4581d6 | ||
|
|
9ede9b92d3 | ||
|
|
3903f12c78 | ||
|
|
ee17d6413d | ||
|
|
71f5a493f3 | ||
|
|
592955a895 | ||
|
|
7c09150cdb | ||
|
|
77436451ad | ||
|
|
a5c88eb39b | ||
|
|
627c944fec |
254 changed files with 8794 additions and 1521 deletions
25
.forgejo/issue_template/bug.yaml
Normal file
25
.forgejo/issue_template/bug.yaml
Normal 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
|
||||||
13
.forgejo/pull_request_template.md
Normal file
13
.forgejo/pull_request_template.md
Normal 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
3
.gitignore
vendored
|
|
@ -56,6 +56,9 @@ pleroma.iml
|
||||||
# asdf
|
# asdf
|
||||||
.tool-versions
|
.tool-versions
|
||||||
|
|
||||||
|
# mise
|
||||||
|
mise.toml
|
||||||
|
|
||||||
# Editor temp files
|
# Editor temp files
|
||||||
*~
|
*~
|
||||||
*#
|
*#
|
||||||
|
|
|
||||||
20
.woodpecker/changelog.yaml
Normal file
20
.woodpecker/changelog.yaml
Normal 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
|
||||||
85
.woodpecker/docker-armv7.yaml
Normal file
85
.woodpecker/docker-armv7.yaml
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
when:
|
||||||
|
# Temporary PR validation while arm/v7 Docker builds are being brought up.
|
||||||
|
- event: pull_request
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
|
||||||
|
- event: tag
|
||||||
|
- event: manual
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- event: manual
|
||||||
|
branch: stable
|
||||||
|
|
||||||
|
# Target the CAX21 arm64 host with native arm32 userspace support, not generic arm64 runners.
|
||||||
|
labels:
|
||||||
|
platform: linux/arm
|
||||||
|
armv7: native
|
||||||
|
|
||||||
|
variables:
|
||||||
|
docker_variables: &docker_variables
|
||||||
|
repo: pleroma/pleroma
|
||||||
|
registry: git.pleroma.social
|
||||||
|
armv7_build_settings: &armv7_build_settings
|
||||||
|
<<: *docker_variables
|
||||||
|
build_args:
|
||||||
|
- ELIXIR_IMG=arm32v7/elixir
|
||||||
|
- ELIXIR_TAG=1.17.3-otp-26-alpine
|
||||||
|
- ALPINE_IMG=arm32v7/alpine
|
||||||
|
- ALPINE_VER=3.20
|
||||||
|
extra_opts: --custom-platform=linux/arm/v7
|
||||||
|
armv7_push_settings: &armv7_push_settings
|
||||||
|
<<: *armv7_build_settings
|
||||||
|
username:
|
||||||
|
from_secret: pleroma-ci-user
|
||||||
|
password:
|
||||||
|
from_secret: pleroma-ci-password
|
||||||
|
|
||||||
|
steps:
|
||||||
|
docker-armv7-pr:
|
||||||
|
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
settings:
|
||||||
|
<<: *armv7_build_settings
|
||||||
|
dry_run: true
|
||||||
|
tags:
|
||||||
|
- armv7-pr-check
|
||||||
|
|
||||||
|
docker-develop-armv7:
|
||||||
|
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- event: manual
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
settings:
|
||||||
|
<<: *armv7_push_settings
|
||||||
|
tags:
|
||||||
|
- latest-armv7
|
||||||
|
- develop-armv7
|
||||||
|
- ${CI_COMMIT_SHA:0:8}-armv7
|
||||||
|
|
||||||
|
docker-stable-armv7:
|
||||||
|
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||||
|
when:
|
||||||
|
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
|
||||||
|
settings:
|
||||||
|
<<: *armv7_push_settings
|
||||||
|
tags:
|
||||||
|
- latest-armv7
|
||||||
|
- stable-armv7
|
||||||
|
- ${CI_COMMIT_SHA:0:8}-armv7
|
||||||
|
|
||||||
|
docker-stable-tag-armv7:
|
||||||
|
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||||
|
when:
|
||||||
|
- event: tag
|
||||||
|
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
|
||||||
|
settings:
|
||||||
|
<<: *armv7_push_settings
|
||||||
|
tags:
|
||||||
|
- latest-armv7
|
||||||
|
- stable-armv7
|
||||||
|
- ${CI_COMMIT_SHA:0:8}-armv7
|
||||||
|
- ${CI_COMMIT_TAG}-armv7
|
||||||
60
.woodpecker/docker-combine.yaml
Normal file
60
.woodpecker/docker-combine.yaml
Normal file
|
|
@ -0,0 +1,60 @@
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
|
||||||
|
- event: tag
|
||||||
|
- event: manual
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- event: manual
|
||||||
|
branch: stable
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
skip_clone: true
|
||||||
|
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
|
|
||||||
|
steps:
|
||||||
|
docker-develop-combine:
|
||||||
|
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- event: manual
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
settings: &docker_settings
|
||||||
|
registry: "git.pleroma.social"
|
||||||
|
image: "pleroma/pleroma"
|
||||||
|
architectures: [amd64, arm64]
|
||||||
|
tags:
|
||||||
|
- latest
|
||||||
|
- develop
|
||||||
|
- ${CI_COMMIT_SHA:0:8}
|
||||||
|
username:
|
||||||
|
from_secret: pleroma-ci-user
|
||||||
|
password:
|
||||||
|
from_secret: pleroma-ci-password
|
||||||
|
|
||||||
|
docker-stable-combine:
|
||||||
|
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
|
||||||
|
when:
|
||||||
|
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
|
||||||
|
settings:
|
||||||
|
<<: *docker_settings
|
||||||
|
tags: &stable_docker_tags
|
||||||
|
- latest
|
||||||
|
- stable
|
||||||
|
- ${CI_COMMIT_SHA:0:8}
|
||||||
|
|
||||||
|
docker-stable-tag-combine:
|
||||||
|
image: git.fluffytail.org/phnt/wpc-docker-tagger:latest
|
||||||
|
when:
|
||||||
|
- event: tag
|
||||||
|
- evaluate: 'CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
|
||||||
|
settings:
|
||||||
|
<<: *docker_settings
|
||||||
|
tags:
|
||||||
|
- <<: *stable_docker_tags
|
||||||
|
- ${CI_COMMIT_TAG}
|
||||||
108
.woodpecker/docker.yaml
Normal file
108
.woodpecker/docker.yaml
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**", "Dockerfile" ]
|
||||||
|
- event: tag
|
||||||
|
- event: manual
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- event: manual
|
||||||
|
branch: stable
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
|
|
||||||
|
# This is needed for the when clauses below.
|
||||||
|
labels:
|
||||||
|
platform: ${platform}
|
||||||
|
memory: 'high'
|
||||||
|
|
||||||
|
variables:
|
||||||
|
docker_variables: &docker_variables
|
||||||
|
repo: pleroma/pleroma
|
||||||
|
registry: git.pleroma.social
|
||||||
|
username:
|
||||||
|
from_secret: pleroma-ci-user
|
||||||
|
password:
|
||||||
|
from_secret: pleroma-ci-password
|
||||||
|
kaniko_image: &kaniko_image woodpeckerci/plugin-kaniko:2.3.1
|
||||||
|
|
||||||
|
steps:
|
||||||
|
docker-develop-amd64:
|
||||||
|
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
settings:
|
||||||
|
<<: *docker_variables
|
||||||
|
tags:
|
||||||
|
- latest-amd64
|
||||||
|
- develop-amd64
|
||||||
|
- ${CI_COMMIT_SHA:0:8}-amd64
|
||||||
|
|
||||||
|
docker-develop-arm64:
|
||||||
|
image: woodpeckerci/plugin-kaniko:2.3.1
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
settings:
|
||||||
|
<<: *docker_variables
|
||||||
|
tags:
|
||||||
|
- latest-arm64
|
||||||
|
- develop-arm64
|
||||||
|
- ${CI_COMMIT_SHA:0:8}-arm64
|
||||||
|
|
||||||
|
docker-stable-amd64:
|
||||||
|
image: *kaniko_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
|
||||||
|
settings:
|
||||||
|
<<: *docker_variables
|
||||||
|
tags: &amd64_tags
|
||||||
|
- latest-amd64
|
||||||
|
- stable-amd64
|
||||||
|
- ${CI_COMMIT_SHA:0:8}-amd64
|
||||||
|
|
||||||
|
# FE workflow runs only on linux/amd64
|
||||||
|
docker-stable-e2e-amd64:
|
||||||
|
image: *kaniko_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||||
|
settings:
|
||||||
|
<<: *docker_variables
|
||||||
|
dockerfile: Dockerfile-e2e
|
||||||
|
tags:
|
||||||
|
- stable-e2e
|
||||||
|
|
||||||
|
docker-stable-tag-amd64:
|
||||||
|
image: *kaniko_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
|
||||||
|
settings:
|
||||||
|
<<: *docker_variables
|
||||||
|
tags:
|
||||||
|
- <<: *amd64_tags
|
||||||
|
- ${CI_COMMIT_TAG}-amd64
|
||||||
|
|
||||||
|
docker-stable-arm64:
|
||||||
|
image: *kaniko_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG == ""'
|
||||||
|
settings:
|
||||||
|
<<: *docker_variables
|
||||||
|
tags: &arm64_tags
|
||||||
|
- latest-arm64
|
||||||
|
- stable-arm64
|
||||||
|
- ${CI_COMMIT_SHA:0:8}-arm64
|
||||||
|
|
||||||
|
docker-stable-tag-arm64:
|
||||||
|
image: *kaniko_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable" && CI_COMMIT_TAG != ""'
|
||||||
|
settings:
|
||||||
|
<<: *docker_variables
|
||||||
|
tags:
|
||||||
|
- <<: *arm64_tags
|
||||||
|
- ${CI_COMMIT_TAG}-arm64
|
||||||
78
.woodpecker/lint.yaml
Normal file
78
.woodpecker/lint.yaml
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
|
|
||||||
|
variables:
|
||||||
|
script_file_entrypoint: &script_file_entrypoint
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
mix-format:
|
||||||
|
image: &elixir-image
|
||||||
|
docker.io/elixir:1.15-alpine
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
failure: ignore
|
||||||
|
commands:
|
||||||
|
- |
|
||||||
|
if ! mix format --check-formatted; then
|
||||||
|
touch fail.stamp
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
credo:
|
||||||
|
image: *elixir-image
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
failure: ignore
|
||||||
|
environment:
|
||||||
|
MIX_ENV: test
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
|
||||||
|
- adduser -D -h /home/testuser testuser
|
||||||
|
- mkdir -p /home/testuser/.mix /home/testuser/.hex
|
||||||
|
- chown -R testuser:testuser . /home/testuser
|
||||||
|
- su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix deps.get"
|
||||||
|
- |
|
||||||
|
if ! su testuser -c "HOME=/home/testuser mix analyze"; then
|
||||||
|
touch fail.stamp
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# cycles:
|
||||||
|
# image: *elixir-image
|
||||||
|
# failure: ignore
|
||||||
|
# commands:
|
||||||
|
# - apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
|
||||||
|
# - adduser -D -h /home/testuser testuser
|
||||||
|
# - mkdir -p /home/testuser/.mix /home/testuser/.hex
|
||||||
|
# - chown -R testuser:testuser . /home/testuser
|
||||||
|
# - su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||||
|
# - su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||||
|
# - su testuser -c "HOME=/home/testuser mix compile"
|
||||||
|
# - |
|
||||||
|
# if ! su testuser -c "HOME=/home/testuser mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != \"No cycles found\")}'"; then
|
||||||
|
# touch fail.stamp
|
||||||
|
# exit 1
|
||||||
|
# fi
|
||||||
|
|
||||||
|
ensure-status:
|
||||||
|
image: *elixir-image
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
commands: |
|
||||||
|
if test -f fail.stamp; then
|
||||||
|
echo "One or more previous steps fails. Failing workflow..."
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
echo "All steps passed"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
292
.woodpecker/otp-musl.yaml
Normal file
292
.woodpecker/otp-musl.yaml
Normal file
|
|
@ -0,0 +1,292 @@
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
- event: tag
|
||||||
|
- event: manual
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- event: manual
|
||||||
|
branch: stable
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm
|
||||||
|
- linux/arm64
|
||||||
|
|
||||||
|
# This is needed for the when clauses below.
|
||||||
|
# When the platform clause is fixed, this might not be needed anymore
|
||||||
|
labels:
|
||||||
|
platform: ${platform}
|
||||||
|
|
||||||
|
variables:
|
||||||
|
script_file_entrypoint: &script_file_entrypoint
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||||
|
build_cmds: &build_cmds
|
||||||
|
- apk add git build-base cmake file-dev openssl vips-dev zip
|
||||||
|
- echo "import Config" > config/prod.secret.exs
|
||||||
|
- mix local.hex --force
|
||||||
|
- mix local.rebar --force
|
||||||
|
- mix deps.get --only prod
|
||||||
|
- mkdir release
|
||||||
|
- export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH}
|
||||||
|
- mix release --path release
|
||||||
|
build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
|
||||||
|
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3-alpine
|
||||||
|
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-alpine-3.22.1
|
||||||
|
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
|
||||||
|
artifacts_uploader_settings: &artifacts_uploader_settings
|
||||||
|
user:
|
||||||
|
from_secret: pleroma-ci-user
|
||||||
|
password:
|
||||||
|
from_secret: pleroma-ci-password
|
||||||
|
owner: 'pleroma'
|
||||||
|
env: &env
|
||||||
|
MIX_ENV: prod
|
||||||
|
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
|
||||||
|
|
||||||
|
steps:
|
||||||
|
otp-develop-amd64-musl:
|
||||||
|
image: *build_image_amd64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
environment: *env
|
||||||
|
commands: &amd64_build
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release
|
||||||
|
|
||||||
|
otp-stable-amd64-musl:
|
||||||
|
image: *build_image_amd64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||||
|
environment: *env
|
||||||
|
commands: *amd64_build
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
otp-stable-tag-amd64-musl:
|
||||||
|
image: *build_image_amd64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
environment: *env
|
||||||
|
commands:
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip release
|
||||||
|
|
||||||
|
otp-develop-arm-musl:
|
||||||
|
image: *build_image_arm
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
environment: *env
|
||||||
|
commands: &arm_build
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip release
|
||||||
|
|
||||||
|
otp-stable-arm-musl:
|
||||||
|
image: *build_image_arm
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||||
|
environment: *env
|
||||||
|
commands: *arm_build
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
otp-stable-tag-arm-musl:
|
||||||
|
image: *build_image_arm
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
environment: *env
|
||||||
|
commands:
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip release
|
||||||
|
|
||||||
|
otp-develop-arm64-musl:
|
||||||
|
image: *build_image_arm64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
environment: *env
|
||||||
|
commands: &arm64_build
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release
|
||||||
|
|
||||||
|
otp-stable-arm64-musl:
|
||||||
|
image: *build_image_arm64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||||
|
environment: *env
|
||||||
|
commands: *arm64_build
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
otp-stable-tag-arm64-musl:
|
||||||
|
image: *build_image_arm64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
environment: *env
|
||||||
|
commands:
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip release
|
||||||
|
|
||||||
|
upload-artifacts-amd64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl
|
||||||
|
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||||
|
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-latest-amd64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64-musl
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-artifacts-tag-amd64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-amd64-musl
|
||||||
|
package_version: stable-${CI_COMMIT_SHA:0:8}-amd64-musl
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||||
|
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-latest-tag-amd64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-amd64-musl
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64-musl.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-artifacts-arm-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl
|
||||||
|
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||||
|
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-latest-arm-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm-musl
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-artifacts-tag-arm-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm-musl
|
||||||
|
package_version: stable-${CI_COMMIT_SHA:0:8}-arm-musl
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||||
|
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-latest-tag-arm-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm-musl
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm-musl.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-artifacts-arm64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl
|
||||||
|
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||||
|
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-latest-arm64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64-musl
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-artifacts-tag-arm64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm64-musl
|
||||||
|
package_version: stable-${CI_COMMIT_SHA:0:8}-arm64-musl
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||||
|
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-latest-tag-arm64-musl:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm64-musl
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64-musl.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
293
.woodpecker/otp.yaml
Normal file
293
.woodpecker/otp.yaml
Normal file
|
|
@ -0,0 +1,293 @@
|
||||||
|
when:
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
- event: tag
|
||||||
|
- event: manual
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
- event: manual
|
||||||
|
branch: stable
|
||||||
|
|
||||||
|
matrix:
|
||||||
|
platform:
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm
|
||||||
|
- linux/arm64
|
||||||
|
|
||||||
|
# This is needed for the when clauses below.
|
||||||
|
# When the platform clause is fixed, this might not be needed anymore
|
||||||
|
labels:
|
||||||
|
platform: ${platform}
|
||||||
|
|
||||||
|
variables:
|
||||||
|
script_file_entrypoint: &script_file_entrypoint
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||||
|
build_cmds: &build_cmds
|
||||||
|
- apt-get update && apt-get install -y cmake libmagic-dev libvips-dev erlang-dev git build-essential zip
|
||||||
|
- echo "import Config" > config/prod.secret.exs
|
||||||
|
- mix local.hex --force
|
||||||
|
- mix local.rebar --force
|
||||||
|
- mix deps.get --only prod
|
||||||
|
- mkdir release
|
||||||
|
- export PLEROMA_BUILD_BRANCH=${CI_COMMIT_BRANCH}
|
||||||
|
- mix release --path release
|
||||||
|
build_image_amd64: &build_image_amd64 docker.io/hexpm/elixir-amd64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
|
||||||
|
build_image_arm: &build_image_arm docker.io/arm32v7/elixir:1.17.3
|
||||||
|
build_image_arm64: &build_image_arm64 docker.io/hexpm/elixir-arm64:1.17.3-erlang-27.3.4.2-ubuntu-noble-20250716
|
||||||
|
artifacts_uploader_image: &artifacts_uploader_image docker.io/woodpeckercommunity/plugin-gitea-package:0.5.0
|
||||||
|
artifacts_uploader_settings: &artifacts_uploader_settings
|
||||||
|
user:
|
||||||
|
from_secret: pleroma-ci-user
|
||||||
|
password:
|
||||||
|
from_secret: pleroma-ci-password
|
||||||
|
owner: 'pleroma'
|
||||||
|
env: &env
|
||||||
|
MIX_ENV: prod
|
||||||
|
VIX_COMPILATION_MODE: PLATFORM_PROVIDED_LIBVIPS
|
||||||
|
DEBIAN_FRONTEND: noninteractive
|
||||||
|
|
||||||
|
steps:
|
||||||
|
otp-develop-amd64:
|
||||||
|
image: *build_image_amd64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
environment: *env
|
||||||
|
commands: &amd64_build
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip release
|
||||||
|
|
||||||
|
otp-stable-amd64:
|
||||||
|
image: *build_image_amd64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||||
|
environment: *env
|
||||||
|
commands: *amd64_build
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
otp-stable-tag-amd64:
|
||||||
|
image: *build_image_amd64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
environment: *env
|
||||||
|
commands:
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-amd64.zip release
|
||||||
|
|
||||||
|
otp-develop-arm:
|
||||||
|
image: *build_image_arm
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
environment: *env
|
||||||
|
commands: &arm_build
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip release
|
||||||
|
|
||||||
|
otp-stable-arm:
|
||||||
|
image: *build_image_arm
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||||
|
environment: *env
|
||||||
|
commands: *arm_build
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
otp-stable-tag-arm:
|
||||||
|
image: *build_image_arm
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
environment: *env
|
||||||
|
commands:
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm.zip release
|
||||||
|
|
||||||
|
otp-develop-arm64:
|
||||||
|
image: *build_image_arm64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
environment: *env
|
||||||
|
commands: &arm64_build
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip release
|
||||||
|
|
||||||
|
otp-stable-arm64:
|
||||||
|
image: *build_image_arm64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual" && CI_COMMIT_BRANCH == "stable"'
|
||||||
|
environment: *env
|
||||||
|
commands: *arm64_build
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
otp-stable-tag-arm64:
|
||||||
|
image: *build_image_arm64
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
environment: *env
|
||||||
|
commands:
|
||||||
|
- <<: *build_cmds
|
||||||
|
- zip -9rq stable-${CI_COMMIT_SHA:0:8}-arm64.zip release
|
||||||
|
|
||||||
|
upload-artifacts-amd64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64
|
||||||
|
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||||
|
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-latest-amd64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-amd64
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-artifacts-tag-amd64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-amd64
|
||||||
|
package_version: stable-${CI_COMMIT_SHA:0:8}-amd64
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||||
|
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-latest-tag-amd64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/amd64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-amd64
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-amd64.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-artifacts-arm:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm
|
||||||
|
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||||
|
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-latest-arm:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-artifacts-tag-arm:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm
|
||||||
|
package_version: stable-${CI_COMMIT_SHA:0:8}-arm
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||||
|
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-latest-tag-arm:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-artifacts-arm64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64
|
||||||
|
package_version: ${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||||
|
file_name: pleroma-${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
upload-latest-arm64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "push" && CI_COMMIT_BRANCH == "${CI_REPO_DEFAULT_BRANCH}"'
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "manual"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-${CI_COMMIT_BRANCH}-arm64
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./${CI_COMMIT_BRANCH}-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-artifacts-tag-arm64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm64
|
||||||
|
package_version: stable-${CI_COMMIT_SHA:0:8}-arm64
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||||
|
file_name: pleroma-stable-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||||
|
update: 'true'
|
||||||
|
|
||||||
|
# Tag events don't have CI_COMMIT_BRANCH set, hardcode stable
|
||||||
|
upload-latest-tag-arm64:
|
||||||
|
image: *artifacts_uploader_image
|
||||||
|
when:
|
||||||
|
- evaluate: 'platform == "linux/arm64" && CI_PIPELINE_EVENT == "tag"'
|
||||||
|
settings:
|
||||||
|
<<: *artifacts_uploader_settings
|
||||||
|
package_name: pleroma-otp-stable-arm64
|
||||||
|
package_version: latest
|
||||||
|
file_source: ./stable-${CI_COMMIT_SHA:0:8}-arm64.zip
|
||||||
|
file_name: pleroma.zip
|
||||||
|
update: 'true'
|
||||||
45
.woodpecker/unit-testing-elixir-1.15.yaml
Normal file
45
.woodpecker/unit-testing-elixir-1.15.yaml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- lint
|
||||||
|
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
|
|
||||||
|
variables:
|
||||||
|
script_file_entrypoint: &script_file_entrypoint
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
unit-testing-elixir-1.15:
|
||||||
|
image: elixir:1.15-alpine
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
environment:
|
||||||
|
MIX_ENV: test
|
||||||
|
DB_HOST: postgres
|
||||||
|
DB_PORT: 5432
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
|
||||||
|
- adduser -D -h /home/testuser testuser
|
||||||
|
- mkdir -p /home/testuser/.mix /home/testuser/.hex
|
||||||
|
- chown -R testuser:testuser . /home/testuser
|
||||||
|
- su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix deps.get"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --preload-modules"
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:13-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: pleroma_test
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
45
.woodpecker/unit-testing-elixir-1.18.yaml
Normal file
45
.woodpecker/unit-testing-elixir-1.18.yaml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
when:
|
||||||
|
- event: pull_request
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
evaluate: 'CI_COMMIT_SOURCE_BRANCH != "weblate"'
|
||||||
|
- event: push
|
||||||
|
branch: ${CI_REPO_DEFAULT_BRANCH}
|
||||||
|
path: [ "**/*.ex", "**/*.eex", "**/*.exs", "mix.lock", ".woodpecker/**" ]
|
||||||
|
|
||||||
|
depends_on:
|
||||||
|
- lint
|
||||||
|
|
||||||
|
labels:
|
||||||
|
platform: linux/amd64
|
||||||
|
|
||||||
|
variables:
|
||||||
|
script_file_entrypoint: &script_file_entrypoint
|
||||||
|
- /bin/sh
|
||||||
|
- -c
|
||||||
|
- 'printf "%s" "$CI_SCRIPT" | base64 -d > /tmp/ci-script.sh && /bin/sh -xe /tmp/ci-script.sh'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
unit-testing-elixir-1.18:
|
||||||
|
image: elixir:1.18-otp-27-alpine
|
||||||
|
entrypoint: *script_file_entrypoint
|
||||||
|
environment:
|
||||||
|
MIX_ENV: test
|
||||||
|
DB_HOST: postgres
|
||||||
|
DB_PORT: 5432
|
||||||
|
commands:
|
||||||
|
- apk add --no-cache build-base cmake exiftool ffmpeg file-dev git openssl
|
||||||
|
- adduser -D -h /home/testuser testuser
|
||||||
|
- mkdir -p /home/testuser/.mix /home/testuser/.hex
|
||||||
|
- chown -R testuser:testuser . /home/testuser
|
||||||
|
- su testuser -c "HOME=/home/testuser mix local.hex --force"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix local.rebar --force"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix deps.get"
|
||||||
|
- su testuser -c "HOME=/home/testuser mix pleroma.test_runner --preload-modules"
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:13-alpine
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: pleroma_test
|
||||||
|
POSTGRES_USER: postgres
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
54
CHANGELOG.md
54
CHANGELOG.md
|
|
@ -4,6 +4,60 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## 2.10.2
|
||||||
|
|
||||||
|
### Security
|
||||||
|
|
||||||
|
- ActivityPub: Fixed failed-signature inbox retry handling and signer identity checks to prevent spoofed remote activities from being processed
|
||||||
|
|
||||||
|
## 2.10.1
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- Move avatar_description and header_description fields to the account object
|
||||||
|
- Update Bandit to 1.10.4
|
||||||
|
- No-op code correctness improvements detected by Elixir 1.19 compiler
|
||||||
|
- Downgrade Hackney to 1.20.1
|
||||||
|
- Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney
|
||||||
|
- Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation
|
||||||
|
- Paginate follow requests
|
||||||
|
- Moved Phoenix LiveDashboard to /pleroma/live_dashboard
|
||||||
|
- Add mute/block expiry to the relationship object
|
||||||
|
- Filter indexable activities before inserting indexing jobs into the queue.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- Allow assigning users to reports
|
||||||
|
- Allow fine-grained announce visibilities
|
||||||
|
- Add immutable tag on cache-control header for several endpoints that's serving the same exact things.
|
||||||
|
- Add reasonable defaults for :database_config_whitelist
|
||||||
|
- Support lists `exclusive` param
|
||||||
|
- Add v1/instance/domain_blocks endpoint
|
||||||
|
- Add /api/v2/instance profile fields limits info used by Mastodon
|
||||||
|
- Added Oban Web dashboard located at /pleroma/oban
|
||||||
|
- Add instructions on how to run a release in docker, to make it easier to run on older distros.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- Fix the daily email digest job which was not executing
|
||||||
|
- Encode custom emoji URLs in EmojiReact activity tags.
|
||||||
|
- Gopher: Fix Ranch listener not being stopped properly on Pleroma restart when database configuration is enabled
|
||||||
|
- Fix fetching Hubzilla Actors with alsoKnownAs as string
|
||||||
|
- Fix /phoenix/live_dashboard redirect not working when user added a path segment
|
||||||
|
- Fix 404 error codes for missing static files
|
||||||
|
- Fix OAuth app registration to accept `redirect_uris` as an array of strings (RFC 7591), while keeping backwards compatibility with string input.
|
||||||
|
- Correct old migrations for expiring activities and user access tokens.
|
||||||
|
- Federate `votersCount` correctly
|
||||||
|
- DB prune: Check if user follows hashtag with no objects before deletion
|
||||||
|
- Stop the rate limiter from crashing when run with wrong settings.
|
||||||
|
- Restore embeds route
|
||||||
|
- ReverseProxy: Recursively follow redirects until redirect_limit is reached
|
||||||
|
- Fix compilation with vips-8.18.0 with bumping to vix 0.36.0
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
- Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS)
|
||||||
|
|
||||||
## 2.10
|
## 2.10
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,10 @@ ARG ELIXIR_IMG=hexpm/elixir
|
||||||
ARG ELIXIR_VER=1.17.3
|
ARG ELIXIR_VER=1.17.3
|
||||||
ARG ERLANG_VER=26.2.5.6
|
ARG ERLANG_VER=26.2.5.6
|
||||||
ARG ALPINE_VER=3.17.9
|
ARG ALPINE_VER=3.17.9
|
||||||
|
ARG ELIXIR_TAG=${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER}
|
||||||
|
ARG ALPINE_IMG=alpine
|
||||||
|
|
||||||
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} AS build
|
FROM ${ELIXIR_IMG}:${ELIXIR_TAG} AS build
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
|
@ -20,7 +22,7 @@ RUN apk add git gcc g++ musl-dev make cmake file-dev vips-dev &&\
|
||||||
mkdir release &&\
|
mkdir release &&\
|
||||||
mix release --path release
|
mix release --path release
|
||||||
|
|
||||||
FROM alpine:${ALPINE_VER}
|
FROM ${ALPINE_IMG}:${ALPINE_VER}
|
||||||
|
|
||||||
ARG BUILD_DATE
|
ARG BUILD_DATE
|
||||||
ARG VCS_REF
|
ARG VCS_REF
|
||||||
|
|
|
||||||
61
Dockerfile-e2e
Normal file
61
Dockerfile-e2e
Normal 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"]
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<img src="https://git.pleroma.social/pleroma/pleroma/uploads/8cec84f5a084d887339f57deeb8a293e/pleroma-banner-vector-nopad-notext.svg" width="300px" />
|
<img src="https://git.pleroma.social/attachments/06a95f5a-7cac-42ad-8b1d-1483f1739f38" width="300px" />
|
||||||
|
|
||||||
## About
|
## About
|
||||||
|
|
||||||
|
|
|
||||||
1
changelog.d/captcha-package.fix
Normal file
1
changelog.d/captcha-package.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Switch native captcha to the published pleroma_captcha Hex package.
|
||||||
1
changelog.d/context-cleanup.skip
Normal file
1
changelog.d/context-cleanup.skip
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
litepub-0.1.jsonld cleanup
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Encode custom emoji URLs in EmojiReact activity tags.
|
|
||||||
1
changelog.d/featured-collection-page.fix
Normal file
1
changelog.d/featured-collection-page.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Support fetching featured collection items from the first collection page
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Use a custom redirect handler to ensure MediaProxy redirects are followed with Hackney
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Update Hackney, the default HTTP client, to the latest release which supports Happy Eyeballs for improved IPv6 federation
|
|
||||||
1
changelog.d/host-header-verification.security
Normal file
1
changelog.d/host-header-verification.security
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Ensure Host header is present and matches instance URI
|
||||||
1
changelog.d/http-signatures-0.1.3.fix
Normal file
1
changelog.d/http-signatures-0.1.3.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix compatibility with timestamped HTTP Signatures used by GoToSocial
|
||||||
1
changelog.d/iceshrimpnet-reports-fix.fix
Normal file
1
changelog.d/iceshrimpnet-reports-fix.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Handle reports with just actor ap id as the object
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Docs: Removed outdated, incorrect, unmaintained and inappropriate installation documentation (Arch, NetBSD, NixOS)
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Add v1/instance/domain_blocks endpoint
|
|
||||||
1
changelog.d/majic-1.2.0.change
Normal file
1
changelog.d/majic-1.2.0.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Update majic to 1.2.0.
|
||||||
1
changelog.d/mastodon-websocket-protocol.fix
Normal file
1
changelog.d/mastodon-websocket-protocol.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Echo Mastodon-style `Sec-WebSocket-Protocol` tokens in streaming WebSocket handshakes.
|
||||||
1
changelog.d/mfm-backend.add
Normal file
1
changelog.d/mfm-backend.add
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add backend support for Misskey Markdown (MFM) posts
|
||||||
0
changelog.d/mfm-extend.skip
Normal file
0
changelog.d/mfm-extend.skip
Normal 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.
|
|
||||||
1
changelog.d/oban-plugins-lazarus-hex.change
Normal file
1
changelog.d/oban-plugins-lazarus-hex.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Use the Hex package for oban_plugins_lazarus.
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Added Oban Web dashboard located at /pleroma/oban
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Paginate follow requests
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Moved Phoenix LiveDashboard to /pleroma/live_dashboard
|
|
||||||
1
changelog.d/phoenix-sec-websocket-headers.change
Normal file
1
changelog.d/phoenix-sec-websocket-headers.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Switch patched Phoenix 1.7.14 back to upstream with Phoenix 1.8.0+
|
||||||
1
changelog.d/pleroma-fe-link.fix
Normal file
1
changelog.d/pleroma-fe-link.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Updated Pleroma-FE build URL after Forgejo migration
|
||||||
1
changelog.d/poll-voters-count-inflation.fix
Normal file
1
changelog.d/poll-voters-count-inflation.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Fix votersCount inflation when same voter picks multiple options
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Reduce the number of flaky tests by making them sync if they affect the global state, and silence noisy test output.
|
|
||||||
1
changelog.d/reject-third-party-reports.fix
Normal file
1
changelog.d/reject-third-party-reports.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Reject incoming reports when both the reporter and reported account are remote
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Add instructions on how to run a release in docker, to make it easier to run on older distros.
|
|
||||||
1
changelog.d/remote-ip-hex.change
Normal file
1
changelog.d/remote-ip-hex.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Use upstream Hex releases for the `remote_ip` dependency and expose client IP ranges for remote IP resolution.
|
||||||
1
changelog.d/user-search-sorting.change
Normal file
1
changelog.d/user-search-sorting.change
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Improve user search / autocompletion ordering.
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
Fix compilation with vips-8.18.0 with bumping to vix 0.36.0
|
|
||||||
0
changelog.d/weblate-ci.skip
Normal file
0
changelog.d/weblate-ci.skip
Normal file
1
changelog.d/wss-necroposts.fix
Normal file
1
changelog.d/wss-necroposts.fix
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
RichMedia: Fix backfill causing old posts to show up on timelines by disabling it in MastoAPI StatusView
|
||||||
|
|
@ -203,7 +203,8 @@ config :pleroma, :instance,
|
||||||
"text/plain",
|
"text/plain",
|
||||||
"text/html",
|
"text/html",
|
||||||
"text/markdown",
|
"text/markdown",
|
||||||
"text/bbcode"
|
"text/bbcode",
|
||||||
|
"text/x.misskeymarkdown"
|
||||||
],
|
],
|
||||||
autofollowed_nicknames: [],
|
autofollowed_nicknames: [],
|
||||||
autofollowing_nicknames: [],
|
autofollowing_nicknames: [],
|
||||||
|
|
@ -737,6 +738,7 @@ config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true, min_lifeti
|
||||||
config :pleroma, Pleroma.Web.Plugs.RemoteIp,
|
config :pleroma, Pleroma.Web.Plugs.RemoteIp,
|
||||||
enabled: true,
|
enabled: true,
|
||||||
headers: ["x-forwarded-for"],
|
headers: ["x-forwarded-for"],
|
||||||
|
clients: [],
|
||||||
proxies: [],
|
proxies: [],
|
||||||
reserved: [
|
reserved: [
|
||||||
"127.0.0.0/8",
|
"127.0.0.0/8",
|
||||||
|
|
@ -775,7 +777,7 @@ config :pleroma, :frontends,
|
||||||
"name" => "pleroma-fe",
|
"name" => "pleroma-fe",
|
||||||
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
|
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
|
||||||
"build_url" =>
|
"build_url" =>
|
||||||
"https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build",
|
"https://git.pleroma.social/api/packages/pleroma/generic/pleroma-fe-builds/${ref}/latest.zip",
|
||||||
"ref" => "develop"
|
"ref" => "develop"
|
||||||
},
|
},
|
||||||
"fedi-fe" => %{
|
"fedi-fe" => %{
|
||||||
|
|
@ -960,6 +962,15 @@ config :pleroma, Pleroma.Search.QdrantSearch,
|
||||||
vectors: %{size: 384, distance: "Cosine"}
|
vectors: %{size: 384, distance: "Cosine"}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
config :pleroma, :database_config_whitelist, [
|
||||||
|
{:pleroma},
|
||||||
|
{:cors_plug},
|
||||||
|
{:ex_aws, :s3},
|
||||||
|
{:mime},
|
||||||
|
{:prometheus, Pleroma.Web.Endpoint.MetricsExporter},
|
||||||
|
{:web_push_encryption, :vapid_details}
|
||||||
|
]
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
|
||||||
|
|
@ -815,7 +815,8 @@ config :pleroma, :config_description, [
|
||||||
"text/plain",
|
"text/plain",
|
||||||
"text/html",
|
"text/html",
|
||||||
"text/markdown",
|
"text/markdown",
|
||||||
"text/bbcode"
|
"text/bbcode",
|
||||||
|
"text/x.misskeymarkdown"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
|
|
@ -1394,7 +1395,13 @@ config :pleroma, :config_description, [
|
||||||
label: "Post Content Type",
|
label: "Post Content Type",
|
||||||
type: {:dropdown, :atom},
|
type: {:dropdown, :atom},
|
||||||
description: "Default post formatting option",
|
description: "Default post formatting option",
|
||||||
suggestions: ["text/plain", "text/html", "text/markdown", "text/bbcode"]
|
suggestions: [
|
||||||
|
"text/plain",
|
||||||
|
"text/html",
|
||||||
|
"text/markdown",
|
||||||
|
"text/bbcode",
|
||||||
|
"text/x.misskeymarkdown"
|
||||||
|
]
|
||||||
},
|
},
|
||||||
%{
|
%{
|
||||||
key: :redirectRootNoLogin,
|
key: :redirectRootNoLogin,
|
||||||
|
|
@ -2903,7 +2910,7 @@ config :pleroma, :config_description, [
|
||||||
key: Pleroma.Web.Plugs.RemoteIp,
|
key: Pleroma.Web.Plugs.RemoteIp,
|
||||||
type: :group,
|
type: :group,
|
||||||
description: """
|
description: """
|
||||||
`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://git.pleroma.social/pleroma/remote_ip) but with runtime configuration.
|
`Pleroma.Web.Plugs.RemoteIp` is a shim to call [`RemoteIp`](https://hex.pm/packages/remote_ip) but with runtime configuration.
|
||||||
**If your instance is not behind at least one reverse proxy, you should not enable this plug.**
|
**If your instance is not behind at least one reverse proxy, you should not enable this plug.**
|
||||||
""",
|
""",
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -2919,6 +2926,12 @@ config :pleroma, :config_description, [
|
||||||
A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`.
|
A list of strings naming the HTTP headers to use when deriving the true client IP. Default: `["x-forwarded-for"]`.
|
||||||
"""
|
"""
|
||||||
},
|
},
|
||||||
|
%{
|
||||||
|
key: :clients,
|
||||||
|
type: {:list, :string},
|
||||||
|
description:
|
||||||
|
"A list of client IPs or subnets in CIDR notation. These will not be treated as proxies or reserved ranges. Defaults to `[]`. IPv4 entries without a bitmask will be assumed to be /32 and IPv6 /128."
|
||||||
|
},
|
||||||
%{
|
%{
|
||||||
key: :proxies,
|
key: :proxies,
|
||||||
type: {:list, :string},
|
type: {:list, :string},
|
||||||
|
|
|
||||||
70
docker-entrypoint-e2e.sh
Executable file
70
docker-entrypoint-e2e.sh
Executable 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"
|
||||||
|
|
@ -169,4 +169,18 @@ This forcibly removes any enabled MRF that does not exist and will fix the abili
|
||||||
=== "From Source"
|
=== "From Source"
|
||||||
```sh
|
```sh
|
||||||
mix pleroma.config fix_mrf_policies
|
mix pleroma.config fix_mrf_policies
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Remove non-whitelisted configs from the database
|
||||||
|
|
||||||
|
This removes any configuration value that is not explicitly whitelisted by `:pleroma, :database_config_whitelist`. Might be useful after updating the whitelist.
|
||||||
|
|
||||||
|
=== "OTP"
|
||||||
|
```sh
|
||||||
|
./bin/pleroma_ctl config filter_whitelisted
|
||||||
|
```
|
||||||
|
|
||||||
|
=== "From Source"
|
||||||
|
```sh
|
||||||
|
mix pleroma.config filter_whitelisted
|
||||||
|
```
|
||||||
|
|
|
||||||
|
|
@ -1132,8 +1132,9 @@ Boolean, enables/disables in-database configuration. Read [Transferring the conf
|
||||||
|
|
||||||
List of valid configuration sections which are allowed to be configured from the
|
List of valid configuration sections which are allowed to be configured from the
|
||||||
database. Settings stored in the database before the whitelist is configured are
|
database. Settings stored in the database before the whitelist is configured are
|
||||||
still applied, so it is suggested to only use the whitelist on instances that
|
still applied. Consider running the `mix pleroma.config filter_whitelisted` task
|
||||||
have not migrated the config to the database.
|
after updating the whitelist. Read [Remove non-whitelisted configs from the database](../administration/CLI_tasks/config.md#remove-non-whitelisted-configs-from-the-database)
|
||||||
|
for more information.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
```elixir
|
```elixir
|
||||||
|
|
|
||||||
|
|
@ -665,6 +665,7 @@ Status: 404
|
||||||
- *optional* `limit`: **integer** the number of records to retrieve
|
- *optional* `limit`: **integer** the number of records to retrieve
|
||||||
- *optional* `page`: **integer** page number
|
- *optional* `page`: **integer** page number
|
||||||
- *optional* `page_size`: **integer** number of log entries per page (default is `50`)
|
- *optional* `page_size`: **integer** number of log entries per page (default is `50`)
|
||||||
|
- *optional* `assigned_account`: **string** assigned account ID
|
||||||
- Response:
|
- Response:
|
||||||
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
|
- On failure: 403 Forbidden error `{"error": "error_msg"}` when requested by anonymous or non-admin
|
||||||
- On success: JSON, returns a list of reports, where:
|
- On success: JSON, returns a list of reports, where:
|
||||||
|
|
@ -749,6 +750,7 @@ Status: 404
|
||||||
"url": "https://pleroma.example.org/users/lain",
|
"url": "https://pleroma.example.org/users/lain",
|
||||||
"username": "lain"
|
"username": "lain"
|
||||||
},
|
},
|
||||||
|
"assigned_account": null,
|
||||||
"content": "Please delete it",
|
"content": "Please delete it",
|
||||||
"created_at": "2019-04-29T19:48:15.000Z",
|
"created_at": "2019-04-29T19:48:15.000Z",
|
||||||
"id": "9iJGOv1j8hxuw19bcm",
|
"id": "9iJGOv1j8hxuw19bcm",
|
||||||
|
|
@ -868,6 +870,37 @@ Status: 404
|
||||||
]
|
]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
- Response:
|
||||||
|
- On failure:
|
||||||
|
- 400 Bad Request, JSON:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
`id`, // report id
|
||||||
|
`error` // error message
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
- On success: `204`, empty response
|
||||||
|
|
||||||
|
## `POST /api/v1/pleroma/admin/reports/assign_account`
|
||||||
|
|
||||||
|
### Assign account to one or multiple reports
|
||||||
|
|
||||||
|
- Params:
|
||||||
|
|
||||||
|
```json
|
||||||
|
`reports`: [
|
||||||
|
{
|
||||||
|
`id`, // required, report id
|
||||||
|
`nickname` // account nickname, use null to unassign account
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
- Response:
|
- Response:
|
||||||
- On failure:
|
- On failure:
|
||||||
- 400 Bad Request, JSON:
|
- 400 Bad Request, JSON:
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,61 @@ defmodule Mix.Tasks.Pleroma.Config do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Removes non-whitelisted configuration sections
|
||||||
|
def run(["filter_whitelisted" | rest]) do
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(
|
||||||
|
rest,
|
||||||
|
strict: [force: :boolean],
|
||||||
|
aliases: [f: :force]
|
||||||
|
)
|
||||||
|
|
||||||
|
force = Keyword.get(options, :force, false)
|
||||||
|
|
||||||
|
start_pleroma()
|
||||||
|
|
||||||
|
whitelisted_configs = Pleroma.Config.get(:database_config_whitelist)
|
||||||
|
|
||||||
|
if whitelisted_configs in [nil, false] do
|
||||||
|
shell_error("No unwanted settings in ConfigDB. No changes made.")
|
||||||
|
else
|
||||||
|
whitelisted_groups =
|
||||||
|
whitelisted_configs
|
||||||
|
|> Enum.filter(fn
|
||||||
|
{_group} -> true
|
||||||
|
_ -> false
|
||||||
|
end)
|
||||||
|
|> Enum.map(fn {group} -> group end)
|
||||||
|
|
||||||
|
whitelisted_keys =
|
||||||
|
whitelisted_configs
|
||||||
|
|> Enum.filter(fn
|
||||||
|
{_group, _key} -> true
|
||||||
|
_ -> false
|
||||||
|
end)
|
||||||
|
|
||||||
|
filtered =
|
||||||
|
from(c in ConfigDB)
|
||||||
|
|> Repo.all()
|
||||||
|
|> Enum.filter(¬_whitelisted?(&1, whitelisted_groups, whitelisted_keys))
|
||||||
|
|
||||||
|
if not Enum.empty?(filtered) do
|
||||||
|
shell_info("The following settings will be removed from ConfigDB:\n")
|
||||||
|
Enum.each(filtered, &dump(&1))
|
||||||
|
|
||||||
|
if force or shell_prompt("Are you sure you want to continue?", "n") in ~w(Yn Y y) do
|
||||||
|
filtered_ids = Enum.map(filtered, fn %{id: id} -> id end)
|
||||||
|
|
||||||
|
Repo.delete_all(from(c in ConfigDB, where: c.id in ^filtered_ids))
|
||||||
|
else
|
||||||
|
shell_error("No changes made.")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
shell_error("No unwanted settings in ConfigDB. No changes made.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@spec migrate_to_db(Path.t() | nil) :: any()
|
@spec migrate_to_db(Path.t() | nil) :: any()
|
||||||
def migrate_to_db(file_path \\ nil) do
|
def migrate_to_db(file_path \\ nil) do
|
||||||
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
|
with :ok <- Pleroma.Config.DeprecationWarnings.warn() do
|
||||||
|
|
@ -434,4 +489,9 @@ defmodule Mix.Tasks.Pleroma.Config do
|
||||||
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
|
Ecto.Adapters.SQL.query!(Repo, "TRUNCATE config;")
|
||||||
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
|
Ecto.Adapters.SQL.query!(Repo, "ALTER SEQUENCE config_id_seq RESTART;")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp not_whitelisted?(%{group: group, key: key}, whitelisted_groups, whitelisted_keys) do
|
||||||
|
not Enum.member?(whitelisted_groups, group) and
|
||||||
|
not Enum.member?(whitelisted_keys, {group, key})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 ->
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ defmodule Pleroma.ConfigDB do
|
||||||
import Pleroma.Web.Gettext
|
import Pleroma.Web.Gettext
|
||||||
|
|
||||||
alias __MODULE__
|
alias __MODULE__
|
||||||
|
alias Pleroma.EctoType.Config.RateLimit
|
||||||
alias Pleroma.Repo
|
alias Pleroma.Repo
|
||||||
|
|
||||||
@type t :: %__MODULE__{}
|
@type t :: %__MODULE__{}
|
||||||
|
|
@ -60,8 +61,59 @@ defmodule Pleroma.ConfigDB do
|
||||||
|> cast(params, [:key, :group, :value])
|
|> cast(params, [:key, :group, :value])
|
||||||
|> validate_required([:key, :group, :value])
|
|> validate_required([:key, :group, :value])
|
||||||
|> unique_constraint(:key, name: :config_group_key_index)
|
|> unique_constraint(:key, name: :config_group_key_index)
|
||||||
|
|> validate_rate_limit()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp validate_rate_limit(changeset) do
|
||||||
|
group = get_field(changeset, :group)
|
||||||
|
key = get_field(changeset, :key)
|
||||||
|
|
||||||
|
if group == :pleroma and key == :rate_limit do
|
||||||
|
value = get_field(changeset, :value)
|
||||||
|
|
||||||
|
case normalize_rate_limit(value) do
|
||||||
|
{:ok, normalized_value} ->
|
||||||
|
put_change(changeset, :value, normalized_value)
|
||||||
|
|
||||||
|
{:error, {limiter_name, reason}} ->
|
||||||
|
add_error(
|
||||||
|
changeset,
|
||||||
|
:value,
|
||||||
|
"invalid :rate_limit value for #{inspect(limiter_name)}: #{reason}"
|
||||||
|
)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
changeset
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_rate_limit(nil), do: {:ok, nil}
|
||||||
|
|
||||||
|
defp normalize_rate_limit(%{} = value), do: normalize_rate_limit(Map.to_list(value))
|
||||||
|
|
||||||
|
defp normalize_rate_limit(value) when is_list(value) do
|
||||||
|
if Keyword.keyword?(value) do
|
||||||
|
value
|
||||||
|
|> Enum.reduce_while({:ok, []}, fn {limiter_name, limiter_value}, {:ok, acc} ->
|
||||||
|
case RateLimit.cast_with_error(limiter_value) do
|
||||||
|
{:ok, normalized_limiter_value} ->
|
||||||
|
{:cont, {:ok, [{limiter_name, normalized_limiter_value} | acc]}}
|
||||||
|
|
||||||
|
{:error, reason} ->
|
||||||
|
{:halt, {:error, {limiter_name, reason}}}
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|> case do
|
||||||
|
{:ok, acc} -> {:ok, Enum.reverse(acc)}
|
||||||
|
{:error, _} = error -> error
|
||||||
|
end
|
||||||
|
else
|
||||||
|
{:error, {:rate_limit, "must be a keyword list"}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_rate_limit(_), do: {:error, {:rate_limit, "must be a keyword list"}}
|
||||||
|
|
||||||
defp create(params) do
|
defp create(params) do
|
||||||
%ConfigDB{}
|
%ConfigDB{}
|
||||||
|> changeset(params)
|
|> changeset(params)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal file
71
lib/pleroma/ecto_type/config/rate_limit.ex
Normal 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
|
||||||
|
|
@ -127,6 +127,13 @@ defmodule Pleroma.Formatter do
|
||||||
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false})
|
Earmark.as_html!(text, %Earmark.Options{compact_output: true, smartypants: false})
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def markdown_to_html(text, opts) do
|
||||||
|
Earmark.as_html!(
|
||||||
|
text,
|
||||||
|
%Earmark.Options{compact_output: true, smartypants: false} |> Map.merge(opts)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def html_escape({text, mentions, hashtags}, type) do
|
def html_escape({text, mentions, hashtags}, type) do
|
||||||
{html_escape(text, type), mentions, hashtags}
|
{html_escape(text, type), mentions, hashtags}
|
||||||
end
|
end
|
||||||
|
|
@ -135,6 +142,10 @@ defmodule Pleroma.Formatter do
|
||||||
HTML.filter_tags(text)
|
HTML.filter_tags(text)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def html_escape(text, "text/x.misskeymarkdown") do
|
||||||
|
HTML.filter_tags(text)
|
||||||
|
end
|
||||||
|
|
||||||
def html_escape(text, "text/plain") do
|
def html_escape(text, "text/plain") do
|
||||||
Regex.split(@link_regex, text, include_captures: true)
|
Regex.split(@link_regex, text, include_captures: true)
|
||||||
|> Enum.map_every(2, fn chunk ->
|
|> Enum.map_every(2, fn chunk ->
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,14 @@ defmodule Pleroma.List do
|
||||||
field(:title, :string)
|
field(:title, :string)
|
||||||
field(:following, {:array, :string}, default: [])
|
field(:following, {:array, :string}, default: [])
|
||||||
field(:ap_id, :string)
|
field(:ap_id, :string)
|
||||||
|
field(:exclusive, :boolean, default: false)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
def title_changeset(list, attrs \\ %{}) do
|
def update_changeset(list, attrs \\ %{}) do
|
||||||
list
|
list
|
||||||
|> cast(attrs, [:title])
|
|> cast(attrs, [:title, :exclusive])
|
||||||
|> validate_required([:title])
|
|> validate_required([:title])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -91,14 +92,14 @@ defmodule Pleroma.List do
|
||||||
|> Repo.all()
|
|> Repo.all()
|
||||||
end
|
end
|
||||||
|
|
||||||
def rename(%Pleroma.List{} = list, title) do
|
def update(%Pleroma.List{} = list, params) do
|
||||||
list
|
list
|
||||||
|> title_changeset(%{title: title})
|
|> update_changeset(params)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
def create(title, %User{} = creator) do
|
def create(params, %User{} = creator) do
|
||||||
changeset = title_changeset(%Pleroma.List{user_id: creator.id}, %{title: title})
|
changeset = update_changeset(%Pleroma.List{user_id: creator.id}, params)
|
||||||
|
|
||||||
if changeset.valid? do
|
if changeset.valid? do
|
||||||
Repo.transaction(fn ->
|
Repo.transaction(fn ->
|
||||||
|
|
@ -149,4 +150,14 @@ defmodule Pleroma.List do
|
||||||
end
|
end
|
||||||
|
|
||||||
def member?(_, _), do: false
|
def member?(_, _), do: false
|
||||||
|
|
||||||
|
def get_exclusive_list_members(%User{id: user_id}) do
|
||||||
|
Pleroma.List
|
||||||
|
|> where([l], l.user_id == ^user_id)
|
||||||
|
|> where([l], l.exclusive == true)
|
||||||
|
|> select([l], l.following)
|
||||||
|
|> Repo.all()
|
||||||
|
|> List.flatten()
|
||||||
|
|> Enum.uniq()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,8 @@ defmodule Pleroma.MFA.Changeset do
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
def disable(%Ecto.Changeset{} = changeset, force \\ false) do
|
def disable(%Ecto.Changeset{} = changeset, force \\ false) do
|
||||||
settings =
|
%Settings{} =
|
||||||
|
settings =
|
||||||
changeset
|
changeset
|
||||||
|> Ecto.Changeset.apply_changes()
|
|> Ecto.Changeset.apply_changes()
|
||||||
|> MFA.fetch_settings()
|
|> MFA.fetch_settings()
|
||||||
|
|
@ -20,20 +21,20 @@ defmodule Pleroma.MFA.Changeset do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def disable_totp(%User{multi_factor_authentication_settings: settings} = user) do
|
def disable_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
|
||||||
user
|
user
|
||||||
|> put_change(%Settings{settings | totp: %Settings.TOTP{}})
|
|> put_change(%Settings{settings | totp: %Settings.TOTP{}})
|
||||||
end
|
end
|
||||||
|
|
||||||
def confirm_totp(%User{multi_factor_authentication_settings: settings} = user) do
|
def confirm_totp(%User{multi_factor_authentication_settings: %Settings{} = settings} = user) do
|
||||||
totp_settings = %Settings.TOTP{settings.totp | confirmed: true}
|
totp_settings = %Settings.TOTP{(%Settings.TOTP{} = settings.totp) | confirmed: true}
|
||||||
|
|
||||||
user
|
user
|
||||||
|> put_change(%Settings{settings | totp: totp_settings, enabled: true})
|
|> put_change(%Settings{settings | totp: totp_settings, enabled: true})
|
||||||
end
|
end
|
||||||
|
|
||||||
def setup_totp(%User{} = user, attrs) do
|
def setup_totp(%User{} = user, attrs) do
|
||||||
mfa_settings = MFA.fetch_settings(user)
|
%Settings{} = mfa_settings = MFA.fetch_settings(user)
|
||||||
|
|
||||||
totp_settings =
|
totp_settings =
|
||||||
%Settings.TOTP{}
|
%Settings.TOTP{}
|
||||||
|
|
@ -46,7 +47,7 @@ defmodule Pleroma.MFA.Changeset do
|
||||||
def cast_backup_codes(%User{} = user, codes) do
|
def cast_backup_codes(%User{} = user, codes) do
|
||||||
user
|
user
|
||||||
|> put_change(%Settings{
|
|> put_change(%Settings{
|
||||||
user.multi_factor_authentication_settings
|
(%Settings{} = user.multi_factor_authentication_settings)
|
||||||
| backup_codes: codes
|
| backup_codes: codes
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -132,11 +132,18 @@ defmodule Pleroma.ModerationLog do
|
||||||
end
|
end
|
||||||
|
|
||||||
def insert_log(%{actor: %User{}, action: action, subject: %Activity{} = subject} = attrs)
|
def insert_log(%{actor: %User{}, action: action, subject: %Activity{} = subject} = attrs)
|
||||||
when action in ["report_note_delete", "report_update", "report_note"] do
|
when action in [
|
||||||
|
"report_note_delete",
|
||||||
|
"report_update",
|
||||||
|
"report_note",
|
||||||
|
"report_unassigned",
|
||||||
|
"report_assigned"
|
||||||
|
] do
|
||||||
data =
|
data =
|
||||||
attrs
|
attrs
|
||||||
|> prepare_log_data
|
|> prepare_log_data
|
||||||
|> Pleroma.Maps.put_if_present("text", attrs[:text])
|
|> Pleroma.Maps.put_if_present("text", attrs[:text])
|
||||||
|
|> Pleroma.Maps.put_if_present("assigned_account", attrs[:assigned_account])
|
||||||
|> Map.merge(%{"subject" => report_to_map(subject)})
|
|> Map.merge(%{"subject" => report_to_map(subject)})
|
||||||
|
|
||||||
insert_log_entry_with_message(%ModerationLog{data: data})
|
insert_log_entry_with_message(%ModerationLog{data: data})
|
||||||
|
|
@ -441,6 +448,35 @@ defmodule Pleroma.ModerationLog do
|
||||||
" with '#{state}' state"
|
" with '#{state}' state"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(
|
||||||
|
%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "report_assigned",
|
||||||
|
"subject" => %{"id" => subject_id, "type" => "report"},
|
||||||
|
"assigned_account" => assigned_account
|
||||||
|
}
|
||||||
|
} = log
|
||||||
|
) do
|
||||||
|
"@#{actor_nickname} assigned report ##{subject_id}" <>
|
||||||
|
subject_actor_nickname(log, " (on user ", ")") <>
|
||||||
|
" to user #{assigned_account}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_log_entry_message(
|
||||||
|
%ModerationLog{
|
||||||
|
data: %{
|
||||||
|
"actor" => %{"nickname" => actor_nickname},
|
||||||
|
"action" => "report_unassigned",
|
||||||
|
"subject" => %{"id" => subject_id, "type" => "report"}
|
||||||
|
}
|
||||||
|
} = log
|
||||||
|
) do
|
||||||
|
"@#{actor_nickname} unassigned report ##{subject_id}" <>
|
||||||
|
subject_actor_nickname(log, " (on user ", ")") <>
|
||||||
|
" from a user"
|
||||||
|
end
|
||||||
|
|
||||||
def get_log_entry_message(
|
def get_log_entry_message(
|
||||||
%ModerationLog{
|
%ModerationLog{
|
||||||
data: %{
|
data: %{
|
||||||
|
|
|
||||||
|
|
@ -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})
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
defmodule Pleroma.ReleaseTasks do
|
defmodule Pleroma.ReleaseTasks do
|
||||||
@repo Pleroma.Repo
|
@repo Pleroma.Repo
|
||||||
|
|
||||||
def run(args) do
|
# TODO: Kept for some backwards compatibility with buggy pleroma_ctl,
|
||||||
|
# if a mismatch between pleroma_ctl and Pleroma accidentaly happens.
|
||||||
|
# Remove in the future.
|
||||||
|
def run(args) when is_binary(args) do
|
||||||
[task | args] = String.split(args)
|
[task | args] = String.split(args)
|
||||||
|
|
||||||
case task do
|
case task do
|
||||||
|
|
@ -16,6 +19,20 @@ defmodule Pleroma.ReleaseTasks do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# HACK: Script arguments need to be received as a list, otherwise (quoted) arguments with
|
||||||
|
# whitespace will be broken. Previously the broken string form above was used,
|
||||||
|
# escaping in the shell does not help.
|
||||||
|
def run(args) when is_list(args) do
|
||||||
|
[task | args] = args
|
||||||
|
|
||||||
|
case task do
|
||||||
|
"migrate" -> migrate(args)
|
||||||
|
"create" -> create()
|
||||||
|
"rollback" -> rollback(args)
|
||||||
|
task -> mix_task(task, args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def find_module(task) do
|
def find_module(task) do
|
||||||
module_name =
|
module_name =
|
||||||
task
|
task
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ defmodule Pleroma.ReverseProxy do
|
||||||
@keep_resp_headers @resp_cache_headers ++
|
@keep_resp_headers @resp_cache_headers ++
|
||||||
~w(content-length content-type content-disposition content-encoding) ++
|
~w(content-length content-type content-disposition content-encoding) ++
|
||||||
~w(content-range accept-ranges vary)
|
~w(content-range accept-ranges vary)
|
||||||
@default_cache_control_header "public, max-age=1209600"
|
@default_cache_control_header "public, max-age=1209600, immutable"
|
||||||
@valid_resp_codes [200, 206, 304]
|
@valid_resp_codes [200, 206, 304]
|
||||||
@max_read_duration :timer.seconds(30)
|
@max_read_duration :timer.seconds(30)
|
||||||
@max_body_length :infinity
|
@max_body_length :infinity
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,9 @@
|
||||||
|
|
||||||
defmodule Pleroma.ReverseProxy.Client.Hackney do
|
defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
@behaviour Pleroma.ReverseProxy.Client
|
@behaviour Pleroma.ReverseProxy.Client
|
||||||
|
@redirect_limit 5
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
# In-app redirect handler to avoid Hackney redirect bugs:
|
# In-app redirect handler to avoid Hackney redirect bugs:
|
||||||
# - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
|
# - https://github.com/benoitc/hackney/issues/527 (relative/protocol-less redirects can crash Hackney)
|
||||||
|
|
@ -31,26 +34,15 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
|
|
||||||
if opts[:follow_redirect] != false do
|
if opts[:follow_redirect] != false do
|
||||||
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
|
{_state, req_opts} = Access.get_and_update(opts, :follow_redirect, fn a -> {a, false} end)
|
||||||
|
env = %{method: method, headers: headers, body: body, req_opts: req_opts}
|
||||||
res = :hackney.request(method, url, headers, body, req_opts)
|
res = :hackney.request(method, url, headers, body, req_opts)
|
||||||
|
|
||||||
case res do
|
case res do
|
||||||
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
|
{:ok, code, resp_headers, _client} when code in @redirect_statuses ->
|
||||||
:hackney.request(
|
redirect(url, resp_headers, env, @redirect_limit)
|
||||||
method,
|
|
||||||
absolute_redirect_url(url, resp_headers),
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
req_opts
|
|
||||||
)
|
|
||||||
|
|
||||||
{:ok, code, resp_headers} when code in @redirect_statuses ->
|
{:ok, code, resp_headers} when code in @redirect_statuses ->
|
||||||
:hackney.request(
|
redirect(url, resp_headers, env, @redirect_limit)
|
||||||
method,
|
|
||||||
absolute_redirect_url(url, resp_headers),
|
|
||||||
headers,
|
|
||||||
body,
|
|
||||||
req_opts
|
|
||||||
)
|
|
||||||
|
|
||||||
_ ->
|
_ ->
|
||||||
res
|
res
|
||||||
|
|
@ -71,4 +63,32 @@ defmodule Pleroma.ReverseProxy.Client.Hackney do
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def close(ref), do: :hackney.close(ref)
|
def close(ref), do: :hackney.close(ref)
|
||||||
|
|
||||||
|
defp redirect(url, resp_headers, env, limit) when limit == 0 do
|
||||||
|
new_url = absolute_redirect_url(url, resp_headers)
|
||||||
|
|
||||||
|
Logger.debug(
|
||||||
|
"#{__MODULE__}: Handling redirect #{url} -> #{new_url}; redirect limit was reached - returning response after final redirect"
|
||||||
|
)
|
||||||
|
|
||||||
|
:hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp redirect(url, resp_headers, env, limit) do
|
||||||
|
new_url = absolute_redirect_url(url, resp_headers)
|
||||||
|
Logger.debug("#{__MODULE__}: handling redirect #{url} -> #{new_url}; limit = #{limit}")
|
||||||
|
|
||||||
|
res = :hackney.request(env.method, new_url, env.headers, env.body, env.req_opts)
|
||||||
|
|
||||||
|
case res do
|
||||||
|
{:ok, code, new_resp_headers, _client} when code in @redirect_statuses ->
|
||||||
|
redirect(new_url, new_resp_headers, env, limit - 1)
|
||||||
|
|
||||||
|
{:ok, code, new_resp_headers} when code in @redirect_statuses ->
|
||||||
|
redirect(new_url, new_resp_headers, env, limit - 1)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
res
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,28 @@
|
||||||
defmodule Pleroma.Search do
|
defmodule Pleroma.Search do
|
||||||
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
alias Pleroma.Workers.SearchIndexingWorker
|
alias Pleroma.Workers.SearchIndexingWorker
|
||||||
|
|
||||||
def add_to_index(%Pleroma.Activity{id: activity_id}) do
|
@spec add_to_index(Activity.t()) :: {:ok, Oban.Job.t() | :noop} | {:error, Oban.Job.changeset()}
|
||||||
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|
def add_to_index(%Activity{id: activity_id, object: %Object{} = object} = activity) do
|
||||||
|> Oban.insert()
|
with {_, true} <- {:indexable, indexable?(activity)},
|
||||||
|
{_, "public"} <- {:visibility, Visibility.get_visibility(object)} do
|
||||||
|
SearchIndexingWorker.new(%{"op" => "add_to_index", "activity" => activity_id})
|
||||||
|
|> Oban.insert()
|
||||||
|
else
|
||||||
|
_ -> {:ok, :noop}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def add_to_index(%Activity{id: activity_id}) do
|
||||||
|
case Activity.get_by_id_with_object(activity_id) do
|
||||||
|
%Activity{} = preloaded -> add_to_index(preloaded)
|
||||||
|
_ -> {:ok, :noop}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec remove_from_index(Object.t()) :: {:ok, Oban.Job.t()} | {:error, Oban.Job.changeset()}
|
||||||
def remove_from_index(%Pleroma.Object{id: object_id}) do
|
def remove_from_index(%Pleroma.Object{id: object_id}) do
|
||||||
SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id})
|
SearchIndexingWorker.new(%{"op" => "remove_from_index", "object" => object_id})
|
||||||
|> Oban.insert()
|
|> Oban.insert()
|
||||||
|
|
@ -20,4 +37,44 @@ defmodule Pleroma.Search do
|
||||||
search_module = Pleroma.Config.get([Pleroma.Search, :module])
|
search_module = Pleroma.Config.get([Pleroma.Search, :module])
|
||||||
search_module.healthcheck_endpoints()
|
search_module.healthcheck_endpoints()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def object_to_search_data(%Object{} = object) do
|
||||||
|
data = object.data
|
||||||
|
|
||||||
|
content_str =
|
||||||
|
case data["content"] do
|
||||||
|
[nil | rest] -> to_string(rest)
|
||||||
|
str -> str
|
||||||
|
end
|
||||||
|
|
||||||
|
content =
|
||||||
|
with {:ok, scrubbed} <-
|
||||||
|
FastSanitize.Sanitizer.scrub(content_str, Pleroma.HTML.Scrubber.SearchIndexing),
|
||||||
|
trimmed <- String.trim(scrubbed) do
|
||||||
|
trimmed
|
||||||
|
end
|
||||||
|
|
||||||
|
# Make sure we have a non-empty string
|
||||||
|
if content != "" do
|
||||||
|
{:ok, published, _} = DateTime.from_iso8601(data["published"])
|
||||||
|
|
||||||
|
%{
|
||||||
|
id: object.id,
|
||||||
|
content: content,
|
||||||
|
ap: data["id"],
|
||||||
|
published: published |> DateTime.to_unix()
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp indexable?(%Activity{
|
||||||
|
data: %{"type" => "Create"},
|
||||||
|
object: %Object{
|
||||||
|
data: %{"content" => content, "published" => published, "type" => "Note"}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
when not is_nil(content) and content not in ["", "."] and not is_nil(published),
|
||||||
|
do: true
|
||||||
|
|
||||||
|
defp indexable?(_), do: false
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ defmodule Pleroma.Search.Meilisearch do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config.Getting, as: Config
|
alias Pleroma.Config.Getting, as: Config
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Search
|
||||||
|
|
||||||
import Pleroma.Search.DatabaseSearch
|
import Pleroma.Search.DatabaseSearch
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
|
|
@ -118,66 +120,24 @@ defmodule Pleroma.Search.Meilisearch do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def object_to_search_data(object) do
|
|
||||||
# Only index public or unlisted Notes
|
|
||||||
if not is_nil(object) and object.data["type"] == "Note" and
|
|
||||||
not is_nil(object.data["content"]) and
|
|
||||||
not is_nil(object.data["published"]) and
|
|
||||||
(Pleroma.Constants.as_public() in object.data["to"] or
|
|
||||||
Pleroma.Constants.as_public() in object.data["cc"]) and
|
|
||||||
object.data["content"] not in ["", "."] do
|
|
||||||
data = object.data
|
|
||||||
|
|
||||||
content_str =
|
|
||||||
case data["content"] do
|
|
||||||
[nil | rest] -> to_string(rest)
|
|
||||||
str -> str
|
|
||||||
end
|
|
||||||
|
|
||||||
content =
|
|
||||||
with {:ok, scrubbed} <-
|
|
||||||
FastSanitize.Sanitizer.scrub(content_str, Pleroma.HTML.Scrubber.SearchIndexing),
|
|
||||||
trimmed <- String.trim(scrubbed) do
|
|
||||||
trimmed
|
|
||||||
end
|
|
||||||
|
|
||||||
# Make sure we have a non-empty string
|
|
||||||
if content != "" do
|
|
||||||
{:ok, published, _} = DateTime.from_iso8601(data["published"])
|
|
||||||
|
|
||||||
%{
|
|
||||||
id: object.id,
|
|
||||||
content: content,
|
|
||||||
ap: data["id"],
|
|
||||||
published: published |> DateTime.to_unix()
|
|
||||||
}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def add_to_index(activity) do
|
def add_to_index(%Activity{object: %Object{} = object} = activity) do
|
||||||
maybe_search_data = object_to_search_data(activity.object)
|
search_data = Search.object_to_search_data(object)
|
||||||
|
|
||||||
if activity.data["type"] == "Create" and maybe_search_data do
|
result =
|
||||||
result =
|
meili_put(
|
||||||
meili_put(
|
"/indexes/objects/documents",
|
||||||
"/indexes/objects/documents",
|
[search_data]
|
||||||
[maybe_search_data]
|
)
|
||||||
)
|
|
||||||
|
|
||||||
with {:ok, %{"status" => "enqueued"}} <- result do
|
with {:ok, %{"status" => "enqueued"}} <- result do
|
||||||
# Added successfully
|
# Added successfully
|
||||||
:ok
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
# There was an error, report it
|
|
||||||
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
|
|
||||||
{:error, result}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
# The post isn't something we can search, that's ok
|
|
||||||
:ok
|
:ok
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
# There was an error, report it
|
||||||
|
Logger.error("Failed to add activity #{activity.id} to index: #{inspect(result)}")
|
||||||
|
{:error, result}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,11 +4,12 @@ defmodule Pleroma.Search.QdrantSearch do
|
||||||
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Config.Getting, as: Config
|
alias Pleroma.Config.Getting, as: Config
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Search
|
||||||
|
|
||||||
alias __MODULE__.OpenAIClient
|
alias __MODULE__.OpenAIClient
|
||||||
alias __MODULE__.QdrantClient
|
alias __MODULE__.QdrantClient
|
||||||
|
|
||||||
import Pleroma.Search.Meilisearch, only: [object_to_search_data: 1]
|
|
||||||
import Pleroma.Search.DatabaseSearch, only: [maybe_fetch: 3]
|
import Pleroma.Search.DatabaseSearch, only: [maybe_fetch: 3]
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
|
|
@ -82,23 +83,18 @@ defmodule Pleroma.Search.QdrantSearch do
|
||||||
end
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def add_to_index(activity) do
|
def add_to_index(%Activity{object: %Object{} = object} = activity) do
|
||||||
# This will only index public or unlisted notes
|
search_data = Search.object_to_search_data(object)
|
||||||
maybe_search_data = object_to_search_data(activity.object)
|
|
||||||
|
|
||||||
if activity.data["type"] == "Create" and maybe_search_data do
|
with {:ok, embedding} <- get_embedding(search_data.content),
|
||||||
with {:ok, embedding} <- get_embedding(maybe_search_data.content),
|
{:ok, %{status: 200}} <-
|
||||||
{:ok, %{status: 200}} <-
|
QdrantClient.put(
|
||||||
QdrantClient.put(
|
"/collections/posts/points",
|
||||||
"/collections/posts/points",
|
build_index_payload(activity, embedding)
|
||||||
build_index_payload(activity, embedding)
|
) do
|
||||||
) do
|
|
||||||
:ok
|
|
||||||
else
|
|
||||||
e -> {:error, e}
|
|
||||||
end
|
|
||||||
else
|
|
||||||
:ok
|
:ok
|
||||||
|
else
|
||||||
|
e -> {:error, e}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ defmodule Pleroma.Signature do
|
||||||
|> put_req_header("(request-target)", request_target)
|
|> put_req_header("(request-target)", request_target)
|
||||||
|> put_req_header("@request-target", request_target)
|
|> put_req_header("@request-target", request_target)
|
||||||
|
|
||||||
@http_signatures_impl.validate_conn(conn)
|
@http_signatures_impl.validate_conn(conn) == true
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec validate_signature(Plug.Conn.t()) :: boolean()
|
@spec validate_signature(Plug.Conn.t()) :: boolean()
|
||||||
|
|
|
||||||
|
|
@ -93,7 +93,7 @@ defmodule Pleroma.Upload do
|
||||||
def store(upload, opts \\ []) do
|
def store(upload, opts \\ []) do
|
||||||
opts = get_opts(opts)
|
opts = get_opts(opts)
|
||||||
|
|
||||||
with {:ok, upload} <- prepare_upload(upload, opts),
|
with {:ok, %__MODULE__{} = upload} <- prepare_upload(upload, opts),
|
||||||
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
|
upload = %__MODULE__{upload | path: upload.path || "#{upload.id}/#{upload.name}"},
|
||||||
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
|
{:ok, upload} <- Pleroma.Upload.Filter.filter(opts.filters, upload),
|
||||||
description = get_description(upload),
|
description = get_description(upload),
|
||||||
|
|
|
||||||
|
|
@ -342,7 +342,7 @@ defmodule Pleroma.User.Backup do
|
||||||
dir,
|
dir,
|
||||||
"outbox",
|
"outbox",
|
||||||
fn a ->
|
fn a ->
|
||||||
with {:ok, activity} <- Transmogrifier.prepare_outgoing(a.data) do
|
with {:ok, activity} <- Transmogrifier.prepare_activity(a.data) do
|
||||||
{:ok, Map.delete(activity, "@context")}
|
{:ok, Map.delete(activity, "@context")}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
defmodule Pleroma.User.Search do
|
defmodule Pleroma.User.Search do
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators.Uri, as: UriType
|
||||||
|
alias Pleroma.Instances.Instance
|
||||||
alias Pleroma.Pagination
|
alias Pleroma.Pagination
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
|
|
||||||
|
|
@ -88,12 +89,13 @@ defmodule Pleroma.User.Search do
|
||||||
|> filter_invisible_users()
|
|> filter_invisible_users()
|
||||||
|> filter_internal_users()
|
|> filter_internal_users()
|
||||||
|> filter_blocked_domains(for_user)
|
|> filter_blocked_domains(for_user)
|
||||||
|
|> filter_unreachable_users()
|
||||||
|> fts_search(query_string)
|
|> fts_search(query_string)
|
||||||
|> select_top_users(top_user_ids)
|
|> select_top_users(top_user_ids)
|
||||||
|> trigram_rank(query_string)
|
|> trigram_rank(query_string)
|
||||||
|> boost_search_rank(for_user, top_user_ids)
|
|> boost_search_rank(for_user, top_user_ids)
|
||||||
|> subquery()
|
|> subquery()
|
||||||
|> order_by(desc: :search_rank)
|
|> order_by_search_rank(for_user)
|
||||||
|> maybe_restrict_local(for_user)
|
|> maybe_restrict_local(for_user)
|
||||||
|> maybe_restrict_accepting_chat_messages(capabilities)
|
|> maybe_restrict_accepting_chat_messages(capabilities)
|
||||||
|> filter_deactivated_users()
|
|> filter_deactivated_users()
|
||||||
|
|
@ -196,6 +198,14 @@ defmodule Pleroma.User.Search do
|
||||||
|
|
||||||
defp filter_blocked_domains(query, _), do: query
|
defp filter_blocked_domains(query, _), do: query
|
||||||
|
|
||||||
|
defp filter_unreachable_users(query) do
|
||||||
|
from(u in query,
|
||||||
|
left_join: i in Instance,
|
||||||
|
on: i.host == fragment("substring(? from '.*://([^/]*)')", u.ap_id),
|
||||||
|
where: is_nil(i.unreachable_since)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
defp maybe_resolve(true, user, query) do
|
defp maybe_resolve(true, user, query) do
|
||||||
case {limit(), user} do
|
case {limit(), user} do
|
||||||
{:all, _} -> :noop
|
{:all, _} -> :noop
|
||||||
|
|
@ -236,6 +246,16 @@ defmodule Pleroma.User.Search do
|
||||||
|
|
||||||
from(u in subquery(query),
|
from(u in subquery(query),
|
||||||
select_merge: %{
|
select_merge: %{
|
||||||
|
search_type:
|
||||||
|
fragment(
|
||||||
|
"""
|
||||||
|
CASE WHEN (?) THEN 2
|
||||||
|
WHEN (?) THEN 1
|
||||||
|
ELSE 0 END
|
||||||
|
""",
|
||||||
|
u.id in ^top_user_ids,
|
||||||
|
u.id in ^friends_ids or u.id in ^followers_ids
|
||||||
|
),
|
||||||
search_rank:
|
search_rank:
|
||||||
fragment(
|
fragment(
|
||||||
"""
|
"""
|
||||||
|
|
@ -261,6 +281,14 @@ defmodule Pleroma.User.Search do
|
||||||
defp boost_search_rank(query, _for_user, top_user_ids) do
|
defp boost_search_rank(query, _for_user, top_user_ids) do
|
||||||
from(u in subquery(query),
|
from(u in subquery(query),
|
||||||
select_merge: %{
|
select_merge: %{
|
||||||
|
search_type:
|
||||||
|
fragment(
|
||||||
|
"""
|
||||||
|
CASE WHEN (?) THEN 2
|
||||||
|
ELSE 0 END
|
||||||
|
""",
|
||||||
|
u.id in ^top_user_ids
|
||||||
|
),
|
||||||
search_rank:
|
search_rank:
|
||||||
fragment(
|
fragment(
|
||||||
"""
|
"""
|
||||||
|
|
@ -273,4 +301,22 @@ defmodule Pleroma.User.Search do
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp order_by_search_rank(query, %User{}) do
|
||||||
|
order_by(
|
||||||
|
query,
|
||||||
|
[u],
|
||||||
|
desc: u.search_type,
|
||||||
|
desc_nulls_last:
|
||||||
|
fragment(
|
||||||
|
"CASE WHEN ? = 1 THEN COALESCE(?, ?) ELSE NULL END",
|
||||||
|
u.search_type,
|
||||||
|
u.last_status_at,
|
||||||
|
u.last_active_at
|
||||||
|
),
|
||||||
|
desc: u.search_rank
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp order_by_search_rank(query, _), do: order_by(query, desc: :search_rank)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ defmodule Pleroma.UserRelationship do
|
||||||
do: exists?(unquote(relationship_type), source, target)
|
do: exists?(unquote(relationship_type), source, target)
|
||||||
|
|
||||||
# `def get_block_expire_date/2`, `def get_mute_expire_date/2`,
|
# `def get_block_expire_date/2`, `def get_mute_expire_date/2`,
|
||||||
# `def get_reblog_mute_expire_date/2`, `def get_notification_mute_exists?/2`,
|
# `def get_reblog_mute_expire_date/2`, `def get_notification_mute_expire_date/2`,
|
||||||
# `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2`
|
# `def get_inverse_subscription_expire_date/2`, `def get_inverse_endorsement_expire_date/2`
|
||||||
def unquote(:"get_#{relationship_type}_expire_date")(source, target),
|
def unquote(:"get_#{relationship_type}_expire_date")(source, target),
|
||||||
do: get_expire_date(unquote(relationship_type), source, target)
|
do: get_expire_date(unquote(relationship_type), source, target)
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ defmodule Pleroma.Utils do
|
||||||
dir
|
dir
|
||||||
|> File.ls!()
|
|> File.ls!()
|
||||||
|> Enum.map(&Path.join(dir, &1))
|
|> Enum.map(&Path.join(dir, &1))
|
||||||
|> Kernel.ParallelCompiler.compile()
|
|> Kernel.ParallelCompiler.compile(return_diagnostics: true)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ defmodule Pleroma.Web do
|
||||||
|
|
||||||
def controller do
|
def controller do
|
||||||
quote do
|
quote do
|
||||||
use Phoenix.Controller, namespace: Pleroma.Web
|
use Phoenix.Controller, formats: [json: "View", html: "View"]
|
||||||
|
|
||||||
import Plug.Conn
|
import Plug.Conn
|
||||||
|
|
||||||
|
|
@ -42,7 +42,10 @@ defmodule Pleroma.Web do
|
||||||
plug(:set_put_layout)
|
plug(:set_put_layout)
|
||||||
|
|
||||||
defp set_put_layout(conn, _) do
|
defp set_put_layout(conn, _) do
|
||||||
put_layout(conn, Pleroma.Config.get(:app_layout, "app.html"))
|
case Pleroma.Config.get(:app_layout, "app.html") do
|
||||||
|
false -> put_layout(conn, false)
|
||||||
|
layout -> put_layout(conn, {Pleroma.Web.LayoutView, layout})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain
|
# Marks plugs intentionally skipped and blocks their execution if present in plugs chain
|
||||||
|
|
|
||||||
|
|
@ -1003,6 +1003,14 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
|
|
||||||
defp restrict_state(query, _), do: query
|
defp restrict_state(query, _), do: query
|
||||||
|
|
||||||
|
defp restrict_assigned_account(query, %{assigned_account: assigned_account}) do
|
||||||
|
from(activity in query,
|
||||||
|
where: fragment("?->>'assigned_account' = ?", activity.data, ^assigned_account)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp restrict_assigned_account(query, _), do: query
|
||||||
|
|
||||||
defp restrict_favorited_by(query, %{favorited_by: ap_id}) do
|
defp restrict_favorited_by(query, %{favorited_by: ap_id}) do
|
||||||
from(
|
from(
|
||||||
[_activity, object] in query,
|
[_activity, object] in query,
|
||||||
|
|
@ -1471,6 +1479,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
|> restrict_actor(opts)
|
|> restrict_actor(opts)
|
||||||
|> restrict_type(opts)
|
|> restrict_type(opts)
|
||||||
|> restrict_state(opts)
|
|> restrict_state(opts)
|
||||||
|
|> restrict_assigned_account(opts)
|
||||||
|> restrict_favorited_by(opts)
|
|> restrict_favorited_by(opts)
|
||||||
|> restrict_blocked(restrict_blocked_opts)
|
|> restrict_blocked(restrict_blocked_opts)
|
||||||
|> restrict_blockers_visibility(opts)
|
|> restrict_blockers_visibility(opts)
|
||||||
|
|
@ -1609,6 +1618,10 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
|
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
|
||||||
defp normalize_image(_), do: nil
|
defp normalize_image(_), do: nil
|
||||||
|
|
||||||
|
defp normalize_also_known_as(urls) when is_list(urls), do: urls
|
||||||
|
defp normalize_also_known_as(url) when is_binary(url), do: [url]
|
||||||
|
defp normalize_also_known_as(nil), do: []
|
||||||
|
|
||||||
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
|
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
|
||||||
Map.put(map, "name", description)
|
Map.put(map, "name", description)
|
||||||
end
|
end
|
||||||
|
|
@ -1664,44 +1677,80 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
|
|
||||||
show_birthday = !!birthday
|
show_birthday = !!birthday
|
||||||
|
|
||||||
# if WebFinger request was already done, we probably have acct, otherwise
|
with {:ok, nickname} <- nickname_from_actor(data, additional) do
|
||||||
# we request WebFinger here
|
{:ok,
|
||||||
nickname = additional[:nickname_from_acct] || generate_nickname(data)
|
%{
|
||||||
|
ap_id: data["id"],
|
||||||
|
uri: get_actor_url(data["url"]),
|
||||||
|
banner: normalize_image(data["image"]),
|
||||||
|
fields: fields,
|
||||||
|
emoji: emojis,
|
||||||
|
is_locked: is_locked,
|
||||||
|
is_discoverable: is_discoverable,
|
||||||
|
invisible: invisible,
|
||||||
|
avatar: normalize_image(data["icon"]),
|
||||||
|
name: data["name"],
|
||||||
|
follower_address: data["followers"],
|
||||||
|
following_address: data["following"],
|
||||||
|
featured_address: featured_address,
|
||||||
|
bio: data["summary"] || "",
|
||||||
|
actor_type: actor_type,
|
||||||
|
also_known_as: normalize_also_known_as(data["alsoKnownAs"]),
|
||||||
|
public_key: public_key,
|
||||||
|
inbox: data["inbox"],
|
||||||
|
shared_inbox: shared_inbox,
|
||||||
|
accepts_chat_messages: accepts_chat_messages,
|
||||||
|
birthday: birthday,
|
||||||
|
show_birthday: show_birthday,
|
||||||
|
pinned_objects: pinned_objects,
|
||||||
|
nickname: nickname
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
%{
|
defp nickname_from_actor(data, additional) do
|
||||||
ap_id: data["id"],
|
generated = generated_nickname(data)
|
||||||
uri: get_actor_url(data["url"]),
|
|
||||||
banner: normalize_image(data["image"]),
|
case additional[:nickname_from_acct] do
|
||||||
fields: fields,
|
^generated when is_binary(generated) ->
|
||||||
emoji: emojis,
|
{:ok, generated}
|
||||||
is_locked: is_locked,
|
|
||||||
is_discoverable: is_discoverable,
|
acct when is_binary(acct) ->
|
||||||
invisible: invisible,
|
with ^acct <- webfinger_nickname(data) do
|
||||||
avatar: normalize_image(data["icon"]),
|
{:ok, acct}
|
||||||
name: data["name"],
|
else
|
||||||
follower_address: data["followers"],
|
_ -> {:error, {:webfinger_actor_mismatch, acct, data["id"]}}
|
||||||
following_address: data["following"],
|
end
|
||||||
featured_address: featured_address,
|
|
||||||
bio: data["summary"] || "",
|
_ ->
|
||||||
actor_type: actor_type,
|
{:ok, generate_nickname(data)}
|
||||||
also_known_as: Map.get(data, "alsoKnownAs", []),
|
end
|
||||||
public_key: public_key,
|
end
|
||||||
inbox: data["inbox"],
|
|
||||||
shared_inbox: shared_inbox,
|
defp generated_nickname(%{"preferredUsername" => username, "id" => ap_id})
|
||||||
accepts_chat_messages: accepts_chat_messages,
|
when is_binary(username) and is_binary(ap_id) do
|
||||||
birthday: birthday,
|
case URI.parse(ap_id) do
|
||||||
show_birthday: show_birthday,
|
%URI{host: host} when is_binary(host) -> "#{username}@#{host}"
|
||||||
pinned_objects: pinned_objects,
|
_ -> nil
|
||||||
nickname: nickname
|
end
|
||||||
}
|
end
|
||||||
|
|
||||||
|
defp generated_nickname(_), do: nil
|
||||||
|
|
||||||
|
defp webfinger_nickname(data) do
|
||||||
|
with generated when is_binary(generated) <- generated_nickname(data),
|
||||||
|
{:ok, %{"subject" => "acct:" <> acct, "ap_id" => ap_id}} <- WebFinger.finger(generated),
|
||||||
|
true <- ap_id == data["id"] do
|
||||||
|
acct
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do
|
defp generate_nickname(%{"preferredUsername" => username} = data) when is_binary(username) do
|
||||||
generated = "#{username}@#{URI.parse(data["id"]).host}"
|
generated = generated_nickname(data)
|
||||||
|
|
||||||
if Config.get([WebFinger, :update_nickname_on_user_fetch]) do
|
if Config.get([WebFinger, :update_nickname_on_user_fetch]) do
|
||||||
case WebFinger.finger(generated) do
|
case webfinger_nickname(data) do
|
||||||
{:ok, %{"subject" => "acct:" <> acct}} -> acct
|
acct when is_binary(acct) -> acct
|
||||||
_ -> generated
|
_ -> generated
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
|
|
@ -1781,9 +1830,11 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
defp collection_private(_data), do: {:ok, true}
|
defp collection_private(_data), do: {:ok, true}
|
||||||
|
|
||||||
def user_data_from_user_object(data, additional \\ []) do
|
def user_data_from_user_object(data, additional \\ []) do
|
||||||
with {:ok, data} <- MRF.filter(data) do
|
with {:ok, data} <- MRF.filter(data),
|
||||||
{:ok, object_to_user_data(data, additional)}
|
{:ok, data} <- object_to_user_data(data, additional) do
|
||||||
|
{:ok, data}
|
||||||
else
|
else
|
||||||
|
{:error, _} = e -> e
|
||||||
e -> {:error, e}
|
e -> {:error, e}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
@ -1830,11 +1881,15 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@featured_collection_types ["OrderedCollection", "Collection"]
|
||||||
|
@featured_collection_page_types ["OrderedCollectionPage", "CollectionPage"]
|
||||||
|
@featured_collection_item_types @featured_collection_types ++ @featured_collection_page_types
|
||||||
|
|
||||||
def pin_data_from_featured_collection(%{
|
def pin_data_from_featured_collection(%{
|
||||||
"type" => type,
|
"type" => type,
|
||||||
"orderedItems" => objects
|
"orderedItems" => objects
|
||||||
})
|
})
|
||||||
when type in ["OrderedCollection", "Collection"] do
|
when type in @featured_collection_item_types do
|
||||||
Map.new(objects, fn
|
Map.new(objects, fn
|
||||||
%{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()}
|
%{"id" => object_ap_id} -> {object_ap_id, NaiveDateTime.utc_now()}
|
||||||
object_ap_id when is_binary(object_ap_id) -> {object_ap_id, NaiveDateTime.utc_now()}
|
object_ap_id when is_binary(object_ap_id) -> {object_ap_id, NaiveDateTime.utc_now()}
|
||||||
|
|
@ -1852,7 +1907,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
|
|
||||||
def fetch_and_prepare_featured_from_ap_id(ap_id) do
|
def fetch_and_prepare_featured_from_ap_id(ap_id) do
|
||||||
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
|
with {:ok, data} <- Fetcher.fetch_and_contain_remote_object_from_id(ap_id) do
|
||||||
{:ok, pin_data_from_featured_collection(data)}
|
{:ok, prepare_featured_collection(data)}
|
||||||
else
|
else
|
||||||
e ->
|
e ->
|
||||||
Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}")
|
Logger.error("Could not decode featured collection at fetch #{ap_id}, #{inspect(e)}")
|
||||||
|
|
@ -1860,6 +1915,34 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp prepare_featured_collection(%{"orderedItems" => objects} = data) when is_list(objects) do
|
||||||
|
pin_data_from_featured_collection(data)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_featured_collection(%{
|
||||||
|
"type" => type,
|
||||||
|
"first" => %{"type" => page_type} = first
|
||||||
|
})
|
||||||
|
when type in @featured_collection_types and page_type in @featured_collection_page_types do
|
||||||
|
pin_data_from_featured_collection(first)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_featured_collection(%{"type" => type, "first" => first})
|
||||||
|
when type in @featured_collection_types and is_binary(first) do
|
||||||
|
case Fetcher.fetch_and_contain_remote_object_from_id(first) do
|
||||||
|
{:ok, data} ->
|
||||||
|
pin_data_from_featured_collection(data)
|
||||||
|
|
||||||
|
e ->
|
||||||
|
Logger.error("Could not decode featured collection page at fetch #{first}, #{inspect(e)}")
|
||||||
|
%{}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_featured_collection(data) do
|
||||||
|
pin_data_from_featured_collection(data)
|
||||||
|
end
|
||||||
|
|
||||||
def enqueue_pin_fetches(%{pinned_objects: pins}) do
|
def enqueue_pin_fetches(%{pinned_objects: pins}) do
|
||||||
# enqueue a task to fetch all pinned objects
|
# enqueue a task to fetch all pinned objects
|
||||||
Enum.each(pins, fn {ap_id, _} ->
|
Enum.each(pins, fn {ap_id, _} ->
|
||||||
|
|
|
||||||
|
|
@ -303,7 +303,12 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def inbox(%{assigns: %{valid_signature: true}} = conn, %{"nickname" => nickname} = params) do
|
def inbox(
|
||||||
|
%{
|
||||||
|
assigns: %{valid_signature: true, valid_host_header: true}
|
||||||
|
} = conn,
|
||||||
|
%{"nickname" => nickname} = params
|
||||||
|
) do
|
||||||
with {:recipient_exists, %User{} = recipient} <-
|
with {:recipient_exists, %User{} = recipient} <-
|
||||||
{:recipient_exists, User.get_cached_by_nickname(nickname)},
|
{:recipient_exists, User.get_cached_by_nickname(nickname)},
|
||||||
{:sender_exists, {:ok, %User{} = actor}} <-
|
{:sender_exists, {:ok, %User{} = actor}} <-
|
||||||
|
|
@ -342,13 +347,13 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubController do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def inbox(%{assigns: %{valid_signature: true}} = conn, params) do
|
def inbox(%{assigns: %{valid_signature: true, valid_host_header: true}} = conn, params) do
|
||||||
Federator.incoming_ap_doc(params)
|
Federator.incoming_ap_doc(params)
|
||||||
json(conn, "ok")
|
json(conn, "ok")
|
||||||
end
|
end
|
||||||
|
|
||||||
def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
|
def inbox(%{assigns: %{valid_signature: false}} = conn, params) do
|
||||||
Federator.incoming_ap_doc(%{
|
Federator.incoming_failed_signature_ap_doc(%{
|
||||||
method: conn.method,
|
method: conn.method,
|
||||||
req_headers: conn.req_headers,
|
req_headers: conn.req_headers,
|
||||||
request_path: conn.request_path,
|
request_path: conn.request_path,
|
||||||
|
|
|
||||||
|
|
@ -332,21 +332,18 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
|
|
||||||
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
@spec announce(User.t(), Object.t(), keyword()) :: {:ok, map(), keyword()}
|
||||||
def announce(actor, object, options \\ []) do
|
def announce(actor, object, options \\ []) do
|
||||||
public? = Keyword.get(options, :public, false)
|
visibility = Keyword.get(options, :visibility, "public")
|
||||||
|
|
||||||
to =
|
{to, cc} =
|
||||||
cond do
|
if actor.ap_id == Relay.ap_id() do
|
||||||
actor.ap_id == Relay.ap_id() ->
|
{[actor.follower_address], []}
|
||||||
[actor.follower_address]
|
else
|
||||||
|
Pleroma.Web.CommonAPI.Utils.get_to_and_cc_for_visibility(
|
||||||
public? and Visibility.local_public?(object) ->
|
visibility,
|
||||||
[actor.follower_address, object.data["actor"], Utils.as_local_public()]
|
actor.follower_address,
|
||||||
|
nil,
|
||||||
public? ->
|
[object.data["actor"]]
|
||||||
[actor.follower_address, object.data["actor"], Pleroma.Constants.as_public()]
|
)
|
||||||
|
|
||||||
true ->
|
|
||||||
[actor.follower_address, object.data["actor"]]
|
|
||||||
end
|
end
|
||||||
|
|
||||||
{:ok,
|
{:ok,
|
||||||
|
|
@ -355,6 +352,7 @@ defmodule Pleroma.Web.ActivityPub.Builder do
|
||||||
"actor" => actor.ap_id,
|
"actor" => actor.ap_id,
|
||||||
"object" => object.data["id"],
|
"object" => object.data["id"],
|
||||||
"to" => to,
|
"to" => to,
|
||||||
|
"cc" => cc,
|
||||||
"context" => object.data["context"],
|
"context" => object.data["context"],
|
||||||
"type" => "Announce",
|
"type" => "Announce",
|
||||||
"published" => Utils.make_date()
|
"published" => Utils.make_date()
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,12 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||||
|
alias Pleroma.HTML
|
||||||
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonFixes
|
||||||
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
alias Pleroma.Web.ActivityPub.ObjectValidators.CommonValidations
|
||||||
alias Pleroma.Web.ActivityPub.Transmogrifier
|
alias Pleroma.Web.ActivityPub.Transmogrifier
|
||||||
|
alias Pleroma.Web.CommonAPI.Utils
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
|
@ -26,6 +29,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:replies, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
|
field(:source, :map)
|
||||||
end
|
end
|
||||||
|
|
||||||
def cast_and_apply(data) do
|
def cast_and_apply(data) do
|
||||||
|
|
@ -80,6 +84,113 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
|
|
||||||
def fix_attachments(data), do: data
|
def fix_attachments(data), do: data
|
||||||
|
|
||||||
|
defp remote_mention_resolver(
|
||||||
|
%{"id" => ap_id, "tag" => tags},
|
||||||
|
"@" <> nickname = mention,
|
||||||
|
buffer,
|
||||||
|
opts,
|
||||||
|
acc
|
||||||
|
)
|
||||||
|
when is_binary(ap_id) and is_list(tags) do
|
||||||
|
initial_host =
|
||||||
|
ap_id
|
||||||
|
|> URI.parse()
|
||||||
|
|> Map.get(:host)
|
||||||
|
|
||||||
|
with mention_tag when not is_nil(mention_tag) <-
|
||||||
|
Enum.find(tags, &mention_tag?(&1, mention, initial_host)),
|
||||||
|
href when is_binary(href) <- mention_tag["href"],
|
||||||
|
%User{} = user <- User.get_cached_by_ap_id(href) do
|
||||||
|
link = Pleroma.Formatter.mention_from_user(user, opts)
|
||||||
|
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
|
||||||
|
else
|
||||||
|
_ -> {buffer, acc}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp remote_mention_resolver(_object, _mention, buffer, _opts, acc), do: {buffer, acc}
|
||||||
|
|
||||||
|
defp mention_tag?(%{"type" => "Mention", "name" => name}, mention, initial_host)
|
||||||
|
when is_binary(name) do
|
||||||
|
name == mention || mention == "#{name}@#{initial_host}"
|
||||||
|
end
|
||||||
|
|
||||||
|
defp mention_tag?(_tag, _mention, _initial_host), do: false
|
||||||
|
|
||||||
|
defp scrub_content(%{"content" => content} = object) when is_binary(content) do
|
||||||
|
Map.put(object, "content", HTML.filter_tags(content))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp scrub_content(object), do: object
|
||||||
|
|
||||||
|
defp mfm_parse_limit do
|
||||||
|
min(Pleroma.Config.get([:instance, :limit]), Pleroma.Config.get([:instance, :remote_limit]))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_source(%{"source" => source} = object) when is_binary(source) do
|
||||||
|
object
|
||||||
|
|> Map.put("source", %{"content" => source})
|
||||||
|
|> normalize_source()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_source(%{"source" => source} = object) when is_map(source) do
|
||||||
|
source =
|
||||||
|
case source["content"] do
|
||||||
|
content when is_binary(content) ->
|
||||||
|
if String.length(content) <= mfm_parse_limit() do
|
||||||
|
source
|
||||||
|
else
|
||||||
|
Map.delete(source, "content")
|
||||||
|
end
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
source
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
Map.delete(source, "content")
|
||||||
|
end
|
||||||
|
|
||||||
|
Map.put(object, "source", source)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp normalize_source(object), do: object
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"htmlMfm" => true, "content" => content} = object)
|
||||||
|
when is_binary(content) do
|
||||||
|
Map.put(object, "content", HTML.filter_tags(content))
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"htmlMfm" => true} = object), do: object
|
||||||
|
|
||||||
|
defp fix_misskey_content(
|
||||||
|
%{"source" => %{"mediaType" => "text/x.misskeymarkdown", "content" => content}} = object
|
||||||
|
)
|
||||||
|
when is_binary(content) do
|
||||||
|
mention_handler = fn nick, buffer, opts, acc ->
|
||||||
|
remote_mention_resolver(object, nick, buffer, opts, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
{linked, _mentions, _tags} =
|
||||||
|
Utils.format_input(content, "text/x.misskeymarkdown", mention_handler: mention_handler)
|
||||||
|
|
||||||
|
Map.put(object, "content", linked)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"source" => %{"mediaType" => "text/x.misskeymarkdown"}} = object),
|
||||||
|
do: scrub_content(object)
|
||||||
|
|
||||||
|
defp fix_misskey_content(%{"_misskey_content" => content} = object) when is_binary(content) do
|
||||||
|
object
|
||||||
|
|> Map.put("source", %{
|
||||||
|
"content" => content,
|
||||||
|
"mediaType" => "text/x.misskeymarkdown"
|
||||||
|
})
|
||||||
|
|> Map.delete("_misskey_content")
|
||||||
|
|> fix_misskey_content()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp fix_misskey_content(object), do: object
|
||||||
|
|
||||||
defp fix(data) do
|
defp fix(data) do
|
||||||
data
|
data
|
||||||
|> CommonFixes.fix_actor()
|
|> CommonFixes.fix_actor()
|
||||||
|
|
@ -88,6 +199,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.ArticleNotePageValidator do
|
||||||
|> fix_tag()
|
|> fix_tag()
|
||||||
|> fix_replies()
|
|> fix_replies()
|
||||||
|> fix_attachments()
|
|> fix_attachments()
|
||||||
|
|> normalize_source()
|
||||||
|
|> fix_misskey_content()
|
||||||
|> CommonFixes.fix_quote_url()
|
|> CommonFixes.fix_quote_url()
|
||||||
|> CommonFixes.fix_likes()
|
|> CommonFixes.fix_likes()
|
||||||
|> Transmogrifier.fix_emoji()
|
|> Transmogrifier.fix_emoji()
|
||||||
|
|
|
||||||
|
|
@ -32,6 +32,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.CommonFields do
|
||||||
quote bind_quoted: binding() do
|
quote bind_quoted: binding() do
|
||||||
field(:content, :string)
|
field(:content, :string)
|
||||||
field(:contentMap, ObjectValidators.ContentLanguageMap)
|
field(:contentMap, ObjectValidators.ContentLanguageMap)
|
||||||
|
field(:htmlMfm, :boolean)
|
||||||
|
|
||||||
field(:published, ObjectValidators.DateTime)
|
field(:published, ObjectValidators.DateTime)
|
||||||
field(:updated, ObjectValidators.DateTime)
|
field(:updated, ObjectValidators.DateTime)
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.QuestionValidator do
|
||||||
end
|
end
|
||||||
|
|
||||||
field(:closed, ObjectValidators.DateTime)
|
field(:closed, ObjectValidators.DateTime)
|
||||||
|
field(:votersCount, :integer)
|
||||||
field(:voters, {:array, ObjectValidators.ObjectID}, default: [])
|
field(:voters, {:array, ObjectValidators.ObjectID}, default: [])
|
||||||
field(:nonAnonymous, :boolean)
|
field(:nonAnonymous, :boolean)
|
||||||
embeds_many(:anyOf, QuestionOptionsValidator)
|
embeds_many(:anyOf, QuestionOptionsValidator)
|
||||||
|
|
|
||||||
|
|
@ -75,15 +75,40 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidators.UpdateValidator do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# For remote Updates, verify the host is the same.
|
# For remote Updates, verify the Actor is the same
|
||||||
def validate_updating_rights_remote(cng) do
|
def validate_updating_rights_remote(cng) do
|
||||||
with actor = get_field(cng, :actor),
|
with actor = get_field(cng, :actor),
|
||||||
object = get_field(cng, :object),
|
object = get_field(cng, :object),
|
||||||
{:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
|
{:ok, object_id} <- ObjectValidators.ObjectID.cast(object),
|
||||||
actor_uri <- URI.parse(actor),
|
entity <-
|
||||||
object_uri <- URI.parse(object_id),
|
Object.normalize(object_id, fetch: false) || User.get_cached_by_ap_id(object_id) do
|
||||||
true <- actor_uri.host == object_uri.host do
|
case entity do
|
||||||
cng
|
# Actor must own Object to update it
|
||||||
|
%Object{} ->
|
||||||
|
if actor == entity.data["actor"] do
|
||||||
|
cng
|
||||||
|
else
|
||||||
|
cng
|
||||||
|
|> add_error(:object, "Can't be updated by this actor")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Actor must only be allowed to update itself
|
||||||
|
%User{} ->
|
||||||
|
if actor == entity.ap_id do
|
||||||
|
cng
|
||||||
|
else
|
||||||
|
cng
|
||||||
|
|> add_error(:object, "Can't be updated by this actor")
|
||||||
|
end
|
||||||
|
|
||||||
|
nil ->
|
||||||
|
cng
|
||||||
|
|> add_error(:object, "Can't be updated by this actor")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
cng
|
||||||
|
|> add_error(:object, "Update is neither for Object or Actor")
|
||||||
|
end
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
cng
|
cng
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
||||||
Determine if an activity can be represented by running it through Transmogrifier.
|
Determine if an activity can be represented by running it through Transmogrifier.
|
||||||
"""
|
"""
|
||||||
def representable?(%Activity{} = activity) do
|
def representable?(%Activity{} = activity) do
|
||||||
with {:ok, _data} <- @transmogrifier_impl.prepare_outgoing(activity.data) do
|
with {:ok, _data} <- @transmogrifier_impl.prepare_activity(activity.data) do
|
||||||
true
|
true
|
||||||
else
|
else
|
||||||
_e ->
|
_e ->
|
||||||
|
|
@ -102,14 +102,14 @@ defmodule Pleroma.Web.ActivityPub.Publisher do
|
||||||
Logger.debug("Federating #{ap_id} to #{inbox}")
|
Logger.debug("Federating #{ap_id} to #{inbox}")
|
||||||
uri = %{path: path} = URI.parse(inbox)
|
uri = %{path: path} = URI.parse(inbox)
|
||||||
|
|
||||||
{:ok, data} = @transmogrifier_impl.prepare_outgoing(activity.data)
|
{:ok, data} = @transmogrifier_impl.prepare_activity(activity.data)
|
||||||
|
|
||||||
{actor, data} =
|
{actor, data} =
|
||||||
with {_, false} <- {:actor_changed?, data["actor"] != activity.data["actor"]} do
|
with {_, false} <- {:actor_changed?, data["actor"] != activity.data["actor"]} do
|
||||||
{actor, data}
|
{actor, data}
|
||||||
else
|
else
|
||||||
{:actor_changed?, true} ->
|
{:actor_changed?, true} ->
|
||||||
# If prepare_outgoing changes the actor, re-get it from the db
|
# If prepare_activity changes the actor, re-get it from the db
|
||||||
new_actor = User.get_cached_by_ap_id(data["actor"])
|
new_actor = User.get_cached_by_ap_id(data["actor"])
|
||||||
{new_actor, data}
|
{new_actor, data}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -430,6 +430,12 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp reject_third_party_report(%User{local: false}, %User{local: false} = account) do
|
||||||
|
{:reject, "[Transmogrifier] third-party report: #{account.ap_id}"}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp reject_third_party_report(_, _), do: :ok
|
||||||
|
|
||||||
def handle_incoming(data, options \\ []) do
|
def handle_incoming(data, options \\ []) do
|
||||||
data
|
data
|
||||||
|> fix_recursive(&strip_internal_fields/1)
|
|> fix_recursive(&strip_internal_fields/1)
|
||||||
|
|
@ -444,9 +450,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
) do
|
) do
|
||||||
with context <- data["context"] || Utils.generate_context_id(),
|
with context <- data["context"] || Utils.generate_context_id(),
|
||||||
content <- data["content"] || "",
|
content <- data["content"] || "",
|
||||||
|
objects <- List.wrap(objects),
|
||||||
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
%User{} = actor <- User.get_cached_by_ap_id(actor),
|
||||||
# Reduce the object list to find the reported user.
|
# Reduce the object list to find the reported user.
|
||||||
%User{} = account <- get_reported(objects),
|
%User{} = account <- get_reported(objects),
|
||||||
|
:ok <- reject_third_party_report(actor, account),
|
||||||
# Remove the reported user from the object list.
|
# Remove the reported user from the object list.
|
||||||
statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
|
statuses <- Enum.filter(objects, fn ap_id -> ap_id != account.ap_id end) do
|
||||||
%{
|
%{
|
||||||
|
|
@ -783,7 +791,13 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|
|
||||||
def set_replies(obj_data), do: obj_data
|
def set_replies(obj_data), do: obj_data
|
||||||
|
|
||||||
# Prepares the object of an outgoing create activity.
|
defp set_voters_count(%{"voters" => [_ | _] = voters} = obj) do
|
||||||
|
Map.merge(obj, %{"votersCount" => length(voters)})
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_voters_count(obj), do: obj
|
||||||
|
|
||||||
|
# Prepares and sanitizes the object for federation.
|
||||||
def prepare_object(object) do
|
def prepare_object(object) do
|
||||||
object
|
object
|
||||||
|> add_hashtags
|
|> add_hashtags
|
||||||
|
|
@ -795,6 +809,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|> set_reply_to_uri
|
|> set_reply_to_uri
|
||||||
|> set_quote_url
|
|> set_quote_url
|
||||||
|> set_replies
|
|> set_replies
|
||||||
|
|> set_voters_count
|
||||||
|> CommonFixes.maybe_add_content_map()
|
|> CommonFixes.maybe_add_content_map()
|
||||||
|> strip_internal_fields
|
|> strip_internal_fields
|
||||||
|> strip_internal_tags
|
|> strip_internal_tags
|
||||||
|
|
@ -824,7 +839,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
# internal -> Mastodon
|
# internal -> Mastodon
|
||||||
# """
|
# """
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => activity_type, "object" => object_id} = data)
|
def prepare_activity(%{"type" => activity_type, "object" => object_id} = data)
|
||||||
when activity_type in ["Create", "Listen"] do
|
when activity_type in ["Create", "Listen"] do
|
||||||
object =
|
object =
|
||||||
object_id
|
object_id
|
||||||
|
|
@ -840,7 +855,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
|
def prepare_activity(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
|
||||||
when objtype in Pleroma.Constants.updatable_object_types() do
|
when objtype in Pleroma.Constants.updatable_object_types() do
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|
|
@ -851,7 +866,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
|
def prepare_activity(%{"type" => "Update", "object" => %{"type" => objtype} = object} = data)
|
||||||
when objtype in Pleroma.Constants.actor_types() do
|
when objtype in Pleroma.Constants.actor_types() do
|
||||||
object =
|
object =
|
||||||
object
|
object
|
||||||
|
|
@ -868,11 +883,11 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
{:ok, data}
|
{:ok, data}
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Update", "object" => %{}} = data) do
|
def prepare_activity(%{"type" => "Update", "object" => %{}} = data) do
|
||||||
raise "Requested to serve an Update for non-updateable object type: #{inspect(data)}"
|
raise "Requested to serve an Update for non-updateable object type: #{inspect(data)}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
|
def prepare_activity(%{"type" => "Announce", "actor" => ap_id, "object" => object_id} = data) do
|
||||||
object =
|
object =
|
||||||
object_id
|
object_id
|
||||||
|> Object.normalize(fetch: false)
|
|> Object.normalize(fetch: false)
|
||||||
|
|
@ -895,7 +910,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
|
|
||||||
# Mastodon Accept/Reject requires a non-normalized object containing the actor URIs,
|
# Mastodon Accept/Reject requires a non-normalized object containing the actor URIs,
|
||||||
# because of course it does.
|
# because of course it does.
|
||||||
def prepare_outgoing(%{"type" => "Accept"} = data) do
|
def prepare_activity(%{"type" => "Accept"} = data) do
|
||||||
with follow_activity <- Activity.normalize(data["object"]) do
|
with follow_activity <- Activity.normalize(data["object"]) do
|
||||||
object = %{
|
object = %{
|
||||||
"actor" => follow_activity.actor,
|
"actor" => follow_activity.actor,
|
||||||
|
|
@ -913,7 +928,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Reject"} = data) do
|
def prepare_activity(%{"type" => "Reject"} = data) do
|
||||||
with follow_activity <- Activity.normalize(data["object"]) do
|
with follow_activity <- Activity.normalize(data["object"]) do
|
||||||
object = %{
|
object = %{
|
||||||
"actor" => follow_activity.actor,
|
"actor" => follow_activity.actor,
|
||||||
|
|
@ -931,7 +946,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => "Flag"} = data) do
|
def prepare_activity(%{"type" => "Flag"} = data) do
|
||||||
with {:ok, stripped_activity} <- Utils.strip_report_status_data(data),
|
with {:ok, stripped_activity} <- Utils.strip_report_status_data(data),
|
||||||
stripped_activity <- Utils.maybe_anonymize_reporter(stripped_activity),
|
stripped_activity <- Utils.maybe_anonymize_reporter(stripped_activity),
|
||||||
stripped_activity <- Map.merge(stripped_activity, Utils.make_json_ld_header()) do
|
stripped_activity <- Map.merge(stripped_activity, Utils.make_json_ld_header()) do
|
||||||
|
|
@ -939,7 +954,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_outgoing(%{"type" => _type} = data) do
|
def prepare_activity(%{"type" => _type} = data) do
|
||||||
data =
|
data =
|
||||||
data
|
data
|
||||||
|> strip_internal_fields
|
|> strip_internal_fields
|
||||||
|
|
|
||||||
|
|
@ -7,5 +7,5 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier.API do
|
||||||
Behaviour for the subset of Transmogrifier used by Publisher.
|
Behaviour for the subset of Transmogrifier used by Publisher.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@callback prepare_outgoing(map()) :: {:ok, map()} | {:error, term()}
|
@callback prepare_activity(map()) :: {:ok, map()} | {:error, term()}
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,8 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
"https://www.w3.org/ns/activitystreams",
|
"https://www.w3.org/ns/activitystreams",
|
||||||
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
"#{Endpoint.url()}/schemas/litepub-0.1.jsonld",
|
||||||
%{
|
%{
|
||||||
"@language" => get_language(data)
|
"@language" => get_language(data),
|
||||||
|
"htmlMfm" => "https://w3id.org/fep/c16b#htmlMfm"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
@ -863,6 +864,34 @@ defmodule Pleroma.Web.ActivityPub.Utils do
|
||||||
|
|
||||||
def update_report_state(_, _), do: {:error, "Unsupported state"}
|
def update_report_state(_, _), do: {:error, "Unsupported state"}
|
||||||
|
|
||||||
|
def assign_report_to_account(%Activity{} = activity, nil = _account) do
|
||||||
|
new_data = Map.delete(activity.data, "assigned_account")
|
||||||
|
|
||||||
|
activity
|
||||||
|
|> Changeset.change(data: new_data)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_report_to_account(%Activity{} = activity, account) do
|
||||||
|
new_data = Map.put(activity.data, "assigned_account", account)
|
||||||
|
|
||||||
|
activity
|
||||||
|
|> Changeset.change(data: new_data)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
|
||||||
|
def assign_report_to_account(activity_ids, account) do
|
||||||
|
activities_num = length(activity_ids)
|
||||||
|
|
||||||
|
from(a in Activity, where: a.id in ^activity_ids)
|
||||||
|
|> update(set: [data: fragment("jsonb_set(data, '{assigned_account}', ?)", ^account)])
|
||||||
|
|> Repo.update_all([])
|
||||||
|
|> case do
|
||||||
|
{^activities_num, _} -> :ok
|
||||||
|
_ -> {:error, activity_ids}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def strip_report_status_data(%Activity{} = activity) do
|
def strip_report_status_data(%Activity{} = activity) do
|
||||||
with {:ok, new_data} <- strip_report_status_data(activity.data) do
|
with {:ok, new_data} <- strip_report_status_data(activity.data) do
|
||||||
{:ok, %{activity | data: new_data}}
|
{:ok, %{activity | data: new_data}}
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ defmodule Pleroma.Web.ActivityPub.ObjectView do
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("object.json", %{object: %Activity{} = activity}) do
|
def render("object.json", %{object: %Activity{} = activity}) do
|
||||||
{:ok, ap_data} = Transmogrifier.prepare_outgoing(activity.data)
|
{:ok, ap_data} = Transmogrifier.prepare_activity(activity.data)
|
||||||
ap_data
|
ap_data
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,32 +35,14 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
def render("endpoints.json", _), do: %{}
|
def render("endpoints.json", _), do: %{}
|
||||||
|
|
||||||
def render("service.json", %{user: user}) do
|
def render("service.json", %{user: user}) do
|
||||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
Map.merge(common_actor_fields(user), %{
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
|
||||||
public_key = :public_key.pem_encode([public_key])
|
|
||||||
|
|
||||||
endpoints = render("endpoints.json", %{user: user})
|
|
||||||
|
|
||||||
%{
|
|
||||||
"id" => user.ap_id,
|
|
||||||
"type" => "Application",
|
"type" => "Application",
|
||||||
"following" => "#{user.ap_id}/following",
|
|
||||||
"followers" => "#{user.ap_id}/followers",
|
|
||||||
"inbox" => "#{user.ap_id}/inbox",
|
|
||||||
"outbox" => "#{user.ap_id}/outbox",
|
|
||||||
"name" => "Pleroma",
|
"name" => "Pleroma",
|
||||||
"summary" =>
|
"summary" =>
|
||||||
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
|
"An internal service actor for this Pleroma instance. No user-serviceable parts inside.",
|
||||||
"url" => user.ap_id,
|
|
||||||
"manuallyApprovesFollowers" => false,
|
"manuallyApprovesFollowers" => false,
|
||||||
"publicKey" => %{
|
|
||||||
"id" => "#{user.ap_id}#main-key",
|
|
||||||
"owner" => user.ap_id,
|
|
||||||
"publicKeyPem" => public_key
|
|
||||||
},
|
|
||||||
"endpoints" => endpoints,
|
|
||||||
"invisible" => User.invisible?(user)
|
"invisible" => User.invisible?(user)
|
||||||
}
|
})
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -77,13 +59,8 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
end
|
end
|
||||||
|
|
||||||
def render("user.json", %{user: user}) do
|
def render("user.json", %{user: user}) do
|
||||||
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
|
||||||
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
|
||||||
public_key = :public_key.pem_encode([public_key])
|
|
||||||
user = User.sanitize_html(user)
|
user = User.sanitize_html(user)
|
||||||
|
|
||||||
endpoints = render("endpoints.json", %{user: user})
|
|
||||||
|
|
||||||
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
emoji_tags = Transmogrifier.take_emoji_tags(user)
|
||||||
|
|
||||||
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
fields = Enum.map(user.fields, &Map.put(&1, "type", "PropertyValue"))
|
||||||
|
|
@ -102,25 +79,9 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
do: Date.to_iso8601(user.birthday),
|
do: Date.to_iso8601(user.birthday),
|
||||||
else: nil
|
else: nil
|
||||||
|
|
||||||
%{
|
Map.merge(common_actor_fields(user), %{
|
||||||
"id" => user.ap_id,
|
|
||||||
"type" => user.actor_type,
|
|
||||||
"following" => "#{user.ap_id}/following",
|
|
||||||
"followers" => "#{user.ap_id}/followers",
|
|
||||||
"inbox" => "#{user.ap_id}/inbox",
|
|
||||||
"outbox" => "#{user.ap_id}/outbox",
|
|
||||||
"featured" => "#{user.ap_id}/collections/featured",
|
"featured" => "#{user.ap_id}/collections/featured",
|
||||||
"preferredUsername" => user.nickname,
|
"preferredUsername" => user.nickname,
|
||||||
"name" => user.name,
|
|
||||||
"summary" => user.bio,
|
|
||||||
"url" => user.ap_id,
|
|
||||||
"manuallyApprovesFollowers" => user.is_locked,
|
|
||||||
"publicKey" => %{
|
|
||||||
"id" => "#{user.ap_id}#main-key",
|
|
||||||
"owner" => user.ap_id,
|
|
||||||
"publicKeyPem" => public_key
|
|
||||||
},
|
|
||||||
"endpoints" => endpoints,
|
|
||||||
"attachment" => fields,
|
"attachment" => fields,
|
||||||
"tag" => emoji_tags,
|
"tag" => emoji_tags,
|
||||||
# Note: key name is indeed "discoverable" (not an error)
|
# Note: key name is indeed "discoverable" (not an error)
|
||||||
|
|
@ -130,7 +91,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
"vcard:bday" => birthday,
|
"vcard:bday" => birthday,
|
||||||
"webfinger" => "acct:#{User.full_nickname(user)}",
|
"webfinger" => "acct:#{User.full_nickname(user)}",
|
||||||
"published" => Pleroma.Web.CommonAPI.Utils.to_masto_date(user.inserted_at)
|
"published" => Pleroma.Web.CommonAPI.Utils.to_masto_date(user.inserted_at)
|
||||||
}
|
})
|
||||||
|> Map.merge(
|
|> Map.merge(
|
||||||
maybe_make_image(
|
maybe_make_image(
|
||||||
&User.avatar_url/2,
|
&User.avatar_url/2,
|
||||||
|
|
@ -283,7 +244,7 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
}) do
|
}) do
|
||||||
collection =
|
collection =
|
||||||
Enum.map(activities, fn activity ->
|
Enum.map(activities, fn activity ->
|
||||||
{:ok, data} = Transmogrifier.prepare_outgoing(activity.data)
|
{:ok, data} = Transmogrifier.prepare_activity(activity.data)
|
||||||
data
|
data
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
@ -309,6 +270,33 @@ defmodule Pleroma.Web.ActivityPub.UserView do
|
||||||
|> Map.merge(Utils.make_json_ld_header())
|
|> Map.merge(Utils.make_json_ld_header())
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp common_actor_fields(%User{} = user) do
|
||||||
|
endpoints = render("endpoints.json", %{user: user})
|
||||||
|
|
||||||
|
{:ok, _, public_key} = Keys.keys_from_pem(user.keys)
|
||||||
|
public_key = :public_key.pem_entry_encode(:SubjectPublicKeyInfo, public_key)
|
||||||
|
public_key = :public_key.pem_encode([public_key])
|
||||||
|
|
||||||
|
%{
|
||||||
|
"id" => user.ap_id,
|
||||||
|
"type" => user.actor_type,
|
||||||
|
"following" => "#{user.ap_id}/following",
|
||||||
|
"followers" => "#{user.ap_id}/followers",
|
||||||
|
"inbox" => "#{user.ap_id}/inbox",
|
||||||
|
"outbox" => "#{user.ap_id}/outbox",
|
||||||
|
"name" => user.name,
|
||||||
|
"summary" => user.bio,
|
||||||
|
"url" => user.ap_id,
|
||||||
|
"manuallyApprovesFollowers" => user.is_locked,
|
||||||
|
"endpoints" => endpoints,
|
||||||
|
"publicKey" => %{
|
||||||
|
"id" => "#{user.ap_id}#main-key",
|
||||||
|
"owner" => user.ap_id,
|
||||||
|
"publicKeyPem" => public_key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
defp maybe_put_total_items(map, false, _total), do: map
|
defp maybe_put_total_items(map, false, _total), do: map
|
||||||
|
|
||||||
defp maybe_put_total_items(map, true, total) do
|
defp maybe_put_total_items(map, true, total) do
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue