Compare commits

..

26 commits

Author SHA1 Message Date
Floatingghost 778b213945 enqueue pin fetches after changeset validation
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
2024-06-01 08:25:35 +01:00
floatingghost 8f97c15b07 Merge pull request 'Preserve Meilisearch’s result ranking' (#772) from Oneric/akkoma:search-meili-order into develop
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
Reviewed-on: #772
2024-05-31 14:12:05 +00:00
Floatingghost 3af0c53a86 use proper workers for fetching pins instead of an ad-hoc task (#788)
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
Reviewed-on: #788
Co-authored-by: Floatingghost <hannah@coffee-and-dreams.uk>
Co-committed-by: Floatingghost <hannah@coffee-and-dreams.uk>
2024-05-31 08:58:52 +00:00
Oneric fc7e07f424 meilisearch: enable using search_key
All checks were successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build-amd64 Pipeline was successful
ci/woodpecker/pr/build-arm64 Pipeline was successful
ci/woodpecker/pr/docs Pipeline was successful
Using only the admin key works as well currently
and Akkoma needs to know the admin key to be able
to add new entries etc. However the Meilisearch
key descriptions suggest the admin key is not
supposed to be used for searches, so let’s not.

For compatibility with existings configs, search_key remains optional.
2024-05-29 23:17:27 +00:00
Oneric 59685e25d2 meilisearch: show keys by name not description
This makes show-key’s output match our documentation as of Meilisearch
1.8.0-8-g4d5971f343c00d45c11ef0cfb6f61e83a8508208. Since I’m not sure
if older versions maybe only provided description, it will fallback to
the latter if no name parameter exists.
2024-05-29 23:17:27 +00:00
Oneric 65aeaefa41 meilisearch: respect meili’s result ranking
Meilisearch is already configured to return results sorted by a
particular ranking configured in the meilisearch CLI task.
Resorting the returned top results by date partially negates this and
runs counter to what someone with tweaked settings expects.

Issue and fix identified by AdamK2003 in
#579
But instead of using a O(n^2) resorting, this commit directly
retrieves results in the correct order from the database.

Closes: #579
2024-05-29 23:17:27 +00:00
Oneric 5d6cb6a459 meilisearch: remove duplicate preload 2024-05-29 23:17:27 +00:00
floatingghost 8afc3bee7a Merge pull request 'Use /var/tmp for media cache path' (#776) from norm/akkoma:nginx-var-tmp into develop
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
Reviewed-on: #776
Reviewed-by: floatingghost <hannah@coffee-and-dreams.uk>
2024-05-28 02:05:17 +00:00
floatingghost 72871d4514 Merge pull request 'Drop unused indices' (#767) from Oneric/akkoma:purge-unused-indices into develop
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
Reviewed-on: #767
2024-05-28 01:35:18 +00:00
floatingghost 72af38c0e9 Merge pull request 'migrate CI config to v2' (#785) from woodpecker-v2 into develop
All checks were successful
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-arm64 Pipeline was successful
ci/woodpecker/push/build-amd64 Pipeline was successful
ci/woodpecker/push/docs Pipeline was successful
Reviewed-on: #785
2024-05-27 03:32:40 +00:00
Floatingghost ae19fd90c9 use elixir 1.16 for format checks
All checks were successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build-amd64 Pipeline was successful
ci/woodpecker/pr/build-arm64 Pipeline was successful
ci/woodpecker/pr/docs Pipeline was successful
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-amd64 Pipeline was successful
ci/woodpecker/push/build-arm64 Pipeline was successful
ci/woodpecker/push/docs Pipeline was successful
2024-05-27 04:07:44 +01:00
Floatingghost 66b3248dd3 mix tests probably shouldn't be async
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
ci/woodpecker/pr/build-amd64 Pipeline is pending
ci/woodpecker/pr/build-arm64 Pipeline is pending
ci/woodpecker/pr/docs Pipeline is pending
ci/woodpecker/pr/lint Pipeline is pending
ci/woodpecker/pr/test Pipeline is pending
2024-05-27 04:03:13 +01:00
Floatingghost 73ead8656a don't allow emoji formatter to be async
Some checks failed
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline failed
ci/woodpecker/pr/build-arm64 unknown status
ci/woodpecker/pr/build-amd64 unknown status
ci/woodpecker/pr/docs unknown status
2024-05-27 03:25:18 +01:00
Floatingghost f32a7fd76a arch is aarch64 now
Some checks failed
ci/woodpecker/push/lint Pipeline was successful
ci/woodpecker/push/test Pipeline was successful
ci/woodpecker/push/build-amd64 Pipeline was successful
ci/woodpecker/push/build-arm64 Pipeline was successful
ci/woodpecker/push/docs Pipeline was successful
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline failed
ci/woodpecker/pr/build-arm64 unknown status
ci/woodpecker/pr/build-amd64 unknown status
ci/woodpecker/pr/docs unknown status
2024-05-27 03:02:02 +01:00
Floatingghost 4078fd655c migrate CI config to v2
Some checks are pending
ci/woodpecker/manual/build-arm64 Pipeline is pending
ci/woodpecker/manual/lint Pipeline was successful
ci/woodpecker/manual/test Pipeline was successful
ci/woodpecker/manual/build-amd64 Pipeline was successful
ci/woodpecker/manual/docs Pipeline was successful
2024-05-27 02:56:05 +01:00
floatingghost 5bdef8c724 Merge pull request 'Allow for attachment to be a single object in user data' (#783) from single-attachment into develop
Some checks are pending
ci/woodpecker/push/test Pipeline is pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/test/1 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test/2 Pipeline is pending
ci/woodpecker/push/test/3 Pipeline is pending
ci/woodpecker/push/test/4 Pipeline is pending
Reviewed-on: #783
2024-05-27 01:44:53 +00:00
floatingghost cdc918c8f1 Merge pull request 'Document AP and nodeinfo extensions' (#778) from Oneric/akkoma:doc_ap-extensions into develop
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
Reviewed-on: #778
2024-05-27 01:34:58 +00:00
Floatingghost f15eded3e1 Add extra test case for nonsense field, increase timeouts
Some checks failed
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build-arm64 unknown status
ci/woodpecker/pr/build-amd64 unknown status
ci/woodpecker/pr/docs unknown status
2024-05-27 02:09:48 +01:00
Oneric 05eda169fe Document AP and nodeinfo extensions
Some checks are pending
ci/woodpecker/pr/build-amd64 Pipeline is pending
ci/woodpecker/pr/build-arm64 Pipeline is pending
ci/woodpecker/pr/docs Pipeline is pending
ci/woodpecker/pr/lint Pipeline is pending
ci/woodpecker/pr/test Pipeline is pending
And while add it point to this via a top-level
FEDERATION.md document as standardised by FEP-67ff.

Also add a few missing descriptions to the config cheatsheet
and move the recently removed C2S extension into an appropiate
subsection.
2024-05-26 19:04:06 +02:00
floatingghost 3ce855cbde Merge pull request 'Fix Exiftool stderr being read as an image description' (#782) from norm/akkoma:fix-exiftool-description into develop
Some checks are pending
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
Reviewed-on: #782
2024-05-26 16:11:12 +00:00
Floatingghost da67e69af5 Allow for attachment to be a single object in user data
Some checks failed
ci/woodpecker/push/build-amd64 Pipeline is pending
ci/woodpecker/push/build-arm64 Pipeline is pending
ci/woodpecker/push/docs Pipeline is pending
ci/woodpecker/push/lint Pipeline is pending
ci/woodpecker/push/test Pipeline is pending
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build-arm64 unknown status
ci/woodpecker/pr/build-amd64 unknown status
ci/woodpecker/pr/docs unknown status
2024-05-26 17:09:26 +01:00
Norm c2d3221be3 Fix Exiftool stderr being read as an image description
Some checks are pending
ci/woodpecker/pr/build-amd64 Pipeline is pending
ci/woodpecker/pr/build-arm64 Pipeline is pending
ci/woodpecker/pr/docs Pipeline is pending
ci/woodpecker/pr/lint Pipeline is pending
ci/woodpecker/pr/test Pipeline is pending
Fixes: #773
2024-05-23 14:44:17 -04:00
Norm bb29c5bed2 Update tor/i2p guide
Some checks are pending
ci/woodpecker/pr/build-amd64 Pipeline is pending
ci/woodpecker/pr/build-arm64 Pipeline is pending
ci/woodpecker/pr/docs Pipeline is pending
ci/woodpecker/pr/lint Pipeline is pending
ci/woodpecker/pr/test Pipeline is pending
Direct users to add in the appropriate headers and update the listening
port instead of copy/pasting a config that's already outdated and
probably would otherwise have to be synced with the main example nginx
config.
2024-05-16 19:08:02 -04:00
Norm bc46f3da4c Update mediaproxy howto
Since the configuration options on the nginx side already exist in the
sample config, there's no need to tell users to copy-paste those
settings in again.
2024-05-16 19:06:59 -04:00
Norm 7e709768c3 Use /var/tmp for media cache path in apache/nginx configs
The /var/tmp directory is not mounted as tmpfs unlike /tmp which is
mounted as such on some distros like Fedora or Arch. Since there isn't
really a benefit to having the cache on tmpfs, this change should allow
for a larger cache if needed without worrying about running out of RAM.
2024-05-15 20:42:48 -04:00
Oneric b7e3d44756 Drop unused indices
Some checks failed
ci/woodpecker/pr/lint Pipeline was successful
ci/woodpecker/pr/test Pipeline was successful
ci/woodpecker/pr/build-arm64 unknown status
ci/woodpecker/pr/docs unknown status
ci/woodpecker/pr/build-amd64 unknown status
This promotes and expands our existing optional migration.
Based on usage statistics from several instances, see:
#764

activities_hosts is now retained after all since it’s essential
for the "instance" query parameter of *oma’s public timeline to
reliably work in a reasonable amount of time. (Although akkoma-fe has
no support for this feature and apparently barely anyone uses it.)

activities_actor_index was already dropped before in
20221211234352_remove_unused_indices; no need to drop it again.

Birthday indices were introduced in pleroma starting with
20220116183110_add_birthday_to_users which is past the
last common migration 20210416051708.
2024-05-02 00:08:33 +02:00
40 changed files with 852 additions and 273 deletions

View file

@ -1,4 +1,5 @@
platform: linux/amd64 labels:
platform: linux/amd64
depends_on: depends_on:
- test - test
@ -34,7 +35,7 @@ variables:
- &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)" - &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)"
- &mix-clean "mix deps.clean --all && mix clean" - &mix-clean "mix deps.clean --all && mix clean"
pipeline: steps:
# Canonical amd64 # Canonical amd64
debian-bookworm: debian-bookworm:
image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612 image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612
@ -76,7 +77,6 @@ pipeline:
- *clean - *clean
- echo "import Config" > config/prod.secret.exs - echo "import Config" > config/prod.secret.exs
- *setup-hex - *setup-hex
- *mix-clean
- *tag-build - *tag-build
- mix deps.get --only prod - mix deps.get --only prod
- mix release --path release - mix release --path release

View file

@ -1,4 +1,5 @@
platform: linux/arm64 labels:
platform: linux/aarch64
depends_on: depends_on:
- test - test
@ -34,7 +35,7 @@ variables:
- &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)" - &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)"
- &mix-clean "mix deps.clean --all && mix clean" - &mix-clean "mix deps.clean --all && mix clean"
pipeline: steps:
# Canonical arm64 # Canonical arm64
debian-bookworm: debian-bookworm:
image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612 image: hexpm/elixir:1.15.4-erlang-26.0.2-debian-bookworm-20230612

View file

@ -1,4 +1,5 @@
platform: linux/amd64 labels:
platform: linux/amd64
depends_on: depends_on:
- test - test
@ -45,7 +46,7 @@ variables:
- &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)" - &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)"
- &mix-clean "mix deps.clean --all && mix clean" - &mix-clean "mix deps.clean --all && mix clean"
pipeline: steps:
docs: docs:
<<: *on-point-release <<: *on-point-release
secrets: secrets:

View file

@ -1,4 +1,5 @@
platform: linux/amd64 labels:
platform: linux/amd64
variables: variables:
- &scw-secrets - &scw-secrets
@ -41,9 +42,9 @@ variables:
- &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)" - &clean "(rm -rf release || true) && (rm -rf _build || true) && (rm -rf /root/.mix)"
- &mix-clean "mix deps.clean --all && mix clean" - &mix-clean "mix deps.clean --all && mix clean"
pipeline: steps:
lint: lint:
image: akkoma/ci-base:1.15-otp26 image: akkoma/ci-base:1.16-otp26
<<: *on-pr-open <<: *on-pr-open
environment: environment:
MIX_ENV: test MIX_ENV: test

View file

@ -1,4 +1,5 @@
platform: linux/amd64 labels:
platform: linux/amd64
depends_on: depends_on:
- lint - lint
@ -12,12 +13,6 @@ matrix:
- 25 - 25
- 26 - 26
include: include:
- ELIXIR_VERSION: 1.14
OTP_VERSION: 25
- ELIXIR_VERSION: 1.15
OTP_VERSION: 25
- ELIXIR_VERSION: 1.15
OTP_VERSION: 26
- ELIXIR_VERSION: 1.16 - ELIXIR_VERSION: 1.16
OTP_VERSION: 26 OTP_VERSION: 26
@ -73,7 +68,7 @@ services:
POSTGRES_USER: postgres POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres POSTGRES_PASSWORD: postgres
pipeline: steps:
test: test:
image: akkoma/ci-base:${ELIXIR_VERSION}-otp${OTP_VERSION} image: akkoma/ci-base:${ELIXIR_VERSION}-otp${OTP_VERSION}
<<: *on-pr-open <<: *on-pr-open

View file

@ -11,6 +11,15 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Issue allowing use of non-media objects as attachments and crashing timeline rendering - Issue allowing use of non-media objects as attachments and crashing timeline rendering
- Issue allowing webfinger spoofing in certain situations - Issue allowing webfinger spoofing in certain situations
## Added
- Implement [FEP-67ff](https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md) (federation documentation)
## Added
- Meilisearch: it is now possible to use separate keys for search and admin actions
## Fixed
- Meilisearch: order of results returned from our REST API now actually matches how Meilisearch ranks results
## 2024.04 ## 2024.04
## Added ## Added
@ -339,11 +348,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## 2022.08 ## 2022.08
### Removed
- Non-finch HTTP adapters. `:tesla, :adapter` is now highly recommended to be set to the default.
## 2022.08
### Added ### Added
- extended runtime module support, see config cheatsheet - extended runtime module support, see config cheatsheet
- quote posting; quotes are limited to public posts - quote posting; quotes are limited to public posts

42
FEDERATION.md Normal file
View file

@ -0,0 +1,42 @@
# Federation
## Supported federation protocols and standards
- [ActivityPub](https://www.w3.org/TR/activitypub/) (Server-to-Server)
- [WebFinger](https://webfinger.net/)
- [Http Signatures](https://datatracker.ietf.org/doc/html/draft-cavage-http-signatures)
- [NodeInfo](https://nodeinfo.diaspora.software/)
## Supported FEPs
- [FEP-67ff: FEDERATION](https://codeberg.org/fediverse/fep/src/branch/main/fep/67ff/fep-67ff.md)
- [FEP-f1d5: NodeInfo in Fediverse Software](https://codeberg.org/fediverse/fep/src/branch/main/fep/f1d5/fep-f1d5.md)
- [FEP-fffd: Proxy Objects](https://codeberg.org/fediverse/fep/src/branch/main/fep/fffd/fep-fffd.md)
## ActivityPub
Akkoma mostly follows the server-to-server parts of the ActivityPub standard,
but implements quirks for Mastodon compatibility as well as Mastodon-specific
and custom extensions.
See our documentation and Mastodons federation information
linked further below for details on these quirks and extensions.
Akkoma does not perform JSON-LD processing.
### Required extensions
#### HTTP Signatures
All AP S2S POST requests to Akkoma instances MUST be signed.
Depending on instance configuration the same may be true for GET requests.
## Nodeinfo
Akkoma provides many additional entries in its nodeinfo response,
see the documentation linked below for details.
## Additional documentation
- [Akkomas ActivityPub extensions](https://docs.akkoma.dev/develop/development/ap_extensions/)
- [Akkomas nodeinfo extensions](https://docs.akkoma.dev/develop/development/nodeinfo_extensions/)
- [Mastodons federation requirements](https://github.com/mastodon/mastodon/blob/main/FEDERATION.md)

View file

@ -63,6 +63,8 @@ To add configuration to your config file, you can copy it from the base config.
* `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `["example.com"]`, (default: `[]`) * `local_bubble`: Array of domains representing instances closely related to yours. Used to populate the `bubble` timeline. e.g `["example.com"]`, (default: `[]`)
* `languages`: List of Language Codes used by the instance. This is used to try and set a default language from the frontend. It will try and find the first match between the languages set here and the user's browser languages. It will default to the first language in this setting if there is no match.. (default `["en"]`) * `languages`: List of Language Codes used by the instance. This is used to try and set a default language from the frontend. It will try and find the first match between the languages set here and the user's browser languages. It will default to the first language in this setting if there is no match.. (default `["en"]`)
* `export_prometheus_metrics`: Enable prometheus metrics, served at `/api/v1/akkoma/metrics`, requiring the `admin:metrics` oauth scope. * `export_prometheus_metrics`: Enable prometheus metrics, served at `/api/v1/akkoma/metrics`, requiring the `admin:metrics` oauth scope.
* `privileged_staff`: Set to `true` to give moderators access to a few higher responsibility actions.
* `federated_timeline_available`: Set to `false` to remove access to the federated timeline for all users.
## :database ## :database
* `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes). * `improved_hashtag_timeline`: Setting to force toggle / force disable improved hashtags timeline. `:enabled` forces hashtags to be fetched from `hashtags` table for hashtags timeline. `:disabled` forces object-embedded hashtags to be used (slower). Keep it `:auto` for automatic behaviour (it is auto-set to `:enabled` [unless overridden] when HashtagsTableMigrator completes).

View file

@ -6,37 +6,17 @@ With the `mediaproxy` function you can use nginx to cache this content, so users
## Activate it ## Activate it
* Edit your nginx config and add the following location to your main server block:
```
location /proxy {
return 404;
}
```
* Set up a subdomain for the proxy with its nginx config on the same machine * Set up a subdomain for the proxy with its nginx config on the same machine
*(the latter is not strictly required, but for simplicity well assume so)* * Edit the nginx config for the upload/MediaProxy subdomain to point to the subdomain that has been set up
* In this subdomains server block add
```
location /proxy {
proxy_cache akkoma_media_cache;
proxy_cache_lock on;
proxy_pass http://localhost:4000;
}
```
Also add the following on top of the configuration, outside of the `server` block:
```
proxy_cache_path /tmp/akkoma-media-cache levels=1:2 keys_zone=akkoma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
```
If you came here from one of the installation guides, take a look at the example configuration `/installation/nginx/akkoma.nginx`, where this part is already included.
* Append the following to your `prod.secret.exs` or `dev.secret.exs` (depends on which mode your instance is running): * Append the following to your `prod.secret.exs` or `dev.secret.exs` (depends on which mode your instance is running):
``` ```elixir
# Replace media.example.td with the subdomain you set up earlier
config :pleroma, :media_proxy, config :pleroma, :media_proxy,
enabled: true, enabled: true,
proxy_opts: [ proxy_opts: [
redirect_on_failure: true redirect_on_failure: true
], ],
base_url: "https://cache.akkoma.social" base_url: "https://media.example.tld"
``` ```
You **really** should use a subdomain to serve proxied files; while we will fix bugs resulting from this, serving arbitrary remote content on your main domain namespace is a significant attack surface. You **really** should use a subdomain to serve proxied files; while we will fix bugs resulting from this, serving arbitrary remote content on your main domain namespace is a significant attack surface.

View file

@ -130,59 +130,26 @@ config :pleroma, :http_security,
enabled: false enabled: false
``` ```
Use this as the Nginx config: In the Nginx config, add the following into the `location /` block:
``` ```nginx
proxy_cache_path /tmp/akkoma-media-cache levels=1:2 keys_zone=akkoma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
# The above already exists in a clearnet instance's config.
# If not, add it.
server {
listen 127.0.0.1:14447;
server_name youri2paddress;
# Comment to enable logs
access_log /dev/null;
error_log /dev/null;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
client_max_body_size 16m;
location / {
add_header X-XSS-Protection "0"; add_header X-XSS-Protection "0";
add_header X-Permitted-Cross-Domain-Policies none; add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY; add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff; add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin; add_header Referrer-Policy same-origin;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_pass http://localhost:4000;
client_max_body_size 16m;
}
location /proxy {
proxy_cache akkoma_media_cache;
proxy_cache_lock on;
proxy_ignore_client_abort on;
proxy_pass http://localhost:4000;
}
}
``` ```
reload Nginx:
Change the `listen` directive to the following:
```nginx
listen 127.0.0.1:14447;
``` ```
systemctl stop i2pd.service --no-block
systemctl start i2pd.service Set `server_name` to your i2p address.
Reload Nginx:
```
systemctl restart i2pd.service --no-block
systemctl reload nginx.service
``` ```
*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes). *Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).

View file

@ -74,56 +74,23 @@ config :pleroma, :http_security,
enabled: false enabled: false
``` ```
Use this as the Nginx config: In the Nginx config, add the following into the `location /` block:
``` ```nginx
proxy_cache_path /tmp/akkoma-media-cache levels=1:2 keys_zone=akkoma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
# The above already exists in a clearnet instance's config.
# If not, add it.
server {
listen 127.0.0.1:8099;
server_name youronionaddress;
# Comment to enable logs
access_log /dev/null;
error_log /dev/null;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
client_max_body_size 16m;
location / {
add_header X-XSS-Protection "0"; add_header X-XSS-Protection "0";
add_header X-Permitted-Cross-Domain-Policies none; add_header X-Permitted-Cross-Domain-Policies none;
add_header X-Frame-Options DENY; add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff; add_header X-Content-Type-Options nosniff;
add_header Referrer-Policy same-origin; add_header Referrer-Policy same-origin;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $http_host;
proxy_pass http://localhost:4000;
client_max_body_size 16m;
}
location /proxy {
proxy_cache akkoma_media_cache;
proxy_cache_lock on;
proxy_ignore_client_abort on;
proxy_pass http://localhost:4000;
}
}
``` ```
reload Nginx:
Change the `listen` directive to the following:
```nginx
listen 127.0.0.1:8099;
```
Set the `server_name` to your onion address.
Reload Nginx:
``` ```
systemctl reload nginx systemctl reload nginx
``` ```

View file

@ -33,6 +33,7 @@ indexes faster when it can process many posts in a single batch.
> config :pleroma, Pleroma.Search.Meilisearch, > config :pleroma, Pleroma.Search.Meilisearch,
> url: "http://127.0.0.1:7700/", > url: "http://127.0.0.1:7700/",
> private_key: "private key", > private_key: "private key",
> search_key: "search key",
> initial_indexing_chunk_size: 100_000 > initial_indexing_chunk_size: 100_000
Information about setting up meilisearch can be found in the Information about setting up meilisearch can be found in the
@ -45,7 +46,7 @@ is hardly usable on a somewhat big instance.
### Private key authentication (optional) ### Private key authentication (optional)
To set the private key, use the `MEILI_MASTER_KEY` environment variable when starting. After setting the _master key_, To set the private key, use the `MEILI_MASTER_KEY` environment variable when starting. After setting the _master key_,
you have to get the _private key_, which is actually used for authentication. you have to get the _private key_ and possibly _search key_, which are actually used for authentication.
=== "OTP" === "OTP"
```sh ```sh
@ -57,7 +58,11 @@ you have to get the _private key_, which is actually used for authentication.
mix pleroma.search.meilisearch show-keys <your master key here> mix pleroma.search.meilisearch show-keys <your master key here>
``` ```
You will see a "Default Admin API Key", this is the key you actually put into your configuration file. You will see a "Default Admin API Key", this is the key you actually put into
your configuration file as `private_key`. You should also see a
"Default Search API key", put this into your config as `search_key`.
If your version of Meilisearch only showed the former,
just leave `search_key` completely unset in Akkoma's config.
### Initial indexing ### Initial indexing

View file

@ -4,7 +4,6 @@
The following endpoints are additionally present into our actors. The following endpoints are additionally present into our actors.
- `oauthRegistrationEndpoint` (`http://litepub.social/ns#oauthRegistrationEndpoint`) - `oauthRegistrationEndpoint` (`http://litepub.social/ns#oauthRegistrationEndpoint`)
- `uploadMedia` (`https://www.w3.org/ns/activitystreams#uploadMedia`)
### oauthRegistrationEndpoint ### oauthRegistrationEndpoint
@ -12,6 +11,279 @@ Points to MastodonAPI `/api/v1/apps` for now.
See <https://docs.joinmastodon.org/methods/apps/> See <https://docs.joinmastodon.org/methods/apps/>
## Emoji reactions
Emoji reactions are implemented as a new activity type `EmojiReact`.
A single user is allowed to react multiple times with different emoji to the
same post. However, they may only react at most once with the same emoji.
Repeated reaction from the same user with the same emoji are to be ignored.
Emoji reactions are also distinct from `Like` activities and a user may both
`Like` and react to a post.
!!! note
Misskey also supports emoji reactions, but the implementations differs.
It equates likes and reactions and only allows a single reaction per post.
The emoji is placed in the `content` field of the activity
and the `object` property points to the note reacting to.
Emoji can either be any Unicode emoji sequence or a custom emoji.
The latter must place their shortcode, including enclosing colons,
into `content` and put the emoji object inside the `tag` property.
The `tag` property MAY be omitted for Unicode emoji.
An example reaction with a Unicode emoji:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://example.org/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"type": "EmojiReact",
"id": "https://example.org/activities/23143872a0346141",
"actor": "https://example.org/users/akko",
"nickname": "akko",
"to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"],
"cc": ["https://www.w3.org/ns/activitystreams#Public"],
"content": "🧡",
"object": "https://remote.example/objects/9f0e93499d8314a9"
}
```
An example reaction with a custom emoji:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://example.org/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"type": "EmojiReact",
"id": "https://example.org/activities/d75586dec0541650",
"actor": "https://example.org/users/akko",
"nickname": "akko",
"to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"],
"cc": ["https://www.w3.org/ns/activitystreams#Public"],
"content": ":mouse:",
"object": "https://remote.example/objects/9f0e93499d8314a9",
"tag": [{
"type": "Emoji",
"id": null,
"name": "mouse",
"icon": {
"type": "Image",
"url": "https://example.org/emoji/mouse/mouse.png"
}
}]
}
```
!!! note
Although an emoji reaction can only contain a single emoji,
for compatibility with older versions of Pleroma and Akkoma,
it is recommended to wrap the emoji object in a single-element array.
When reacting with a remote custom emoji do not include the remote domain in `content`s shortcode
*(unlike in our REST API which needs the domain)*:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://example.org/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"type": "EmojiReact",
"id": "https://example.org/activities/7993dcae98d8d5ec",
"actor": "https://example.org/users/akko",
"nickname": "akko",
"to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"],
"cc": ["https://www.w3.org/ns/activitystreams#Public"],
"content": ":hug:",
"object": "https://remote.example/objects/9f0e93499d8314a9",
"tag": [{
"type": "Emoji",
"id": "https://other.example/emojis/hug",
"name": "hug",
"icon": {
"type": "Image",
"url": "https://other.example/files/b71cea432b3fad67.webp"
}
}]
}
```
Emoji reactions can be retracted using a standard `Undo` activity:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"http://example.org/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"type": "Undo",
"id": "http://example.org/activities/4685792e-efb6-4309-b508-ae4f355dd695",
"actor": "https://example.org/users/akko",
"to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"],
"cc": ["https://www.w3.org/ns/activitystreams#Public"],
"object": "https://example.org/activities/23143872a0346141"
}
```
## User profile backgrounds
Akkoma federates user profile backgrounds the same way as Sharkey.
An actors ActivityPub representation contains an additional
`backgroundUrl` property containing an `Image` object. This property
belongs to the `"sharkey": "https://joinsharkey.org/ns#"` namespace.
## Quote Posts
Akkoma allows referencing a single other note as a quote,
which will be prominently displayed in the interface.
The quoted post is referenced by its ActivityPub id in the `quoteUri` property.
!!! note
Old Misskey only understood and modern Misskey still prefers
the `_misskey_quote` property for this. Similar some other older
software used `quoteUrl` or `quoteURL`.
All current implementations with quote support understand `quoteUri`.
Example:
```json
{
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://example.org/schemas/litepub-0.1.jsonld",
{
"@language": "und"
}
],
"type": "Note",
"id": "https://example.org/activities/85717e587f95d5c0",
"actor": "https://example.org/users/akko",
"to": ["https://remote.example/users/diana", "https://example.org/users/akko/followers"],
"cc": ["https://www.w3.org/ns/activitystreams#Public"],
"context": "https://example.org/contexts/1",
"content": "Look at that!",
"quoteUri": "http://remote.example/status/85717e587f95d5c0",
"contentMap": {
"en": "Look at that!"
},
"source": {
"content": "Look at that!",
"mediaType": "text/plain"
},
"published": "2024-04-06T23:40:28Z",
"updated": "2024-04-06T23:40:28Z",
"attachemnt": [],
"tag": []
}
```
## Threads
Akkoma assigns all posts of the same thread the same `context`. This is a
standard ActivityPub property but its meaning is left vague. Akkoma will
always treat posts with identical `context` as part of the same thread.
`context` must not be assumed to hold any meaning or be dereferencable.
Incoming posts without `context` will be assigned a new context.
!!! note
Mastodon uses the non-standard `conversation` property for the same purpose
*(named after an older OStatus property)*. For incoming posts without
`context` but with `converstions` Akkoma will use the value from
`conversations` to fill in `context`.
For outgoing posts Akkoma will duplicate the context into `conversation`.
## Post Source
Unlike Mastodon, Akkoma supports drafting posts in multiple source formats
besides plaintext, like Markdown or MFM. The original input is preserved
in the standard ActivityPub `source` property *(not supported by Mastodon)*.
Still, `content` will always be present and contain the prerendered HTML form.
Supported `mediaType` include:
- `text/plain`
- `text/markdown`
- `text/bbcode`
- `text/x.misskeymarkdown`
## Post Language
!!! note
This is also supported in and compatible with Mastodon, but since
joinmastodon.org doesnt document it yet it is included here.
[GoToSocial](https://docs.gotosocial.org/en/latest/federation/federating_with_gotosocial/#content-contentmap-and-language)
has a more refined version of this which can correctly deal with multiple language entries.
A post can indicate its language by including a `contentMap` object
which contains a sub key named after the languages ISO 639-1 code
and its content identical to the posts `content` field.
Currently Akkoma, just like Mastodon, only properly supports a single language entry,
in case of multiple entries a random language will be picked.
Furthermore, Akkoma currently only reads the `content` field
and never the value from `contentMap`.
## Local post scope
Post using this scope will never federate to other servers
but for the sake of completeness it is listed here.
In addition to the usual scopes *(public, unlisted, followers-only, direct)*
Akkoma supports an “unlisted” post scope. Such posts will not federate to
other instances and only be shown to logged-in users on the same instance.
It is included into the local timeline.
This may be useful to discuss or announce instance-specific policies and topics.
A post is addressed to the local scope by including `<base url of instance>/#Public`
in its `to` field. E.g. if the instance is on `https://example.org` it would use
`https://example.org/#Public`.
An implementation creating a new post MUST NOT address both the local and
general public scope `as:Public` at the same time. A post addressing the local
scope MUST NOT be sent to other instances or be possible to fetch by other
instances regardless of potential other listed addressees.
When receiving a remote post addressing both the public scope and what appears
to be a local-scope identifier, the post SHOULD be treated without assigning any
special meaning to the potential local-scope identifier.
!!! note
Misskey-derivatives have a similar concept of non-federated posts,
however those are also shown publicly on the local web interface
and are thus visible to non-members.
## List post scope
Messages originally addressed to a custom list will contain
a `listMessage` field with an unresolvable pseudo ActivityPub id.
# Deprecated and Removed Extensions
The following extensions were used in the past but have been dropped.
Documentation is retained here as a reference and since old objects might
still contains related fields.
## Actor endpoints
The following endpoints used to be present:
- `uploadMedia` (`https://www.w3.org/ns/activitystreams#uploadMedia`)
### uploadMedia ### uploadMedia
Inspired by <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>, it is part of the ActivityStreams namespace because it used to be part of the ActivityPub specification and got removed from it. Inspired by <https://www.w3.org/wiki/SocialCG/ActivityPub/MediaUpload>, it is part of the ActivityStreams namespace because it used to be part of the ActivityPub specification and got removed from it.
@ -20,9 +292,8 @@ Content-Type: multipart/form-data
Parameters: Parameters:
- (required) `file`: The file being uploaded - (required) `file`: The file being uploaded
- (optionnal) `description`: A plain-text description of the media, for accessibility purposes. - (optional) `description`: A plain-text description of the media, for accessibility purposes.
Response: HTTP 201 Created with the object into the body, no `Location` header provided as it doesn't have an `id` Response: HTTP 201 Created with the object into the body, no `Location` header provided as it doesn't have an `id`
The object given in the reponse should then be inserted into an Object's `attachment` field. The object given in the response should then be inserted into an Object's `attachment` field.

View file

@ -0,0 +1,141 @@
# Nodeinfo Extensions
Akkoma currently implements version 2.0 and 2.1 of nodeinfo spec,
but provides the following additional fields.
## metadata
The spec leaves the content of `metadata` up to implementations
and indeed Akkoma adds many fields here apart from the commonly
found `nodeName` and `nodeDescription` fields.
### accountActivationRequired
Whether or not users need to confirm their email before completing registration.
*(boolean)*
!!! note
Not to be confused with account approval, where each registration needs to
be manually approved by an admin. Account approval has no nodeinfo entry.
### features
Array of strings denoting supported server features. E.g. a server supporting
quote posts should include a `"quote_posting"` entry here.
A non-exhaustive list of possible features:
- `polls`
- `quote_posting`
- `editing`
- `bubble_timeline`
- `pleroma_emoji_reactions` *(Unicode emoji)*
- `custom_emoji_reactions`
- `akkoma_api`
- `akkoma:machine_translation`
- `mastodon_api`
- `pleroma_api`
### federatedTimelineAvailable
Whether or not the “federated timeline”, i.e. a timeline containing posts from
the entire known network, is made available.
*(boolean)*
### federation
This section is optional and can contain various custom keys describing federation policies.
The following are required to be presented:
- `enabled` *(boolean)* whether the server federates at all
A non-exhaustive list of optional keys:
- `exclusions` *(boolean)* whether some federation policies are withheld
- `mrf_simple` *(object)* describes how the Simple MRF policy is configured
### fieldsLimits
A JSON object documenting restriction for user account info fields.
All properties are integers.
- `maxFields` maximum number of account info fields local users can create
- `maxRemoteFields` maximum number of account info fields remote users can have
before the user gets rejected or fields truncated
- `nameLength` maximum length of a fields name
- `valueLength` maximum length of a fields value
### invitesEnabled
Whether or not signing up via invite codes is possible.
*(boolean)*
### localBubbleInstances
Array of domains (as strings) of other instances chosen
by the admin which are shown in the bubble timeline.
### mailerEnabled
Whether or not the instance can send out emails.
*(boolean)*
### nodeDescription
Human-friendly description of this instance
*(string)*
### nodeName
Human-friendly name of this instance
*(string)*
### pollLimits
JSON object containing limits for polls created by local users.
All values are integers.
- `max_options` maximum number of poll options
- `max_option_chars` maximum characters per poll option
- `min_expiration` minimum time in seconds a poll must be open for
- `max_expiration` maximum time a poll is allowed to be open for
### postFormats
Array of strings containing media types for supported post source formats.
A non-exhaustive list of possible values:
- `text/plain`
- `text/markdown`
- `text/bbcode`
- `text/x.misskeymarkdown`
### private
Whether or not unauthenticated API access is permitted.
*(boolean)*
### privilegedStaff
Whether or not moderators are trusted to perform some
additional tasks like e.g. issuing password reset emails.
### publicTimelineVisibility
JSON object containing boolean-valued keys reporting
if a given timeline can be viewed without login.
- `local`
- `federated`
- `bubble`
### restrictedNicknames
Array of strings listing nicknames forbidden to be used during signup.
### skipThreadContainment
Whether broken threads are filtered out
*(boolean)*
### staffAccounts
Array containing ActivityPub IDs of local accounts
with some form of elevated privilege on the instance.
### suggestions
JSON object containing info on whether the interaction-based
Mastodon `/api/v1/suggestions` feature is enabled and optionally
additional implementation-defined fields with more details
on e.g. how suggested users are selected.
!!! note
This has no relation to the newer /api/v2/suggestions API
which also (or exclusively) contains staff-curated entries.
- `enabled` *(boolean)* whether or not user recommendations are enabled
### uploadLimits
JSON object documenting various upload-related size limits.
All values are integers and in bytes.
- `avatar` maximum size of uploaded user avatars
- `banner` maximum size of uploaded user profile banners
- `background` maximum size of uploaded user profile backgrounds
- `general` maximum size for all other kinds of uploads

View file

@ -60,7 +60,7 @@ ServerTokens Prod
Include /etc/letsencrypt/options-ssl-apache.conf Include /etc/letsencrypt/options-ssl-apache.conf
# Uncomment the following to enable MediaProxy caching on disk # Uncomment the following to enable MediaProxy caching on disk
#CacheRoot /tmp/akkoma-media-cache/ #CacheRoot /var/tmp/akkoma-media-cache/
#CacheDirLevels 1 #CacheDirLevels 1
#CacheDirLength 2 #CacheDirLength 2
#CacheEnable disk /proxy #CacheEnable disk /proxy

View file

@ -16,7 +16,7 @@
SCRIPTNAME=${0##*/} SCRIPTNAME=${0##*/}
# mod_disk_cache directory # mod_disk_cache directory
CACHE_DIRECTORY="/tmp/akkoma-media-cache" CACHE_DIRECTORY="/var/tmp/akkoma-media-cache"
## Removes an item via the htcacheclean utility ## Removes an item via the htcacheclean utility
## $1 - the filename, can be a pattern . ## $1 - the filename, can be a pattern .

View file

@ -3,7 +3,7 @@
# See the documentation at docs.akkoma.dev for your particular distro/OS for # See the documentation at docs.akkoma.dev for your particular distro/OS for
# installation instructions. # installation instructions.
proxy_cache_path /tmp/akkoma-media-cache levels=1:2 keys_zone=akkoma_media_cache:10m max_size=1g proxy_cache_path /var/tmp/akkoma-media-cache levels=1:2 keys_zone=akkoma_media_cache:10m max_size=1g
inactive=720m use_temp_path=off; inactive=720m use_temp_path=off;
# this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only # this is explicitly IPv4 since Pleroma.Web.Endpoint binds on IPv4 only

View file

@ -5,7 +5,7 @@
SCRIPTNAME=${0##*/} SCRIPTNAME=${0##*/}
# NGINX cache directory # NGINX cache directory
CACHE_DIRECTORY="/tmp/akkoma-media-cache" CACHE_DIRECTORY="/var/tmp/akkoma-media-cache"
## Return the files where the items are cached. ## Return the files where the items are cached.
## $1 - the filename, can be a pattern . ## $1 - the filename, can be a pattern .

View file

@ -16,7 +16,7 @@ defmodule Mix.Pleroma do
:fast_html, :fast_html,
:oban :oban
] ]
@cachex_children ["object", "user", "scrubber", "web_resp"] @cachex_children ["object", "user", "scrubber", "web_resp", "http_backoff"]
@doc "Common functions to be reused in mix tasks" @doc "Common functions to be reused in mix tasks"
def start_pleroma do def start_pleroma do
Pleroma.Config.Holder.save_default() Pleroma.Config.Holder.save_default()

View file

@ -17,6 +17,13 @@ defmodule Mix.Tasks.Pleroma.Diagnostics do
|> IO.inspect() |> IO.inspect()
end end
def run(["fetch_object", url]) do
start_pleroma()
Pleroma.Object.Fetcher.fetch_object_from_id(url)
|> IO.inspect()
end
def run(["home_timeline", nickname]) do def run(["home_timeline", nickname]) do
start_pleroma() start_pleroma()
user = Repo.get_by!(User, nickname: nickname) user = Repo.get_by!(User, nickname: nickname)

View file

@ -126,8 +126,12 @@ defmodule Mix.Tasks.Pleroma.Search.Meilisearch do
decoded = Jason.decode!(result.body) decoded = Jason.decode!(result.body)
if decoded["results"] do if decoded["results"] do
Enum.each(decoded["results"], fn %{"description" => desc, "key" => key} -> Enum.each(decoded["results"], fn
IO.puts("#{desc}: #{key}") %{"name" => name, "key" => key} ->
IO.puts("#{name}: #{key}")
%{"description" => desc, "key" => key} ->
IO.puts("#{desc}: #{key}")
end) end)
else else
IO.puts("Error fetching the keys, check the master key is correct: #{inspect(decoded)}") IO.puts("Error fetching the keys, check the master key is correct: #{inspect(decoded)}")

View file

@ -258,6 +258,27 @@ defmodule Pleroma.Activity do
def get_create_by_object_ap_id(_), do: nil def get_create_by_object_ap_id(_), do: nil
@doc """
Accepts a list of `ap__id`.
Returns a query yielding Create activities for the given objects,
in the same order as they were specified in the input list.
"""
@spec get_presorted_create_by_object_ap_id([String.t()]) :: Ecto.Queryable.t()
def get_presorted_create_by_object_ap_id(ap_ids) do
from(
a in Activity,
join:
ids in fragment(
"SELECT * FROM UNNEST(?::text[]) WITH ORDINALITY AS ids(ap_id, ord)",
^ap_ids
),
on:
ids.ap_id == fragment("?->>'object'", a.data) and
fragment("?->>'type'", a.data) == "Create",
order_by: [asc: ids.ord]
)
end
@doc """ @doc """
Accepts `ap_id` or list of `ap_id`. Accepts `ap_id` or list of `ap_id`.
Returns a query. Returns a query.

View file

@ -5,15 +5,27 @@ defmodule Pleroma.Search.Meilisearch do
alias Pleroma.Activity alias Pleroma.Activity
import Pleroma.Search.DatabaseSearch import Pleroma.Search.DatabaseSearch
import Ecto.Query
@behaviour Pleroma.Search.SearchBackend @behaviour Pleroma.Search.SearchBackend
defp meili_headers do defp meili_headers(key) do
private_key = Pleroma.Config.get([Pleroma.Search.Meilisearch, :private_key]) key_header =
if is_nil(key), do: [], else: [{"Authorization", "Bearer #{key}"}]
[{"Content-Type", "application/json"}] ++ [{"Content-Type", "application/json"} | key_header]
if is_nil(private_key), do: [], else: [{"Authorization", "Bearer #{private_key}"}] end
defp meili_headers_admin do
private_key = Pleroma.Config.get([Pleroma.Search.Meilisearch, :private_key])
meili_headers(private_key)
end
defp meili_headers_search do
search_key =
Pleroma.Config.get([Pleroma.Search.Meilisearch, :search_key]) ||
Pleroma.Config.get([Pleroma.Search.Meilisearch, :private_key])
meili_headers(search_key)
end end
def meili_get(path) do def meili_get(path) do
@ -22,7 +34,7 @@ defmodule Pleroma.Search.Meilisearch do
result = result =
Pleroma.HTTP.get( Pleroma.HTTP.get(
Path.join(endpoint, path), Path.join(endpoint, path),
meili_headers() meili_headers_admin()
) )
with {:ok, res} <- result do with {:ok, res} <- result do
@ -30,14 +42,14 @@ defmodule Pleroma.Search.Meilisearch do
end end
end end
def meili_post(path, params) do defp meili_search(params) do
endpoint = Pleroma.Config.get([Pleroma.Search.Meilisearch, :url]) endpoint = Pleroma.Config.get([Pleroma.Search.Meilisearch, :url])
result = result =
Pleroma.HTTP.post( Pleroma.HTTP.post(
Path.join(endpoint, path), Path.join(endpoint, "/indexes/objects/search"),
Jason.encode!(params), Jason.encode!(params),
meili_headers() meili_headers_search()
) )
with {:ok, res} <- result do with {:ok, res} <- result do
@ -53,7 +65,7 @@ defmodule Pleroma.Search.Meilisearch do
:put, :put,
Path.join(endpoint, path), Path.join(endpoint, path),
Jason.encode!(params), Jason.encode!(params),
meili_headers(), meili_headers_admin(),
[] []
) )
@ -70,7 +82,7 @@ defmodule Pleroma.Search.Meilisearch do
:delete, :delete,
Path.join(endpoint, path), Path.join(endpoint, path),
"", "",
meili_headers(), meili_headers_admin(),
[] []
) )
end end
@ -81,25 +93,20 @@ defmodule Pleroma.Search.Meilisearch do
author = Keyword.get(options, :author) author = Keyword.get(options, :author)
res = res =
meili_post( meili_search(%{q: query, offset: offset, limit: limit})
"/indexes/objects/search",
%{q: query, offset: offset, limit: limit}
)
with {:ok, result} <- res do with {:ok, result} <- res do
hits = result["hits"] |> Enum.map(& &1["ap"]) hits = result["hits"] |> Enum.map(& &1["ap"])
try do try do
hits hits
|> Activity.create_by_object_ap_id() |> Activity.get_presorted_create_by_object_ap_id()
|> Activity.with_preloaded_object()
|> Activity.with_preloaded_object() |> Activity.with_preloaded_object()
|> Activity.restrict_deactivated_users() |> Activity.restrict_deactivated_users()
|> maybe_restrict_local(user) |> maybe_restrict_local(user)
|> maybe_restrict_author(author) |> maybe_restrict_author(author)
|> maybe_restrict_blocked(user) |> maybe_restrict_blocked(user)
|> maybe_fetch(user, query) |> maybe_fetch(user, query)
|> order_by([object: obj], desc: obj.data["published"])
|> Pleroma.Repo.all() |> Pleroma.Repo.all()
rescue rescue
_ -> maybe_fetch([], user, query) _ -> maybe_fetch([], user, query)

View file

@ -33,8 +33,7 @@ defmodule Pleroma.Upload.Filter.Exiftool.ReadDescription do
defp read_when_empty(_, file, tag) do defp read_when_empty(_, file, tag) do
try do try do
{tag_content, 0} = {tag_content, 0} =
System.cmd("exiftool", ["-b", "-s3", tag, file], System.cmd("exiftool", ["-b", "-s3", "-ignoreMinorErrors", "-q", "-q", tag, file],
stderr_to_stdout: true,
parallelism: true parallelism: true
) )

View file

@ -1545,11 +1545,19 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
defp normalize_also_known_as(aka) when is_binary(aka), do: [aka] defp normalize_also_known_as(aka) when is_binary(aka), do: [aka]
defp normalize_also_known_as(nil), do: [] defp normalize_also_known_as(nil), do: []
defp normalize_attachment(%{} = attachment), do: [attachment]
defp normalize_attachment(attachment) when is_list(attachment), do: attachment
defp normalize_attachment(_), do: []
defp object_to_user_data(data, additional) do defp object_to_user_data(data, additional) do
fields = fields =
data data
|> Map.get("attachment", []) |> Map.get("attachment", [])
|> Enum.filter(fn %{"type" => t} -> t == "PropertyValue" end) |> normalize_attachment()
|> Enum.filter(fn
%{"type" => t} -> t == "PropertyValue"
_ -> false
end)
|> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end) |> Enum.map(fn fields -> Map.take(fields, ["name", "value"]) end)
emojis = emojis =
@ -1816,19 +1824,20 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
end end
end end
def pinned_fetch_task(nil), do: nil def enqueue_pin_fetches(%{pinned_objects: pins}) do
# enqueue a task to fetch all pinned objects
def pinned_fetch_task(%{pinned_objects: pins}) do Enum.each(pins, fn {ap_id, _} ->
if Enum.all?(pins, fn {ap_id, _} -> if is_nil(Object.get_cached_by_ap_id(ap_id)) do
Object.get_cached_by_ap_id(ap_id) || Pleroma.Workers.RemoteFetcherWorker.enqueue("fetch_remote", %{
match?({:ok, _object}, Fetcher.fetch_object_from_id(ap_id)) "id" => ap_id,
end) do "depth" => 1
:ok })
else end
:error end)
end
end end
def enqueue_pin_fetches(_), do: nil
def make_user_from_ap_id(ap_id, additional \\ []) do def make_user_from_ap_id(ap_id, additional \\ []) do
user = User.get_cached_by_ap_id(ap_id) user = User.get_cached_by_ap_id(ap_id)
@ -1836,8 +1845,6 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
Transmogrifier.upgrade_user_from_ap_id(ap_id) Transmogrifier.upgrade_user_from_ap_id(ap_id)
else else
with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do with {:ok, data} <- fetch_and_prepare_user_from_ap_id(ap_id, additional) do
{:ok, _pid} = Task.start(fn -> pinned_fetch_task(data) end)
user = user =
if data.ap_id != ap_id do if data.ap_id != ap_id do
User.get_cached_by_ap_id(data.ap_id) User.get_cached_by_ap_id(data.ap_id)
@ -1849,6 +1856,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
user user
|> User.remote_user_changeset(data) |> User.remote_user_changeset(data)
|> User.update_and_set_cache() |> User.update_and_set_cache()
|> tap(fn _ -> enqueue_pin_fetches(data) end)
else else
maybe_handle_clashing_nickname(data) maybe_handle_clashing_nickname(data)
@ -1856,6 +1864,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPub do
|> User.remote_user_changeset() |> User.remote_user_changeset()
|> Repo.insert() |> Repo.insert()
|> User.set_cache() |> User.set_cache()
|> tap(fn _ -> enqueue_pin_fetches(data) end)
end end
end end
end end

View file

@ -1034,7 +1034,7 @@ defmodule Pleroma.Web.ActivityPub.Transmogrifier do
with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id), with %User{local: false} = user <- User.get_cached_by_ap_id(ap_id),
{:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id), {:ok, data} <- ActivityPub.fetch_and_prepare_user_from_ap_id(ap_id),
{:ok, user} <- update_user(user, data) do {:ok, user} <- update_user(user, data) do
{:ok, _pid} = Task.start(fn -> ActivityPub.pinned_fetch_task(user) end) ActivityPub.enqueue_pin_fetches(user)
TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id}) TransmogrifierWorker.enqueue("user_upgrade", %{"user_id" => user.id})
{:ok, user} {:ok, user}
else else

View file

@ -0,0 +1,64 @@
defmodule Pleroma.Repo.Migrations.DropUnusedIndexes do
use Ecto.Migration
def up do
# Leftovers from a late Pleroma migration (will not be restored on rollback)
drop_i(:users, [:show_birthday], :users_show_birthday_index)
drop_i(
:users,
["date_part('month', birthday)", "date_part('day', birthday)"],
:users_birthday_month_day_index
)
# Unused
drop_i(:activities, ["(data->'cc')"], :activities_cc_index)
drop_i(:activities, ["(data->'object'->>'inReplyTo')"], :activities_in_reply_to)
drop_i(:activities, ["(data #> '{\"object\",\"likes\"}')"], :activities_likes)
drop_i(:activities, ["(data->'to')"], :activities_to_index)
drop_i(:objects, ["(data->'likes')"], :objects_likes)
drop_i(:users, [:featured_address], :users_featured_address_index)
drop_i(:users, [:following_address], :users_following_address_index)
drop_i(:users, [:invisible], :users_invisible_index)
drop_i(:users, [:last_status_at], :users_last_status_at_index)
drop_i(:users, [:tags], :users_tags_index)
drop_i(:apps, [:client_id, :client_secret], :apps_client_id_client_secret_index)
drop_i(:apps, [:user_id], :apps_user_id_index)
# Duplicate of primary key index (will not be restored on rollback)
drop_i(
:user_frontend_setting_profiles,
[:user_id, :frontend_name, :profile_name],
:user_frontend_setting_profiles_user_id_frontend_name_profile_name_index
)
end
def down do
create_i(:activities, ["(data->'cc')"], :activities_cc_index, :gin)
create_i(:activities, ["(data->'object'->>'inReplyTo')"], :activities_in_reply_to)
create_i(:activities, ["(data #> '{\"object\",\"likes\"}')"], :activities_likes, :gin)
create_i(:activities, ["(data->'to')"], :activities_to_index, :gin)
create_i(:objects, ["(data->'likes')"], :objects_likes, :gin)
create_i(:users, [:featured_address], :users_featured_address_index)
create_i(:users, [:following_address], :users_following_address_index)
create_i(:users, [:invisible], :users_invisible_index)
create_i(:users, [:last_status_at], :users_last_status_at_index)
create_i(:users, [:tags], :users_tags_index, :gin)
create_i(:apps, [:client_id, :client_secret], :apps_client_id_client_secret_index)
create_i(:apps, [:user_id], :apps_user_id_index)
end
defp drop_i(table, fields, name) do
drop_if_exists(index(table, fields, name: name))
end
defp create_i(table, fields, name, type \\ :btree) do
create_if_not_exists(index(table, fields, name: name, using: type))
end
end

View file

@ -1,74 +0,0 @@
defmodule Pleroma.Repo.Migrations.DropUnusedIndexes do
use Ecto.Migration
@disable_ddl_transaction true
@disable_migration_lock true
def up do
drop_if_exists(
index(:activities, ["(data->>'actor')", "inserted_at desc"], name: :activities_actor_index)
)
drop_if_exists(index(:activities, ["(data->'to')"], name: :activities_to_index))
drop_if_exists(index(:activities, ["(data->'cc')"], name: :activities_cc_index))
drop_if_exists(index(:activities, ["(split_part(actor, '/', 3))"], name: :activities_hosts))
drop_if_exists(
index(:activities, ["(data->'object'->>'inReplyTo')"], name: :activities_in_reply_to)
)
drop_if_exists(
index(:activities, ["((data #> '{\"object\",\"likes\"}'))"], name: :activities_likes)
)
end
def down do
create_if_not_exists(
index(:activities, ["(data->>'actor')", "inserted_at desc"],
name: :activities_actor_index,
concurrently: true
)
)
create_if_not_exists(
index(:activities, ["(data->'to')"],
name: :activities_to_index,
using: :gin,
concurrently: true
)
)
create_if_not_exists(
index(:activities, ["(data->'cc')"],
name: :activities_cc_index,
using: :gin,
concurrently: true
)
)
create_if_not_exists(
index(:activities, ["(split_part(actor, '/', 3))"],
name: :activities_hosts,
concurrently: true
)
)
create_if_not_exists(
index(:activities, ["(data->'object'->>'inReplyTo')"],
name: :activities_in_reply_to,
concurrently: true
)
)
create_if_not_exists(
index(:activities, ["((data #> '{\"object\",\"likes\"}'))"],
name: :activities_likes,
using: :gin,
concurrently: true
)
)
end
end

View file

@ -0,0 +1,51 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"blurhash": "toot:blurhash",
"Emoji": "toot:Emoji",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
},
"Hashtag": "as:Hashtag",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount",
"featured": {
"@id": "toot:featured",
"@type": "@id"
}
},
"https://w3id.org/security/v1"
],
"id": "https://fedi.vision/@vote@fedi.vision/",
"type": "Person",
"toot:discoverable": true,
"inbox": "https://fedi.vision/@vote@fedi.vision/inbox/",
"publicKey": {
"id": "https://fedi.vision/@vote@fedi.vision/#main-key",
"owner": "https://fedi.vision/@vote@fedi.vision/",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj2f+uQtdoBO9X/u2Qso4\nxHYdfy8zB24m9Gg982/ts88DAMLxZUzX0JsBWT7coL0Ipf4NSbVaqS6nKrr2P8Qs\nf97wMhowyuYxK22BMPcbpfZkFj3tVT/JkDx2iujBJJ5ZBO5KRlupjDTqV4rOAY7F\n58ad0jK9PsJNJMsJ/b8+0t3Q/K+RqCGVmtK+iPSigOYoiKoquyRzHLTfP+mpOlDa\n3f+uyAbFya7CpcgBx1zz0PALWA+oh/zhZK4yT6719Esa8SDcoJ0ws70zMxWekq1A\n3ia88/Io6SY2qFNBpzzXGO3JK8OFRFtmPV8ZfAh5Pv6y52iuTJ21kxjAG7ZTP/fY\nBQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"attachment": {
"haha": "you expected a proper object, but it was me, random nonsense"
},
"endpoints": {
"sharedInbox": "https://fedi.vision/inbox/"
},
"followers": "https://fedi.vision/@vote@fedi.vision/followers/",
"following": "https://fedi.vision/@vote@fedi.vision/following/",
"icon": {
"type": "Image",
"mediaType": "image/webp",
"url": "https://eu-central-1.linodeobjects.com:443/st4/profile_images/2024/5/9/RwqTbeYx16gauXPXvt-CaysOnGw.webp"
},
"name": "FediVision Vote Bot",
"outbox": "https://fedi.vision/@vote@fedi.vision/outbox/",
"preferredUsername": "vote",
"published": "2024-05-09T09:04:04Z",
"summary": "<p>New in 2024, this is the bot that will count your #Fedivision vote! Accept no substitutes!</p><p>Send this account a toot in the form<br> vote ABCD EFGH IJKL<br>substituting the (up to) three codes for the songs you want to win. Punctuation ignored, case insensitive, order is unimportant. Only your latest toot counts, so change your vote with a new toot.</p>",
"url": "https://fedi.vision/@vote/"
}

View file

@ -0,0 +1,53 @@
{
"@context": [
"https://www.w3.org/ns/activitystreams",
{
"blurhash": "toot:blurhash",
"Emoji": "toot:Emoji",
"focalPoint": {
"@container": "@list",
"@id": "toot:focalPoint"
},
"Hashtag": "as:Hashtag",
"manuallyApprovesFollowers": "as:manuallyApprovesFollowers",
"sensitive": "as:sensitive",
"toot": "http://joinmastodon.org/ns#",
"votersCount": "toot:votersCount",
"featured": {
"@id": "toot:featured",
"@type": "@id"
}
},
"https://w3id.org/security/v1"
],
"id": "https://fedi.vision/@vote@fedi.vision/",
"type": "Person",
"toot:discoverable": true,
"inbox": "https://fedi.vision/@vote@fedi.vision/inbox/",
"publicKey": {
"id": "https://fedi.vision/@vote@fedi.vision/#main-key",
"owner": "https://fedi.vision/@vote@fedi.vision/",
"publicKeyPem": "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAj2f+uQtdoBO9X/u2Qso4\nxHYdfy8zB24m9Gg982/ts88DAMLxZUzX0JsBWT7coL0Ipf4NSbVaqS6nKrr2P8Qs\nf97wMhowyuYxK22BMPcbpfZkFj3tVT/JkDx2iujBJJ5ZBO5KRlupjDTqV4rOAY7F\n58ad0jK9PsJNJMsJ/b8+0t3Q/K+RqCGVmtK+iPSigOYoiKoquyRzHLTfP+mpOlDa\n3f+uyAbFya7CpcgBx1zz0PALWA+oh/zhZK4yT6719Esa8SDcoJ0ws70zMxWekq1A\n3ia88/Io6SY2qFNBpzzXGO3JK8OFRFtmPV8ZfAh5Pv6y52iuTJ21kxjAG7ZTP/fY\nBQIDAQAB\n-----END PUBLIC KEY-----\n"
},
"attachment": {
"type": "PropertyValue",
"value": "<a href=\"https://fedivision.party/vote\" rel=\"nofollow\"><span class=\"invisible\">https://</span>fedivision.party/vote</a>",
"name": "More details"
},
"endpoints": {
"sharedInbox": "https://fedi.vision/inbox/"
},
"followers": "https://fedi.vision/@vote@fedi.vision/followers/",
"following": "https://fedi.vision/@vote@fedi.vision/following/",
"icon": {
"type": "Image",
"mediaType": "image/webp",
"url": "https://eu-central-1.linodeobjects.com:443/st4/profile_images/2024/5/9/RwqTbeYx16gauXPXvt-CaysOnGw.webp"
},
"name": "FediVision Vote Bot",
"outbox": "https://fedi.vision/@vote@fedi.vision/outbox/",
"preferredUsername": "vote",
"published": "2024-05-09T09:04:04Z",
"summary": "<p>New in 2024, this is the bot that will count your #Fedivision vote! Accept no substitutes!</p><p>Send this account a toot in the form<br> vote ABCD EFGH IJKL<br>substituting the (up to) three codes for the songs you want to win. Punctuation ignored, case insensitive, order is unimportant. Only your latest toot counts, so change your vote with a new toot.</p>",
"url": "https://fedi.vision/@vote/"
}

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.AppTest do defmodule Mix.Tasks.Pleroma.AppTest do
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
setup_all do setup_all do
Mix.shell(Mix.Shell.Process) Mix.shell(Mix.Shell.Process)
@ -50,13 +50,13 @@ defmodule Mix.Tasks.Pleroma.AppTest do
defp assert_app(name, redirect, scopes) do defp assert_app(name, redirect, scopes) do
app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name) app = Repo.get_by(Pleroma.Web.OAuth.App, client_name: name)
assert_receive {:mix_shell, :info, [message]}, 1_000 assert_receive {:mix_shell, :info, [message]}, 5_000
assert message == "#{name} successfully created:" assert message == "#{name} successfully created:"
assert_receive {:mix_shell, :info, [message]}, 1_000 assert_receive {:mix_shell, :info, [message]}, 5_000
assert message == "App client_id: #{app.client_id}" assert message == "App client_id: #{app.client_id}"
assert_receive {:mix_shell, :info, [message]}, 1_000 assert_receive {:mix_shell, :info, [message]}, 5_000
assert message == "App client_secret: #{app.client_secret}" assert message == "App client_secret: #{app.client_secret}"
assert app.scopes == scopes assert app.scopes == scopes

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.DatabaseTest do defmodule Mix.Tasks.Pleroma.DatabaseTest do
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
use Oban.Testing, repo: Pleroma.Repo use Oban.Testing, repo: Pleroma.Repo
alias Pleroma.Activity alias Pleroma.Activity

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.Ecto.RollbackTest do defmodule Mix.Tasks.Pleroma.Ecto.RollbackTest do
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
import ExUnit.CaptureLog import ExUnit.CaptureLog
require Logger require Logger

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.EctoTest do defmodule Mix.Tasks.Pleroma.EctoTest do
use ExUnit.Case, async: true use ExUnit.Case, async: false
test "raise on bad path" do test "raise on bad path" do
assert_raise RuntimeError, ~r/Could not find migrations directory/, fn -> assert_raise RuntimeError, ~r/Could not find migrations directory/, fn ->

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Mix.Tasks.Pleroma.EmojiTest do defmodule Mix.Tasks.Pleroma.EmojiTest do
use ExUnit.Case, async: true use ExUnit.Case, async: false
import ExUnit.CaptureIO import ExUnit.CaptureIO
import Tesla.Mock import Tesla.Mock

View file

@ -41,6 +41,26 @@ defmodule Pleroma.ActivityTest do
assert activity == found_activity assert activity == found_activity
end end
test "returns activities by object's AP id in requested presorted order" do
a1 = insert(:note_activity)
o1 = Object.normalize(a1, fetch: false).data["id"]
a2 = insert(:note_activity)
o2 = Object.normalize(a2, fetch: false).data["id"]
a3 = insert(:note_activity)
o3 = Object.normalize(a3, fetch: false).data["id"]
a4 = insert(:note_activity)
o4 = Object.normalize(a4, fetch: false).data["id"]
found_activities =
Activity.get_presorted_create_by_object_ap_id([o3, o2, o4, o1])
|> Repo.all()
assert found_activities == [a3, a2, a4, a1]
end
test "preloading a bookmark" do test "preloading a bookmark" do
user = insert(:user) user = insert(:user)
user2 = insert(:user) user2 = insert(:user)

View file

@ -4,7 +4,7 @@
defmodule Pleroma.Emoji.FormatterTest do defmodule Pleroma.Emoji.FormatterTest do
alias Pleroma.Emoji.Formatter alias Pleroma.Emoji.Formatter
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
describe "emojify" do describe "emojify" do
test "it adds cool emoji" do test "it adds cool emoji" do

View file

@ -4,7 +4,7 @@ defmodule Pleroma.HTTP.BackoffTest do
alias Pleroma.HTTP.Backoff alias Pleroma.HTTP.Backoff
defp within_tolerance?(ttl, expected) do defp within_tolerance?(ttl, expected) do
ttl > expected - 10 and ttl < expected + 10 ttl > expected - 15 and ttl < expected + 15
end end
describe "get/3" do describe "get/3" do

View file

@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-only # SPDX-License-Identifier: AGPL-3.0-only
defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do defmodule Pleroma.Upload.Filter.Exiftool.ReadDescriptionTest do
use Pleroma.DataCase, async: true use Pleroma.DataCase, async: false
alias Pleroma.Upload.Filter alias Pleroma.Upload.Filter
@uploads %Pleroma.Upload{ @uploads %Pleroma.Upload{

View file

@ -233,6 +233,48 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
} }
end end
test "works for takahe actors" do
user_id = "https://fedi.vision/@vote@fedi.vision/"
Tesla.Mock.mock(fn
%{method: :get, url: ^user_id} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/users_mock/takahe_user.json"),
headers: [{"content-type", "application/activity+json"}]
}
end)
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert user.actor_type == "Person"
assert [
%{
"name" => "More details"
}
] = user.fields
end
test "works for actors with malformed attachment fields" do
user_id = "https://fedi.vision/@vote@fedi.vision/"
Tesla.Mock.mock(fn
%{method: :get, url: ^user_id} ->
%Tesla.Env{
status: 200,
body: File.read!("test/fixtures/users_mock/nonsense_attachment_user.json"),
headers: [{"content-type", "application/activity+json"}]
}
end)
{:ok, user} = ActivityPub.make_user_from_ap_id(user_id)
assert user.actor_type == "Person"
assert [] = user.fields
end
test "fetches user featured collection" do test "fetches user featured collection" do
ap_id = "https://example.com/users/lain" ap_id = "https://example.com/users/lain"
@ -283,9 +325,7 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
body: featured_data, body: featured_data,
headers: [{"content-type", "application/activity+json"}] headers: [{"content-type", "application/activity+json"}]
} }
end)
Tesla.Mock.mock_global(fn
%{ %{
method: :get, method: :get,
url: ^object_url url: ^object_url
@ -298,7 +338,8 @@ defmodule Pleroma.Web.ActivityPub.ActivityPubTest do
end) end)
{:ok, user} = ActivityPub.make_user_from_ap_id(ap_id) {:ok, user} = ActivityPub.make_user_from_ap_id(ap_id)
Process.sleep(50) # wait for oban
Pleroma.Tests.ObanHelpers.perform_all()
assert user.featured_address == featured_url assert user.featured_address == featured_url
assert Map.has_key?(user.pinned_objects, object_url) assert Map.has_key?(user.pinned_objects, object_url)