Merge branch 'fork' into backend-new
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
commit
d580f81f27
130 changed files with 1885 additions and 458 deletions
4
.github/workflows/pl.yaml
vendored
4
.github/workflows/pl.yaml
vendored
|
@ -1,4 +1,4 @@
|
|||
# Adapter from https://fly.io/phoenix-files/github-actions-for-elixir-ci/
|
||||
# Adapted from https://fly.io/phoenix-files/github-actions-for-elixir-ci/
|
||||
|
||||
name: pl CI
|
||||
|
||||
|
@ -20,7 +20,7 @@ jobs:
|
|||
test:
|
||||
services:
|
||||
db:
|
||||
image: postgres:12
|
||||
image: postgres:16
|
||||
ports: ['5432:5432']
|
||||
env:
|
||||
POSTGRES_DB: pleroma_test
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25
|
||||
image: git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25
|
||||
|
||||
variables: &global_variables
|
||||
# Only used for the release
|
||||
ELIXIR_VER: 1.13.4
|
||||
ELIXIR_VER: 1.14.5
|
||||
POSTGRES_DB: pleroma_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
|
@ -71,7 +71,7 @@ check-changelog:
|
|||
tags:
|
||||
- amd64
|
||||
|
||||
build-1.13.4-otp-25:
|
||||
build-1.14.5-otp-25:
|
||||
extends:
|
||||
- .build_changes_policy
|
||||
- .using-ci-base
|
||||
|
@ -119,7 +119,7 @@ benchmark:
|
|||
- mix ecto.migrate
|
||||
- mix pleroma.load_testing
|
||||
|
||||
unit-testing-1.13.4-otp-25:
|
||||
unit-testing-1.14.5-otp-25:
|
||||
extends:
|
||||
- .build_changes_policy
|
||||
- .using-ci-base
|
||||
|
@ -134,7 +134,7 @@ unit-testing-1.13.4-otp-25:
|
|||
script: &testing_script
|
||||
- mix ecto.create
|
||||
- mix ecto.migrate
|
||||
- mix test --cover --preload-modules
|
||||
- mix pleroma.test_runner --cover --preload-modules
|
||||
coverage: '/^Line total: ([^ ]*%)$/'
|
||||
artifacts:
|
||||
reports:
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
# https://hub.docker.com/r/hexpm/elixir/tags
|
||||
ARG ELIXIR_IMG=hexpm/elixir
|
||||
ARG ELIXIR_VER=1.13.4
|
||||
ARG ERLANG_VER=24.3.4.15
|
||||
ARG ALPINE_VER=3.17.5
|
||||
ARG ELIXIR_VER=1.14.5
|
||||
ARG ERLANG_VER=25.3.2.14
|
||||
ARG ALPINE_VER=3.17.9
|
||||
|
||||
FROM ${ELIXIR_IMG}:${ELIXIR_VER}-erlang-${ERLANG_VER}-alpine-${ALPINE_VER} as build
|
||||
|
||||
|
|
10
README.md
10
README.md
|
@ -15,18 +15,22 @@ Added features:
|
|||
- [Partial implementation of Mastodon admin API](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3671)
|
||||
- [Moderators can assign users to reports](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3670)
|
||||
- [Mastodon-compatible webhooks](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3683)
|
||||
- UI is restyled to match pl-fe/Soapbox visuals
|
||||
- UI is restyled to match pl-fe visuals
|
||||
- Improved compatibility with akkoma-fe (profiles saving is still missing)
|
||||
|
||||
Features not authored by me:
|
||||
- [Chat deletion](https://git.pleroma.social/pleroma/pleroma/-/merge_requests/3029)
|
||||
- [AntiDuplicationPolicy, AntiMentionSpamPolicy](https://gitlab.com/soapbox-pub/rebased/-/merge_requests/249), [RemoteReportPolicy](https://gitlab.com/soapbox-pub/rebased/-/merge_requests/202) MRFs
|
||||
- [AntiDuplicationPolicy and AntiMentionSpamPolicy MRFs](https://gitlab.com/soapbox-pub/rebased/-/merge_requests/249)
|
||||
- [Bubble timeline](https://akkoma.dev/AkkomaGang/akkoma/pulls/100)
|
||||
- [Hashtag following](https://akkoma.dev/AkkomaGang/akkoma/pulls/341)
|
||||
- [Ability to auto-approve followbacks](https://akkoma.dev/AkkomaGang/akkoma/pulls/674)
|
||||
|
||||
There might be more, it's hard to keep track of it. I'm trying to keep the fork as close to upstream and I hope the list will eventually get much shorter.
|
||||
|
||||
It should be possible to migrate from Pleroma or Rebased to `pl` without issues. It is recommended to use `pl` with [`pl-fe`](https://github.com/mkljczk/pl-fe/tree/develop/packages/pl-fe) for full feature compatibility, but pleroma-fe and other frontends work fine.
|
||||
**DISCLAIMER:**
|
||||
Although `pl` *just works* for me, I cannot guarantee that it'll work well for you. There might be bugs I simply don't care about or I might decide to abandon the project one day.
|
||||
|
||||
It should be possible to migrate from Pleroma or Rebased to `pl` without issues. It is recommended to use `pl` with [`pl-fe`](https://github.com/mkljczk/pl-fe/tree/develop/packages/pl-fe) for full feature compatibility, but pleroma-fe and other frontends work fine too.
|
||||
|
||||
---
|
||||
|
||||
|
|
1
changelog.d/admin-report-notification-type.change
Normal file
1
changelog.d/admin-report-notification-type.change
Normal file
|
@ -0,0 +1 @@
|
|||
Use `admin.report` for report notification type
|
1
changelog.d/akkoma-migration.add
Normal file
1
changelog.d/akkoma-migration.add
Normal file
|
@ -0,0 +1 @@
|
|||
Add instructions for migrating from Akkoma
|
1
changelog.d/argon2-passwords.add
Normal file
1
changelog.d/argon2-passwords.add
Normal file
|
@ -0,0 +1 @@
|
|||
Added support for argon2 passwords and their conversion for migration from Akkoma fork to upstream.
|
1
changelog.d/atom-tag.change
Normal file
1
changelog.d/atom-tag.change
Normal file
|
@ -0,0 +1 @@
|
|||
Metadata: Do not include .atom feed links for remote accounts
|
1
changelog.d/elixir.change
Normal file
1
changelog.d/elixir.change
Normal file
|
@ -0,0 +1 @@
|
|||
Elixir 1.14 and Erlang/OTP 23 is now the minimum supported release
|
1
changelog.d/hashtag-feeds-restricted.add
Normal file
1
changelog.d/hashtag-feeds-restricted.add
Normal file
|
@ -0,0 +1 @@
|
|||
Repesct :restrict_unauthenticated for hashtag rss/atom feeds
|
1
changelog.d/incoming-blocks.fix
Normal file
1
changelog.d/incoming-blocks.fix
Normal file
|
@ -0,0 +1 @@
|
|||
Fix incoming Block activities being rejected
|
1
changelog.d/ldap-ca.add
Normal file
1
changelog.d/ldap-ca.add
Normal file
|
@ -0,0 +1 @@
|
|||
LDAP configuration now permits overriding the CA root certificate file for TLS validation.
|
1
changelog.d/ldap-password-change.add
Normal file
1
changelog.d/ldap-password-change.add
Normal file
|
@ -0,0 +1 @@
|
|||
LDAP now supports users changing their passwords
|
1
changelog.d/ldap-refactor.change
Normal file
1
changelog.d/ldap-refactor.change
Normal file
|
@ -0,0 +1 @@
|
|||
LDAP authentication has been refactored to operate as a GenServer process which will maintain an active connection to the LDAP server.
|
1
changelog.d/ldap-tls.fix
Normal file
1
changelog.d/ldap-tls.fix
Normal file
|
@ -0,0 +1 @@
|
|||
STARTTLS certificate and hostname verification for LDAP authentication
|
0
changelog.d/ldap-warning.skip
Normal file
0
changelog.d/ldap-warning.skip
Normal file
1
changelog.d/ldaps.fix
Normal file
1
changelog.d/ldaps.fix
Normal file
|
@ -0,0 +1 @@
|
|||
LDAPS connections (implicit TLS) are now supported.
|
0
changelog.d/manifest-icon-size.skip
Normal file
0
changelog.d/manifest-icon-size.skip
Normal file
1
changelog.d/mrf-id_filter.add
Normal file
1
changelog.d/mrf-id_filter.add
Normal file
|
@ -0,0 +1 @@
|
|||
Add `id_filter` to MRF to filter URLs and their domain prior to fetching
|
1
changelog.d/notifications-group-key.add
Normal file
1
changelog.d/notifications-group-key.add
Normal file
|
@ -0,0 +1 @@
|
|||
Add `group_key` to notifications
|
1
changelog.d/oban-update.change
Normal file
1
changelog.d/oban-update.change
Normal file
|
@ -0,0 +1 @@
|
|||
Oban updated to 2.18.3
|
1
changelog.d/poll-refresh.change
Normal file
1
changelog.d/poll-refresh.change
Normal file
|
@ -0,0 +1 @@
|
|||
Poll results refreshing is handled asynchronously and will not attempt to keep fetching updates to a closed poll.
|
0
changelog.d/profile-image-descriptions.skip
Normal file
0
changelog.d/profile-image-descriptions.skip
Normal file
1
changelog.d/remote-report-policy.add
Normal file
1
changelog.d/remote-report-policy.add
Normal file
|
@ -0,0 +1 @@
|
|||
Added RemoteReportPolicy from Rebased for handling bogus federated reports
|
1
changelog.d/swoosh-mua.add
Normal file
1
changelog.d/swoosh-mua.add
Normal file
|
@ -0,0 +1 @@
|
|||
Added dependencies for Swoosh's Mua mail adapter
|
|
@ -1 +0,0 @@
|
|||
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.12 --push .
|
|
@ -1,8 +0,0 @@
|
|||
FROM elixir:1.13.4-otp-25
|
||||
|
||||
# Single RUN statement, otherwise intermediate images are created
|
||||
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
|
||||
RUN apt-get update &&\
|
||||
apt-get install -y libmagic-dev cmake libimage-exiftool-perl ffmpeg &&\
|
||||
mix local.hex --force &&\
|
||||
mix local.rebar --force
|
|
@ -1,4 +1,4 @@
|
|||
FROM elixir:1.12.3
|
||||
FROM elixir:1.14.5-otp-25
|
||||
|
||||
# Single RUN statement, otherwise intermediate images are created
|
||||
# https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#run
|
|
@ -1 +1 @@
|
|||
docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.13.4-otp-25 --push .
|
||||
docker buildx build --platform linux/amd64,linux/arm64 -t git.pleroma.social:5050/pleroma/pleroma/ci-base:elixir-1.14.5-otp-25 --push .
|
|
@ -351,7 +351,7 @@
|
|||
icons: [
|
||||
%{
|
||||
src: "/static/logo.svg",
|
||||
sizes: "144x144",
|
||||
sizes: "512x512",
|
||||
purpose: "any",
|
||||
type: "image/svg+xml"
|
||||
}
|
||||
|
@ -625,14 +625,17 @@
|
|||
|
||||
config :pleroma, :ldap,
|
||||
enabled: System.get_env("LDAP_ENABLED") == "true",
|
||||
host: System.get_env("LDAP_HOST") || "localhost",
|
||||
port: String.to_integer(System.get_env("LDAP_PORT") || "389"),
|
||||
host: System.get_env("LDAP_HOST", "localhost"),
|
||||
port: String.to_integer(System.get_env("LDAP_PORT", "389")),
|
||||
ssl: System.get_env("LDAP_SSL") == "true",
|
||||
sslopts: [],
|
||||
tls: System.get_env("LDAP_TLS") == "true",
|
||||
tlsopts: [],
|
||||
base: System.get_env("LDAP_BASE") || "dc=example,dc=com",
|
||||
uid: System.get_env("LDAP_UID") || "cn"
|
||||
base: System.get_env("LDAP_BASE", "dc=example,dc=com"),
|
||||
uid: System.get_env("LDAP_UID", "cn"),
|
||||
# defaults to CAStore's Mozilla roots
|
||||
cacertfile: System.get_env("LDAP_CACERTFILE", nil),
|
||||
mail: System.get_env("LDAP_MAIL", "mail")
|
||||
|
||||
oauth_consumer_strategies =
|
||||
System.get_env("OAUTH_CONSUMER_STRATEGIES")
|
||||
|
@ -830,6 +833,21 @@
|
|||
"https://lily-is.land/infra/glitch-lily/-/jobs/artifacts/${ref}/download?job=build",
|
||||
"ref" => "servant",
|
||||
"build_dir" => "public"
|
||||
},
|
||||
"akkoma-fe" => %{
|
||||
"name" => "akkoma-fe",
|
||||
"git" => "https://akkoma.dev/AkkomaGang/akkoma-fe",
|
||||
"build_url" =>
|
||||
"https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/akkoma-fe.zip",
|
||||
"ref" => "develop",
|
||||
"build_dir" => "dist"
|
||||
},
|
||||
"akkoma-admin-fe" => %{
|
||||
"name" => "akkoma-admin-fe",
|
||||
"git" => "https://akkoma.dev/AkkomaGang/admin-fe",
|
||||
"build_url" =>
|
||||
"https://akkoma-updates.s3-website.fr-par.scw.cloud/frontend/${ref}/admin-fe.zip",
|
||||
"ref" => "stable"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -963,8 +981,6 @@
|
|||
|
||||
config :geospatial, Geospatial.HTTP, user_agent: &Pleroma.Application.user_agent/0
|
||||
|
||||
import_config "soapbox.exs"
|
||||
|
||||
config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch
|
||||
|
||||
config :pleroma, Pleroma.Search.Meilisearch,
|
||||
|
@ -994,6 +1010,8 @@
|
|||
vectors: %{size: 384, distance: "Cosine"}
|
||||
}
|
||||
|
||||
import_config "pl-fe.exs"
|
||||
|
||||
# Import environment specific config. This must remain at the bottom
|
||||
# of this file so it overrides the configuration defined above.
|
||||
import_config "#{Mix.env()}.exs"
|
||||
|
|
|
@ -2305,14 +2305,8 @@
|
|||
label: "SSL options",
|
||||
type: :keyword,
|
||||
description: "Additional SSL options",
|
||||
suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
|
||||
suggestions: [verify: :verify_peer],
|
||||
children: [
|
||||
%{
|
||||
key: :cacertfile,
|
||||
type: :string,
|
||||
description: "Path to file with PEM encoded cacerts",
|
||||
suggestions: ["path/to/file/with/PEM/cacerts"]
|
||||
},
|
||||
%{
|
||||
key: :verify,
|
||||
type: :atom,
|
||||
|
@ -2332,14 +2326,8 @@
|
|||
label: "TLS options",
|
||||
type: :keyword,
|
||||
description: "Additional TLS options",
|
||||
suggestions: [cacertfile: "path/to/file/with/PEM/cacerts", verify: :verify_peer],
|
||||
suggestions: [verify: :verify_peer],
|
||||
children: [
|
||||
%{
|
||||
key: :cacertfile,
|
||||
type: :string,
|
||||
description: "Path to file with PEM encoded cacerts",
|
||||
suggestions: ["path/to/file/with/PEM/cacerts"]
|
||||
},
|
||||
%{
|
||||
key: :verify,
|
||||
type: :atom,
|
||||
|
@ -2356,11 +2344,25 @@
|
|||
},
|
||||
%{
|
||||
key: :uid,
|
||||
label: "UID",
|
||||
label: "UID Attribute",
|
||||
type: :string,
|
||||
description:
|
||||
"LDAP attribute name to authenticate the user, e.g. when \"cn\", the filter will be \"cn=username,base\"",
|
||||
suggestions: ["cn"]
|
||||
},
|
||||
%{
|
||||
key: :cacertfile,
|
||||
label: "CACertfile",
|
||||
type: :string,
|
||||
description: "Path to CA certificate file"
|
||||
},
|
||||
%{
|
||||
key: :mail,
|
||||
label: "Mail Attribute",
|
||||
type: :string,
|
||||
description:
|
||||
"LDAP attribute name to use as the email address when automatically registering the user on first login",
|
||||
suggestions: ["mail"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3583,6 +3585,19 @@
|
|||
"translateLocally intermediate language (used when direct source->target model is not available)",
|
||||
type: :string,
|
||||
suggestions: ["en"]
|
||||
},
|
||||
%{
|
||||
group: {:subgroup, Pleroma.Language.Translation.Mozhi},
|
||||
key: :base_url,
|
||||
label: "Mozhi instance URL",
|
||||
type: :string
|
||||
},
|
||||
%{
|
||||
group: {:subgroup, Pleroma.Language.Translation.Mozhi},
|
||||
key: :engine,
|
||||
label: "Engine used for Mozhi",
|
||||
type: :string,
|
||||
suggestions: ["libretranslate"]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
# Soapbox default config overrides
|
||||
# pl-fe default config overrides
|
||||
# This file gets loaded after config.exs
|
||||
# and before prod.secret.exs
|
||||
import Config
|
||||
|
||||
# Twitter-like block behavior
|
||||
config :pleroma, :activitypub, blockers_visible: false
|
||||
|
||||
# Sane default upload filters
|
||||
config :pleroma, Pleroma.Upload,
|
||||
filters: [
|
||||
|
@ -45,12 +42,7 @@
|
|||
# Sane default media attachment limit
|
||||
config :pleroma, :instance, max_media_attachments: 20
|
||||
|
||||
# Use Soapbox branding
|
||||
config :pleroma, :instance,
|
||||
name: "Soapbox",
|
||||
description: "Social media owned by you",
|
||||
instance_thumbnail: "/instance/thumbnail.png",
|
||||
favicon: "/favicon.svg",
|
||||
account_approval_required: true,
|
||||
moderator_privileges: [
|
||||
:users_read,
|
|
@ -742,6 +742,21 @@ config :pleroma, Pleroma.Emails.Mailer,
|
|||
auth: :always
|
||||
```
|
||||
|
||||
An example for Mua adapter:
|
||||
|
||||
```elixir
|
||||
config :pleroma, Pleroma.Emails.Mailer,
|
||||
enabled: true,
|
||||
adapter: Swoosh.Adapters.Mua,
|
||||
relay: "mail.example.com",
|
||||
port: 465,
|
||||
auth: [
|
||||
username: "YOUR_USERNAME@domain.tld",
|
||||
password: "YOUR_SMTP_PASSWORD"
|
||||
],
|
||||
protocol: :ssl
|
||||
```
|
||||
|
||||
### :email_notifications
|
||||
|
||||
Email notifications settings.
|
||||
|
@ -968,12 +983,13 @@ Pleroma account will be created with the same name as the LDAP user name.
|
|||
* `enabled`: enables LDAP authentication
|
||||
* `host`: LDAP server hostname
|
||||
* `port`: LDAP port, e.g. 389 or 636
|
||||
* `ssl`: true to use SSL, usually implies the port 636
|
||||
* `ssl`: true to use implicit SSL/TLS, usually port 636
|
||||
* `sslopts`: additional SSL options
|
||||
* `tls`: true to start TLS, usually implies the port 389
|
||||
* `tls`: true to use explicit TLS (STARTTLS), usually port 389
|
||||
* `tlsopts`: additional TLS options
|
||||
* `base`: LDAP base, e.g. "dc=example,dc=com"
|
||||
* `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base"
|
||||
* `cacertfile`: Path to alternate CA root certificates file
|
||||
|
||||
Note, if your LDAP server is an Active Directory server the correct value is commonly `uid: "cn"`, but if you use an
|
||||
OpenLDAP server the value may be `uid: "uid"`.
|
||||
|
|
|
@ -238,21 +238,12 @@ The `type` value is `pleroma:chat_mention`
|
|||
- `account`: The account who sent the message
|
||||
- `chat_message`: The chat message
|
||||
|
||||
### Report Notification (not default)
|
||||
|
||||
This notification has to be requested explicitly.
|
||||
|
||||
The `type` value is `pleroma:report`
|
||||
|
||||
- `account`: The account who reported
|
||||
- `report`: The report
|
||||
|
||||
## GET `/api/v1/notifications`
|
||||
|
||||
Accepts additional parameters:
|
||||
|
||||
- `exclude_visibilities`: will exclude the notifications for activities with the given visibilities. The parameter accepts an array of visibility types (`public`, `unlisted`, `private`, `direct`). Usage example: `GET /api/v1/notifications?exclude_visibilities[]=direct&exclude_visibilities[]=private`.
|
||||
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:chat_mention`, `pleroma:report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
|
||||
- `include_types`: will include the notifications for activities with the given types. The parameter accepts an array of types (`mention`, `follow`, `reblog`, `favourite`, `move`, `pleroma:emoji_reaction`, `pleroma:chat_mention`, `admin.report`). Usage example: `GET /api/v1/notifications?include_types[]=mention&include_types[]=reblog`.
|
||||
|
||||
## DELETE `/api/v1/notifications/destroy_multiple`
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Note: This article is potentially outdated because at this time we may not have
|
|||
|
||||
- PostgreSQL 11.0以上 (Ubuntu16.04では9.5しか提供されていないので,[](https://www.postgresql.org/download/linux/ubuntu/)こちらから新しいバージョンを入手してください)
|
||||
- `postgresql-contrib` 11.0以上 (同上)
|
||||
- Elixir 1.13 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
|
||||
- Elixir 1.14 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like)。または [asdf](https://github.com/asdf-vm/asdf) をpleromaユーザーでインストールしてください)
|
||||
- `erlang-dev`
|
||||
- `erlang-nox`
|
||||
- `git`
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
## Required dependencies
|
||||
|
||||
* PostgreSQL >=11.0
|
||||
* Elixir >=1.13.0 <1.17
|
||||
* Erlang OTP >=22.2.0 (supported: <27)
|
||||
* Elixir >=1.14.0 <1.17
|
||||
* Erlang OTP >=23.0.0 (supported: <27)
|
||||
* git
|
||||
* file / libmagic
|
||||
* gcc or clang
|
||||
|
|
17
docs/installation/migrating_from_akkoma.md
Normal file
17
docs/installation/migrating_from_akkoma.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# Migrating from Akkoma
|
||||
|
||||
## Database migration
|
||||
|
||||
> Note: You will lose data related about Akkoma-specific features, including: MastoFE settings, user frontend profiles, status auto-expiration config and DM restrictions. Consider taking a backup.
|
||||
|
||||
To rollback Akkoma-specific migrations:
|
||||
|
||||
- OTP: `./bin/pleroma_ctl rollback --migrations-path priv/repo/optional_migrations/akkoma_rollbacks`
|
||||
- From Source: `mix ecto.rollback --migrations-path priv/repo/optional_migrations/akkoma_rollbacks`
|
||||
|
||||
Then, just
|
||||
|
||||
- OTP: `./bin/pleroma_ctl migrate`
|
||||
- From Source: `mix ecto.migrate`
|
||||
|
||||
to apply Pleroma database migrations.
|
7
installation/openldap/pw_self_service.ldif
Normal file
7
installation/openldap/pw_self_service.ldif
Normal file
|
@ -0,0 +1,7 @@
|
|||
dn: olcDatabase={1}mdb,cn=config
|
||||
changetype: modify
|
||||
add: olcAccess
|
||||
olcAccess: {1}to attrs=userPassword
|
||||
by self write
|
||||
by anonymous auth
|
||||
by * none
|
25
lib/mix/tasks/pleroma/test_runner.ex
Normal file
25
lib/mix/tasks/pleroma/test_runner.ex
Normal file
|
@ -0,0 +1,25 @@
|
|||
defmodule Mix.Tasks.Pleroma.TestRunner do
|
||||
@shortdoc "Retries tests once if they fail"
|
||||
|
||||
use Mix.Task
|
||||
|
||||
def run(args \\ []) do
|
||||
case System.cmd("mix", ["test"] ++ args, into: IO.stream(:stdio, :line)) do
|
||||
{_, 0} ->
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
retry(args)
|
||||
end
|
||||
end
|
||||
|
||||
def retry(args) do
|
||||
case System.cmd("mix", ["test", "--failed"] ++ args, into: IO.stream(:stdio, :line)) do
|
||||
{_, 0} ->
|
||||
:ok
|
||||
|
||||
_ ->
|
||||
exit(1)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -97,6 +97,7 @@ def start(_type, _args) do
|
|||
children =
|
||||
[
|
||||
Pleroma.PromEx,
|
||||
Pleroma.LDAP,
|
||||
Pleroma.Repo,
|
||||
Config.TransferTask,
|
||||
Pleroma.Emoji,
|
||||
|
|
|
@ -21,7 +21,8 @@ defp reboot_time_keys,
|
|||
{:pleroma, :markup},
|
||||
{:pleroma, :streamer},
|
||||
{:pleroma, :pools},
|
||||
{:pleroma, :connections_pool}
|
||||
{:pleroma, :connections_pool},
|
||||
{:pleroma, :ldap}
|
||||
]
|
||||
|
||||
defp reboot_time_subkeys,
|
||||
|
|
|
@ -103,6 +103,7 @@ defmodule Pleroma.Constants do
|
|||
|
||||
const(activity_types,
|
||||
do: [
|
||||
"Block",
|
||||
"Create",
|
||||
"Update",
|
||||
"Delete",
|
||||
|
@ -131,6 +132,10 @@ defmodule Pleroma.Constants do
|
|||
]
|
||||
)
|
||||
|
||||
const(object_types,
|
||||
do: ~w[Event Question Answer Audio Video Image Article Note Page ChatMessage]
|
||||
)
|
||||
|
||||
# basic regex, just there to weed out potential mistakes
|
||||
# https://datatracker.ietf.org/doc/html/rfc2045#section-5.1
|
||||
const(mime_regex,
|
||||
|
|
|
@ -10,9 +10,9 @@ defmodule Pleroma.Hashtag do
|
|||
|
||||
alias Ecto.Multi
|
||||
alias Pleroma.Hashtag
|
||||
alias Pleroma.User.HashtagFollow
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User.HashtagFollow
|
||||
|
||||
schema "hashtags" do
|
||||
field(:name, :string)
|
||||
|
|
|
@ -30,7 +30,7 @@ def translate(content, source_language, target_language) do
|
|||
text: content,
|
||||
source_lang: source_language |> String.upcase(),
|
||||
target_lang: target_language,
|
||||
tag_handling: "html"
|
||||
tag_handling: @name
|
||||
}),
|
||||
"",
|
||||
[
|
||||
|
|
|
@ -46,7 +46,7 @@ def translate(content, source_language, target_language) do
|
|||
%{
|
||||
content: content,
|
||||
detected_source_language: source_language,
|
||||
provider: "LibreTranslate"
|
||||
provider: @name
|
||||
}}
|
||||
|
||||
_ ->
|
||||
|
|
109
lib/pleroma/language/translation/mozhi.ex
Normal file
109
lib/pleroma/language/translation/mozhi.ex
Normal file
|
@ -0,0 +1,109 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Language.Translation.Mozhi do
|
||||
import Pleroma.Web.Utils.Guards, only: [not_empty_string: 1]
|
||||
|
||||
alias Pleroma.Language.Translation.Provider
|
||||
|
||||
use Provider
|
||||
|
||||
@behaviour Provider
|
||||
|
||||
@name "Mozhi"
|
||||
|
||||
@impl Provider
|
||||
def configured?, do: not_empty_string(base_url()) and not_empty_string(engine())
|
||||
|
||||
@impl Provider
|
||||
def translate(content, source_language, target_language) do
|
||||
endpoint =
|
||||
base_url()
|
||||
|> URI.merge("/api/translate")
|
||||
|> URI.to_string()
|
||||
|
||||
case Pleroma.HTTP.get(
|
||||
endpoint <>
|
||||
"?" <>
|
||||
URI.encode_query(%{
|
||||
engine: engine(),
|
||||
text: content,
|
||||
from: source_language,
|
||||
to: target_language
|
||||
}),
|
||||
[{"Accept", "application/json"}]
|
||||
) do
|
||||
{:ok, %{status: 200} = res} ->
|
||||
%{
|
||||
"translated-text" => content,
|
||||
"source_language" => source_language
|
||||
} = Jason.decode!(res.body)
|
||||
|
||||
{:ok,
|
||||
%{
|
||||
content: content,
|
||||
detected_source_language: source_language,
|
||||
provider: @name
|
||||
}}
|
||||
|
||||
_ ->
|
||||
{:error, :internal_server_error}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def supported_languages(type) when type in [:source, :target] do
|
||||
path =
|
||||
case type do
|
||||
:source -> "/api/source_languages"
|
||||
:target -> "/api/target_languages"
|
||||
end
|
||||
|
||||
endpoint =
|
||||
base_url()
|
||||
|> URI.merge(path)
|
||||
|> URI.to_string()
|
||||
|
||||
case Pleroma.HTTP.get(
|
||||
endpoint <>
|
||||
"?" <>
|
||||
URI.encode_query(%{
|
||||
engine: engine()
|
||||
}),
|
||||
[{"Accept", "application/json"}]
|
||||
) do
|
||||
{:ok, %{status: 200} = res} ->
|
||||
languages =
|
||||
Jason.decode!(res.body)
|
||||
|> Enum.map(fn %{"Id" => language} -> language end)
|
||||
|
||||
{:ok, languages}
|
||||
|
||||
_ ->
|
||||
{:error, :internal_server_error}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def languages_matrix do
|
||||
with {:ok, source_languages} <- supported_languages(:source),
|
||||
{:ok, target_languages} <- supported_languages(:target) do
|
||||
{:ok,
|
||||
Map.new(source_languages, fn language -> {language, target_languages -- [language]} end)}
|
||||
else
|
||||
{:error, error} -> {:error, error}
|
||||
end
|
||||
end
|
||||
|
||||
@impl Provider
|
||||
def name, do: @name
|
||||
|
||||
defp base_url do
|
||||
Pleroma.Config.get([__MODULE__, :base_url])
|
||||
end
|
||||
|
||||
defp engine do
|
||||
Pleroma.Config.get([__MODULE__, :engine])
|
||||
end
|
||||
end
|
271
lib/pleroma/ldap.ex
Normal file
271
lib/pleroma/ldap.ex
Normal file
|
@ -0,0 +1,271 @@
|
|||
defmodule Pleroma.LDAP do
|
||||
use GenServer
|
||||
|
||||
require Logger
|
||||
|
||||
alias Pleroma.Config
|
||||
alias Pleroma.User
|
||||
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_user: 1]
|
||||
|
||||
@connection_timeout 2_000
|
||||
@search_timeout 2_000
|
||||
|
||||
def start_link(_) do
|
||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||
end
|
||||
|
||||
def bind_user(name, password) do
|
||||
GenServer.call(__MODULE__, {:bind_user, name, password})
|
||||
end
|
||||
|
||||
def change_password(name, password, new_password) do
|
||||
GenServer.call(__MODULE__, {:change_password, name, password, new_password})
|
||||
end
|
||||
|
||||
@impl true
|
||||
def init(state) do
|
||||
case {Config.get(Pleroma.Web.Auth.Authenticator), Config.get([:ldap, :enabled])} do
|
||||
{Pleroma.Web.Auth.LDAPAuthenticator, true} ->
|
||||
{:ok, state, {:continue, :connect}}
|
||||
|
||||
{Pleroma.Web.Auth.LDAPAuthenticator, false} ->
|
||||
Logger.error(
|
||||
"LDAP Authenticator enabled but :pleroma, :ldap is not enabled. Auth will not work."
|
||||
)
|
||||
|
||||
{:ok, state}
|
||||
|
||||
{_, true} ->
|
||||
Logger.warning(
|
||||
":pleroma, :ldap is enabled but Pleroma.Web.Authenticator is not set to the LDAPAuthenticator. LDAP will not be used."
|
||||
)
|
||||
|
||||
{:ok, state}
|
||||
|
||||
_ ->
|
||||
{:ok, state}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_continue(:connect, _state), do: do_handle_connect()
|
||||
|
||||
@impl true
|
||||
def handle_info(:connect, _state), do: do_handle_connect()
|
||||
|
||||
def handle_info({:bind_after_reconnect, name, password, from}, state) do
|
||||
result = do_bind_user(state[:handle], name, password)
|
||||
|
||||
GenServer.reply(from, result)
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def handle_call({:bind_user, name, password}, from, state) do
|
||||
case do_bind_user(state[:handle], name, password) do
|
||||
:needs_reconnect ->
|
||||
Process.send(self(), {:bind_after_reconnect, name, password, from}, [])
|
||||
{:noreply, state, {:continue, :connect}}
|
||||
|
||||
result ->
|
||||
{:reply, result, state, :hibernate}
|
||||
end
|
||||
end
|
||||
|
||||
def handle_call({:change_password, name, password, new_password}, _from, state) do
|
||||
result = change_password(state[:handle], name, password, new_password)
|
||||
|
||||
{:reply, result, state, :hibernate}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def terminate(_, state) do
|
||||
handle = Keyword.get(state, :handle)
|
||||
|
||||
if not is_nil(handle) do
|
||||
:eldap.close(handle)
|
||||
end
|
||||
|
||||
:ok
|
||||
end
|
||||
|
||||
defp do_handle_connect do
|
||||
state =
|
||||
case connect() do
|
||||
{:ok, handle} ->
|
||||
:eldap.controlling_process(handle, self())
|
||||
Process.link(handle)
|
||||
[handle: handle]
|
||||
|
||||
_ ->
|
||||
Logger.error("Failed to connect to LDAP. Retrying in 5000ms")
|
||||
Process.send_after(self(), :connect, 5_000)
|
||||
[]
|
||||
end
|
||||
|
||||
{:noreply, state}
|
||||
end
|
||||
|
||||
defp connect do
|
||||
ldap = Config.get(:ldap, [])
|
||||
host = Keyword.get(ldap, :host, "localhost")
|
||||
port = Keyword.get(ldap, :port, 389)
|
||||
ssl = Keyword.get(ldap, :ssl, false)
|
||||
tls = Keyword.get(ldap, :tls, false)
|
||||
cacertfile = Keyword.get(ldap, :cacertfile) || CAStore.file_path()
|
||||
|
||||
if ssl, do: Application.ensure_all_started(:ssl)
|
||||
|
||||
default_secure_opts = [
|
||||
verify: :verify_peer,
|
||||
cacerts: decode_certfile(cacertfile),
|
||||
customize_hostname_check: [
|
||||
fqdn_fun: fn _ -> to_charlist(host) end
|
||||
]
|
||||
]
|
||||
|
||||
sslopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :sslopts, []))
|
||||
tlsopts = Keyword.merge(default_secure_opts, Keyword.get(ldap, :tlsopts, []))
|
||||
|
||||
default_options = [{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}]
|
||||
|
||||
# :sslopts can only be included in :eldap.open/2 when {ssl: true}
|
||||
# or the connection will fail
|
||||
options =
|
||||
if ssl do
|
||||
default_options ++ [{:sslopts, sslopts}]
|
||||
else
|
||||
default_options
|
||||
end
|
||||
|
||||
case :eldap.open([to_charlist(host)], options) do
|
||||
{:ok, handle} ->
|
||||
try do
|
||||
cond do
|
||||
tls ->
|
||||
case :eldap.start_tls(
|
||||
handle,
|
||||
tlsopts,
|
||||
@connection_timeout
|
||||
) do
|
||||
:ok ->
|
||||
{:ok, handle}
|
||||
|
||||
error ->
|
||||
Logger.error("Could not start TLS: #{inspect(error)}")
|
||||
:eldap.close(handle)
|
||||
end
|
||||
|
||||
true ->
|
||||
{:ok, handle}
|
||||
end
|
||||
after
|
||||
:ok
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Could not open LDAP connection: #{inspect(error)}")
|
||||
{:error, {:ldap_connection_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp do_bind_user(handle, name, password) do
|
||||
dn = make_dn(name)
|
||||
|
||||
case :eldap.simple_bind(handle, dn, password) do
|
||||
:ok ->
|
||||
case fetch_user(name) do
|
||||
%User{} = user ->
|
||||
user
|
||||
|
||||
_ ->
|
||||
register_user(handle, ldap_base(), ldap_uid(), name)
|
||||
end
|
||||
|
||||
# eldap does not inform us of socket closure
|
||||
# until it is used
|
||||
{:error, {:gen_tcp_error, :closed}} ->
|
||||
:eldap.close(handle)
|
||||
:needs_reconnect
|
||||
|
||||
{:error, error} = e ->
|
||||
Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}")
|
||||
e
|
||||
end
|
||||
end
|
||||
|
||||
defp register_user(handle, base, uid, name) do
|
||||
case :eldap.search(handle, [
|
||||
{:base, to_charlist(base)},
|
||||
{:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))},
|
||||
{:scope, :eldap.wholeSubtree()},
|
||||
{:timeout, @search_timeout}
|
||||
]) do
|
||||
# The :eldap_search_result record structure changed in OTP 24.3 and added a controls field
|
||||
# https://github.com/erlang/otp/pull/5538
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
error ->
|
||||
Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}")
|
||||
{:error, {:ldap_search_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp try_register(name, attributes) do
|
||||
mail_attribute = Config.get([:ldap, :mail])
|
||||
|
||||
params = %{
|
||||
name: name,
|
||||
nickname: name,
|
||||
password: nil
|
||||
}
|
||||
|
||||
params =
|
||||
case List.keyfind(attributes, to_charlist(mail_attribute), 0) do
|
||||
{_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail))
|
||||
_ -> params
|
||||
end
|
||||
|
||||
changeset = User.register_changeset_ldap(%User{}, params)
|
||||
|
||||
case User.register(changeset) do
|
||||
{:ok, user} -> user
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
|
||||
defp change_password(handle, name, password, new_password) do
|
||||
dn = make_dn(name)
|
||||
|
||||
with :ok <- :eldap.simple_bind(handle, dn, password) do
|
||||
:eldap.modify_password(handle, dn, to_charlist(new_password), to_charlist(password))
|
||||
end
|
||||
end
|
||||
|
||||
defp decode_certfile(file) do
|
||||
with {:ok, data} <- File.read(file) do
|
||||
data
|
||||
|> :public_key.pem_decode()
|
||||
|> Enum.map(fn {_, b, _} -> b end)
|
||||
else
|
||||
_ ->
|
||||
Logger.error("Unable to read certfile: #{file}")
|
||||
[]
|
||||
end
|
||||
end
|
||||
|
||||
defp ldap_uid, do: to_charlist(Config.get([:ldap, :uid], "cn"))
|
||||
defp ldap_base, do: to_charlist(Config.get([:ldap, :base]))
|
||||
|
||||
defp make_dn(name) do
|
||||
uid = ldap_uid()
|
||||
base = ldap_base()
|
||||
~c"#{uid}=#{name},#{base}"
|
||||
end
|
||||
end
|
|
@ -70,7 +70,7 @@ def unread_notifications_count(%User{id: user_id}) do
|
|||
move
|
||||
pleroma:chat_mention
|
||||
pleroma:emoji_reaction
|
||||
pleroma:report
|
||||
admin.report
|
||||
reblog
|
||||
poll
|
||||
status
|
||||
|
@ -445,7 +445,7 @@ defp type_from_activity(%{data: %{"type" => type}} = activity) do
|
|||
"pleroma:emoji_reaction"
|
||||
|
||||
"Flag" ->
|
||||
"pleroma:report"
|
||||
"admin.report"
|
||||
|
||||
# Compatibility with old reactions
|
||||
"EmojiReaction" ->
|
||||
|
|
|
@ -99,27 +99,6 @@ defp hashtags_changed?(_, _), do: false
|
|||
def get_by_id(nil), do: nil
|
||||
def get_by_id(id), do: Repo.get(Object, id)
|
||||
|
||||
@spec get_by_id_and_maybe_refetch(integer(), list()) :: Object.t() | nil
|
||||
def get_by_id_and_maybe_refetch(id, opts \\ []) do
|
||||
with %Object{updated_at: updated_at} = object <- get_by_id(id) do
|
||||
if opts[:interval] &&
|
||||
NaiveDateTime.diff(NaiveDateTime.utc_now(), updated_at) > opts[:interval] do
|
||||
case Fetcher.refetch_object(object) do
|
||||
{:ok, %Object{} = object} ->
|
||||
object
|
||||
|
||||
e ->
|
||||
Logger.error("Couldn't refresh #{object.data["id"]}:\n#{inspect(e)}")
|
||||
object
|
||||
end
|
||||
else
|
||||
object
|
||||
end
|
||||
else
|
||||
nil -> nil
|
||||
end
|
||||
end
|
||||
|
||||
def get_by_ap_id(nil), do: nil
|
||||
|
||||
def get_by_ap_id(ap_id) do
|
||||
|
|
|
@ -146,6 +146,7 @@ def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
|
|||
Logger.debug("Fetching object #{id} via AP")
|
||||
|
||||
with {:scheme, true} <- {:scheme, String.starts_with?(id, "http")},
|
||||
{_, true} <- {:mrf, MRF.id_filter(id)},
|
||||
{:ok, body} <- get_object(id),
|
||||
{:ok, data} <- safe_json_decode(body),
|
||||
:ok <- Containment.contain_origin_from_id(id, data) do
|
||||
|
@ -161,6 +162,9 @@ def fetch_and_contain_remote_object_from_id(id) when is_binary(id) do
|
|||
{:error, e} ->
|
||||
{:error, e}
|
||||
|
||||
{:mrf, false} ->
|
||||
{:error, {:reject, "Filtered by id"}}
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
|
|
|
@ -21,7 +21,6 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.FollowingRelationship
|
||||
alias Pleroma.Formatter
|
||||
alias Pleroma.Hashtag
|
||||
alias Pleroma.User.HashtagFollow
|
||||
alias Pleroma.HTML
|
||||
alias Pleroma.Keys
|
||||
alias Pleroma.MFA
|
||||
|
@ -31,6 +30,7 @@ defmodule Pleroma.User do
|
|||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.UserRelationship
|
||||
alias Pleroma.User.HashtagFollow
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.ActivityPub.Builder
|
||||
alias Pleroma.Web.ActivityPub.Pipeline
|
||||
|
@ -440,6 +440,11 @@ def banner_url(user, options \\ []) do
|
|||
end
|
||||
end
|
||||
|
||||
def image_description(image, default \\ "")
|
||||
|
||||
def image_description(%{"name" => name}, _default), do: name
|
||||
def image_description(_, default), do: default
|
||||
|
||||
# Should probably be renamed or removed
|
||||
@spec ap_id(User.t()) :: String.t()
|
||||
def ap_id(%User{nickname: nickname}), do: "#{Endpoint.url()}/users/#{nickname}"
|
||||
|
@ -1167,7 +1172,7 @@ def needs_update?(_), do: true
|
|||
# "Locked" (self-locked) users demand explicit authorization of follow requests
|
||||
@spec can_direct_follow_local(User.t(), User.t()) :: true | false
|
||||
def can_direct_follow_local(%User{} = follower, %User{local: true} = followed) do
|
||||
!followed.is_locked || (followed.permit_followback and is_friend_of(follower, followed))
|
||||
!followed.is_locked || (followed.permit_followback and friend_of?(follower, followed))
|
||||
end
|
||||
|
||||
@spec maybe_direct_follow(User.t(), User.t()) ::
|
||||
|
@ -1552,7 +1557,7 @@ def get_familiar_followers(%User{} = user, %User{} = current_user, page \\ nil)
|
|||
|> Repo.all()
|
||||
end
|
||||
|
||||
def is_friend_of(%User{} = potential_friend, %User{local: true} = user) do
|
||||
def friend_of?(%User{} = potential_friend, %User{local: true} = user) do
|
||||
user
|
||||
|> get_friends_query()
|
||||
|> where(id: ^potential_friend.id)
|
||||
|
|
|
@ -3,9 +3,9 @@ defmodule Pleroma.User.HashtagFollow do
|
|||
import Ecto.Query
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Hashtag
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
schema "user_follows_hashtag" do
|
||||
belongs_to(:user, User, type: FlakeId.Ecto.CompatType)
|
||||
|
|
|
@ -1717,16 +1717,23 @@ defp get_actor_url(url) when is_list(url) do
|
|||
|
||||
defp get_actor_url(_url), do: nil
|
||||
|
||||
defp normalize_image(%{"url" => url}) do
|
||||
defp normalize_image(%{"url" => url} = data) do
|
||||
%{
|
||||
"type" => "Image",
|
||||
"url" => [%{"href" => url}]
|
||||
}
|
||||
|> maybe_put_description(data)
|
||||
end
|
||||
|
||||
defp normalize_image(urls) when is_list(urls), do: urls |> List.first() |> normalize_image()
|
||||
defp normalize_image(_), do: nil
|
||||
|
||||
defp maybe_put_description(map, %{"name" => description}) when is_binary(description) do
|
||||
Map.put(map, "name", description)
|
||||
end
|
||||
|
||||
defp maybe_put_description(map, _), do: map
|
||||
|
||||
defp object_to_user_data(data, additional) do
|
||||
fields =
|
||||
data
|
||||
|
|
|
@ -109,6 +109,14 @@ def filter(policies, %{} = message) do
|
|||
|
||||
def filter(%{} = object), do: get_policies() |> filter(object)
|
||||
|
||||
def id_filter(policies, id) when is_binary(id) do
|
||||
policies
|
||||
|> Enum.filter(&function_exported?(&1, :id_filter, 1))
|
||||
|> Enum.all?(& &1.id_filter(id))
|
||||
end
|
||||
|
||||
def id_filter(id) when is_binary(id), do: get_policies() |> id_filter(id)
|
||||
|
||||
@impl true
|
||||
def pipeline_filter(%{} = message, meta) do
|
||||
object = meta[:object_data]
|
||||
|
|
|
@ -13,6 +13,12 @@ def filter(activity) do
|
|||
{:reject, activity}
|
||||
end
|
||||
|
||||
@impl true
|
||||
def id_filter(id) do
|
||||
Logger.debug("REJECTING #{id}")
|
||||
false
|
||||
end
|
||||
|
||||
@impl true
|
||||
def describe, do: {:ok, %{}}
|
||||
end
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.Policy do
|
||||
@callback filter(Pleroma.Activity.t()) :: {:ok | :reject, Pleroma.Activity.t()}
|
||||
@callback id_filter(String.t()) :: boolean()
|
||||
@callback describe() :: {:ok | :error, map()}
|
||||
@callback config_description() :: %{
|
||||
optional(:children) => [map()],
|
||||
|
@ -13,5 +14,5 @@ defmodule Pleroma.Web.ActivityPub.MRF.Policy do
|
|||
description: String.t()
|
||||
}
|
||||
@callback history_awareness() :: :auto | :manual
|
||||
@optional_callbacks config_description: 0, history_awareness: 0
|
||||
@optional_callbacks config_description: 0, history_awareness: 0, id_filter: 1
|
||||
end
|
||||
|
|
|
@ -9,6 +9,7 @@ def filter(%{"type" => "Flag"} = object) do
|
|||
with {_, false} <- {:local, local?(object)},
|
||||
{:ok, _} <- maybe_reject_all(object),
|
||||
{:ok, _} <- maybe_reject_anonymous(object),
|
||||
{:ok, _} <- maybe_reject_third_party(object),
|
||||
{:ok, _} <- maybe_reject_empty_message(object) do
|
||||
{:ok, object}
|
||||
else
|
||||
|
@ -37,6 +38,22 @@ defp maybe_reject_anonymous(%{"actor" => actor} = object) do
|
|||
end
|
||||
end
|
||||
|
||||
defp maybe_reject_third_party(%{"object" => objects} = object) do
|
||||
{_, to} =
|
||||
case objects do
|
||||
[head | tail] when is_binary(head) -> {tail, head}
|
||||
s when is_binary(s) -> {[], s}
|
||||
_ -> {[], ""}
|
||||
end
|
||||
|
||||
with true <- Config.get([:mrf_remote_report, :reject_third_party]),
|
||||
false <- String.starts_with?(to, Pleroma.Web.Endpoint.url()) do
|
||||
{:reject, "[RemoteReportPolicy] Third-party: #{to}"}
|
||||
else
|
||||
_ -> {:ok, object}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_reject_empty_message(%{"content" => content} = object)
|
||||
when is_binary(content) and content != "" do
|
||||
{:ok, object}
|
||||
|
@ -83,6 +100,12 @@ def config_description do
|
|||
description: "Reject anonymous remote reports?",
|
||||
suggestions: [true]
|
||||
},
|
||||
%{
|
||||
key: :reject_third_party,
|
||||
type: :boolean,
|
||||
description: "Reject reports on users from third-party instances?",
|
||||
suggestions: [true]
|
||||
},
|
||||
%{
|
||||
key: :reject_empty_message,
|
||||
type: :boolean,
|
||||
|
|
|
@ -191,6 +191,18 @@ defp instance_list(config_key) do
|
|||
|> MRF.instance_list_from_tuples()
|
||||
end
|
||||
|
||||
@impl true
|
||||
def id_filter(id) do
|
||||
host_info = URI.parse(id)
|
||||
|
||||
with {:ok, _} <- check_accept(host_info, %{}),
|
||||
{:ok, _} <- check_reject(host_info, %{}) do
|
||||
true
|
||||
else
|
||||
_ -> false
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def filter(%{"type" => "Delete", "actor" => actor} = activity) do
|
||||
%{host: actor_host} = URI.parse(actor)
|
||||
|
|
|
@ -11,6 +11,8 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
|
||||
@behaviour Pleroma.Web.ActivityPub.ObjectValidator.Validating
|
||||
|
||||
import Pleroma.Constants, only: [activity_types: 0, object_types: 0]
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.EctoType.ActivityPub.ObjectValidators
|
||||
alias Pleroma.Object
|
||||
|
@ -42,6 +44,16 @@ defmodule Pleroma.Web.ActivityPub.ObjectValidator do
|
|||
@impl true
|
||||
def validate(object, meta)
|
||||
|
||||
# This overload works together with the InboxGuardPlug
|
||||
# and ensures that we are not accepting any activity type
|
||||
# that cannot pass InboxGuardPlug.
|
||||
# If we want to support any more activity types, make sure to
|
||||
# add it in Pleroma.Constants's activity_types or object_types,
|
||||
# and, if applicable, allowed_activity_types_from_strangers.
|
||||
def validate(%{"type" => type}, _meta)
|
||||
when type not in activity_types() and type not in object_types(),
|
||||
do: {:error, :not_allowed_object_type}
|
||||
|
||||
def validate(%{"type" => "Block"} = block_activity, meta) do
|
||||
with {:ok, block_activity} <-
|
||||
block_activity
|
||||
|
|
|
@ -1021,6 +1021,7 @@ defp replace_instance_host(value, nil), do: value
|
|||
defp replace_instance_host(content, host) when is_binary(content) do
|
||||
content
|
||||
|> String.replace("$INSTANCE$host$", host)
|
||||
|> String.replace("$INSTANCE$tld$", get_tld(host, "$INSTANCE$tld$"))
|
||||
end
|
||||
|
||||
defp replace_instance_host(object, host) when is_map(object) do
|
||||
|
@ -1032,6 +1033,14 @@ defp replace_instance_host(object, host) when is_map(object) do
|
|||
|
||||
defp replace_instance_host(value, _), do: value
|
||||
|
||||
defp get_tld(host, default) do
|
||||
with [domain | _] <- String.split(host, ".") |> Enum.reverse() do
|
||||
domain
|
||||
else
|
||||
_ -> default
|
||||
end
|
||||
end
|
||||
|
||||
defp patch_content_map(%{"contentMap" => %{} = content_map}, host) do
|
||||
content_map
|
||||
|> Enum.map(fn {key, value} -> {key, replace_instance_host(value, host)} end)
|
||||
|
|
|
@ -132,8 +132,22 @@ def render("user.json", %{user: user}) do
|
|||
"webfinger" => "acct:#{User.full_nickname(user)}",
|
||||
"vcard:Address" => user.location
|
||||
}
|
||||
|> Map.merge(maybe_make_image(&User.avatar_url/2, "icon", user))
|
||||
|> Map.merge(maybe_make_image(&User.banner_url/2, "image", user))
|
||||
|> Map.merge(
|
||||
maybe_make_image(
|
||||
&User.avatar_url/2,
|
||||
User.image_description(user.avatar, nil),
|
||||
"icon",
|
||||
user
|
||||
)
|
||||
)
|
||||
|> Map.merge(
|
||||
maybe_make_image(
|
||||
&User.banner_url/2,
|
||||
User.image_description(user.banner, nil),
|
||||
"image",
|
||||
user
|
||||
)
|
||||
)
|
||||
|> Map.merge(Utils.make_json_ld_header())
|
||||
end
|
||||
|
||||
|
@ -314,16 +328,24 @@ def collection(collection, iri, page, show_items \\ true, total \\ nil) do
|
|||
end
|
||||
end
|
||||
|
||||
defp maybe_make_image(func, key, user) do
|
||||
defp maybe_make_image(func, description, key, user) do
|
||||
if image = func.(user, no_default: true) do
|
||||
%{
|
||||
key => %{
|
||||
"type" => "Image",
|
||||
"url" => image
|
||||
}
|
||||
key =>
|
||||
%{
|
||||
"type" => "Image",
|
||||
"url" => image
|
||||
}
|
||||
|> maybe_put_description(description)
|
||||
}
|
||||
else
|
||||
%{}
|
||||
end
|
||||
end
|
||||
|
||||
defp maybe_put_description(map, description) when is_binary(description) do
|
||||
Map.put(map, "name", description)
|
||||
end
|
||||
|
||||
defp maybe_put_description(map, _description), do: map
|
||||
end
|
||||
|
|
100
lib/pleroma/web/akkoma_compat_controller.ex
Normal file
100
lib/pleroma/web/akkoma_compat_controller.ex
Normal file
|
@ -0,0 +1,100 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.AkkomaCompatController do
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Language.Translation
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
|
||||
plug(:skip_auth when action == :translation_languages)
|
||||
|
||||
plug(
|
||||
OAuthScopesPlug,
|
||||
%{fallback: :proceed_unauthenticated, scopes: ["read:statuses"]} when action == :translate
|
||||
)
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate)
|
||||
|
||||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.AkkomaCompatOperation
|
||||
|
||||
@doc "GET /api/v1/akkoma/translation/languages"
|
||||
def translation_languages(conn, _params) do
|
||||
with {:enabled, true} <- {:enabled, Translation.configured?()},
|
||||
{:ok, source_languages} <- Translation.supported_languages(:source),
|
||||
{:ok, target_languages} <- Translation.supported_languages(:target) do
|
||||
source_languages =
|
||||
source_languages
|
||||
|> Enum.map(fn lang -> %{code: lang, name: lang} end)
|
||||
|
||||
target_languages =
|
||||
target_languages
|
||||
|> Enum.map(fn lang -> %{code: lang, name: lang} end)
|
||||
|
||||
conn
|
||||
|> json(%{source: source_languages, target: target_languages})
|
||||
else
|
||||
{:enabled, false} ->
|
||||
json(conn, %{})
|
||||
|
||||
e ->
|
||||
{:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
@doc "GET /api/v1/statuses/:id/translations/:language"
|
||||
def translate(
|
||||
%{
|
||||
assigns: %{user: user},
|
||||
private: %{open_api_spex: %{params: %{id: status_id} = params}}
|
||||
} = conn,
|
||||
_
|
||||
) do
|
||||
with {:authentication, true} <-
|
||||
{:authentication,
|
||||
!is_nil(user) ||
|
||||
Pleroma.Config.get([Translation, :allow_unauthenticated])},
|
||||
%Activity{object: object} <- Activity.get_by_id_with_object(status_id),
|
||||
{:visibility, visibility} when visibility in ["public", "unlisted"] <-
|
||||
{:visibility, Visibility.get_visibility(object)},
|
||||
{:allow_remote, true} <-
|
||||
{:allow_remote,
|
||||
Object.local?(object) ||
|
||||
Pleroma.Config.get([Translation, :allow_remote])},
|
||||
{:language, language} when is_binary(language) <-
|
||||
{:language, Map.get(params, :language) || user.language},
|
||||
{:ok, result} <-
|
||||
Translation.translate(
|
||||
object.data["content"],
|
||||
object.data["language"],
|
||||
language
|
||||
) do
|
||||
json(conn, %{detected_language: result.detected_source_language, text: result.content})
|
||||
else
|
||||
{:authentication, false} ->
|
||||
render_error(conn, :unauthorized, "Authorization is required to translate statuses")
|
||||
|
||||
{:allow_remote, false} ->
|
||||
render_error(conn, :bad_request, "You can't translate remote posts")
|
||||
|
||||
{:language, nil} ->
|
||||
render_error(conn, :bad_request, "Language not specified")
|
||||
|
||||
{:visibility, _} ->
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
|
||||
{:error, :not_found} ->
|
||||
render_error(conn, :not_found, "Translation service not configured")
|
||||
|
||||
{:error, error} when error in [:unexpected_response, :quota_exceeded, :too_many_requests] ->
|
||||
render_error(conn, :service_unavailable, "Translation service not available")
|
||||
|
||||
nil ->
|
||||
render_error(conn, :not_found, "Record not found")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,91 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.ApiSpec.AkkomaCompatOperation do
|
||||
alias OpenApiSpex.Operation
|
||||
alias OpenApiSpex.Schema
|
||||
alias Pleroma.Web.ApiSpec.Schemas.FlakeID
|
||||
|
||||
def open_api_operation(action) do
|
||||
operation = String.to_existing_atom("#{action}_operation")
|
||||
apply(__MODULE__, operation, [])
|
||||
end
|
||||
|
||||
# Adapted from https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/lib/pleroma/web/api_spec/operations/translate_operation.ex
|
||||
def translation_languages_operation do
|
||||
%Operation{
|
||||
tags: ["Akkoma compatibility routes"],
|
||||
summary: "Get translation languages",
|
||||
description: "Retreieve a list of supported source and target language",
|
||||
operationId: "AkkomaCompatController.translation_languages",
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"Translation languages",
|
||||
"application/json",
|
||||
source_dest_languages_schema()
|
||||
)
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp source_dest_languages_schema do
|
||||
%Schema{
|
||||
type: :object,
|
||||
required: [:source, :target],
|
||||
properties: %{
|
||||
source: languages_schema(),
|
||||
target: languages_schema()
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
defp languages_schema do
|
||||
%Schema{
|
||||
type: :array,
|
||||
items: %Schema{
|
||||
type: :object,
|
||||
properties: %{
|
||||
code: %Schema{type: :string},
|
||||
name: %Schema{type: :string}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def translate_operation do
|
||||
%Operation{
|
||||
tags: ["Akkoma compatibility routes"],
|
||||
summary: "Translate status",
|
||||
description: "Translate status with an external API",
|
||||
operationId: "AkkomaCompatController.translate",
|
||||
security: [%{"oAuth" => ["read:statuses"]}],
|
||||
parameters: [
|
||||
Operation.parameter(:id, :path, FlakeID.schema(), "Status ID",
|
||||
example: "9umDrYheeY451cQnEe",
|
||||
required: true
|
||||
),
|
||||
Operation.parameter(:language, :path, :string, "Target language code", example: "en"),
|
||||
Operation.parameter(:from, :query, :string, "Source language code (unused)",
|
||||
example: "en"
|
||||
)
|
||||
],
|
||||
responses: %{
|
||||
200 =>
|
||||
Operation.response(
|
||||
"Translated status",
|
||||
"application/json",
|
||||
%Schema{
|
||||
type: :object,
|
||||
required: [:detected_language, :text],
|
||||
properties: %{
|
||||
detected_language: %Schema{type: :string},
|
||||
text: %Schema{type: :string}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
|
@ -157,6 +157,10 @@ def notification do
|
|||
type: :object,
|
||||
properties: %{
|
||||
id: %Schema{type: :string},
|
||||
group_key: %Schema{
|
||||
type: :string,
|
||||
description: "Group key shared by similar notifications"
|
||||
},
|
||||
type: notification_type(),
|
||||
created_at: %Schema{type: :string, format: :"date-time"},
|
||||
account: %Schema{
|
||||
|
@ -184,6 +188,7 @@ def notification do
|
|||
},
|
||||
example: %{
|
||||
"id" => "34975861",
|
||||
"group-key" => "ungrouped-34975861",
|
||||
"type" => "mention",
|
||||
"created_at" => "2019-11-23T07:49:02.064Z",
|
||||
"account" => Account.schema().example,
|
||||
|
@ -203,7 +208,6 @@ defp notification_type do
|
|||
"mention",
|
||||
"pleroma:emoji_reaction",
|
||||
"pleroma:chat_mention",
|
||||
"pleroma:report",
|
||||
"move",
|
||||
"follow_request",
|
||||
"poll",
|
||||
|
@ -229,7 +233,6 @@ defp notification_type do
|
|||
- `move` - Someone moved their account
|
||||
- `pleroma:emoji_reaction` - Someone reacted with emoji to your status
|
||||
- `pleroma:chat_mention` - Someone mentioned you in a chat message
|
||||
- `pleroma:report` - Someone was reported
|
||||
- `status` - Someone you are subscribed to created a status
|
||||
- `update` - A status you boosted has been edited
|
||||
- `pleroma:event_reminder` – An event you are participating in or created is taking place soon
|
||||
|
|
|
@ -10,4 +10,9 @@ defmodule Pleroma.Web.Auth.Authenticator do
|
|||
@callback handle_error(Plug.Conn.t(), any()) :: any()
|
||||
@callback auth_template() :: String.t() | nil
|
||||
@callback oauth_consumer_template() :: String.t() | nil
|
||||
|
||||
@callback change_password(Pleroma.User.t(), String.t(), String.t(), String.t()) ::
|
||||
{:ok, Pleroma.User.t()} | {:error, term()}
|
||||
|
||||
@optional_callbacks change_password: 4
|
||||
end
|
||||
|
|
|
@ -3,18 +3,14 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
||||
alias Pleroma.LDAP
|
||||
alias Pleroma.User
|
||||
|
||||
require Logger
|
||||
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1]
|
||||
|
||||
@behaviour Pleroma.Web.Auth.Authenticator
|
||||
@base Pleroma.Web.Auth.PleromaAuthenticator
|
||||
|
||||
@connection_timeout 10_000
|
||||
@search_timeout 10_000
|
||||
|
||||
defdelegate get_registration(conn), to: @base
|
||||
defdelegate create_from_registration(conn, registration), to: @base
|
||||
defdelegate handle_error(conn, error), to: @base
|
||||
|
@ -24,7 +20,7 @@ defmodule Pleroma.Web.Auth.LDAPAuthenticator do
|
|||
def get_user(%Plug.Conn{} = conn) do
|
||||
with {:ldap, true} <- {:ldap, Pleroma.Config.get([:ldap, :enabled])},
|
||||
{:ok, {name, password}} <- fetch_credentials(conn),
|
||||
%User{} = user <- ldap_user(name, password) do
|
||||
%User{} = user <- LDAP.bind_user(name, password) do
|
||||
{:ok, user}
|
||||
else
|
||||
{:ldap, _} ->
|
||||
|
@ -35,106 +31,12 @@ def get_user(%Plug.Conn{} = conn) do
|
|||
end
|
||||
end
|
||||
|
||||
defp ldap_user(name, password) do
|
||||
ldap = Pleroma.Config.get(:ldap, [])
|
||||
host = Keyword.get(ldap, :host, "localhost")
|
||||
port = Keyword.get(ldap, :port, 389)
|
||||
ssl = Keyword.get(ldap, :ssl, false)
|
||||
sslopts = Keyword.get(ldap, :sslopts, [])
|
||||
|
||||
options =
|
||||
[{:port, port}, {:ssl, ssl}, {:timeout, @connection_timeout}] ++
|
||||
if sslopts != [], do: [{:sslopts, sslopts}], else: []
|
||||
|
||||
case :eldap.open([to_charlist(host)], options) do
|
||||
{:ok, connection} ->
|
||||
try do
|
||||
if Keyword.get(ldap, :tls, false) do
|
||||
:application.ensure_all_started(:ssl)
|
||||
|
||||
case :eldap.start_tls(
|
||||
connection,
|
||||
Keyword.get(ldap, :tlsopts, []),
|
||||
@connection_timeout
|
||||
) do
|
||||
:ok ->
|
||||
:ok
|
||||
|
||||
error ->
|
||||
Logger.error("Could not start TLS: #{inspect(error)}")
|
||||
end
|
||||
end
|
||||
|
||||
bind_user(connection, ldap, name, password)
|
||||
after
|
||||
:eldap.close(connection)
|
||||
end
|
||||
|
||||
{:error, error} ->
|
||||
Logger.error("Could not open LDAP connection: #{inspect(error)}")
|
||||
{:error, {:ldap_connection_error, error}}
|
||||
def change_password(user, password, new_password, new_password) do
|
||||
case LDAP.change_password(user.nickname, password, new_password) do
|
||||
:ok -> {:ok, user}
|
||||
e -> e
|
||||
end
|
||||
end
|
||||
|
||||
defp bind_user(connection, ldap, name, password) do
|
||||
uid = Keyword.get(ldap, :uid, "cn")
|
||||
base = Keyword.get(ldap, :base)
|
||||
|
||||
case :eldap.simple_bind(connection, "#{uid}=#{name},#{base}", password) do
|
||||
:ok ->
|
||||
case fetch_user(name) do
|
||||
%User{} = user ->
|
||||
user
|
||||
|
||||
_ ->
|
||||
register_user(connection, base, uid, name)
|
||||
end
|
||||
|
||||
error ->
|
||||
Logger.error("Could not bind LDAP user #{name}: #{inspect(error)}")
|
||||
{:error, {:ldap_bind_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp register_user(connection, base, uid, name) do
|
||||
case :eldap.search(connection, [
|
||||
{:base, to_charlist(base)},
|
||||
{:filter, :eldap.equalityMatch(to_charlist(uid), to_charlist(name))},
|
||||
{:scope, :eldap.wholeSubtree()},
|
||||
{:timeout, @search_timeout}
|
||||
]) do
|
||||
# The :eldap_search_result record structure changed in OTP 24.3 and added a controls field
|
||||
# https://github.com/erlang/otp/pull/5538
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
{:ok, {:eldap_search_result, [{:eldap_entry, _object, attributes}], _referrals, _controls}} ->
|
||||
try_register(name, attributes)
|
||||
|
||||
error ->
|
||||
Logger.error("Couldn't register user because LDAP search failed: #{inspect(error)}")
|
||||
{:error, {:ldap_search_error, error}}
|
||||
end
|
||||
end
|
||||
|
||||
defp try_register(name, attributes) do
|
||||
params = %{
|
||||
name: name,
|
||||
nickname: name,
|
||||
password: nil
|
||||
}
|
||||
|
||||
params =
|
||||
case List.keyfind(attributes, ~c"mail", 0) do
|
||||
{_, [mail]} -> Map.put_new(params, :email, :erlang.list_to_binary(mail))
|
||||
_ -> params
|
||||
end
|
||||
|
||||
changeset = User.register_changeset_ldap(%User{}, params)
|
||||
|
||||
case User.register(changeset) do
|
||||
{:ok, user} -> user
|
||||
error -> error
|
||||
end
|
||||
end
|
||||
def change_password(_, _, _, _), do: {:error, :password_confirmation}
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ defmodule Pleroma.Web.Auth.PleromaAuthenticator do
|
|||
alias Pleroma.Registration
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.AuthenticationPlug
|
||||
|
||||
import Pleroma.Web.Auth.Helpers, only: [fetch_credentials: 1, fetch_user: 1]
|
||||
|
@ -101,4 +102,23 @@ def handle_error(%Plug.Conn{} = _conn, error) do
|
|||
def auth_template, do: nil
|
||||
|
||||
def oauth_consumer_template, do: nil
|
||||
|
||||
@doc "Changes Pleroma.User password in the database"
|
||||
def change_password(user, password, new_password, new_password) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, password) do
|
||||
{:ok, user} ->
|
||||
with {:ok, _user} <-
|
||||
User.reset_password(user, %{
|
||||
password: new_password,
|
||||
password_confirmation: new_password
|
||||
}) do
|
||||
{:ok, user}
|
||||
end
|
||||
|
||||
error ->
|
||||
error
|
||||
end
|
||||
end
|
||||
|
||||
def change_password(_, _, _, _), do: {:error, :password_confirmation}
|
||||
end
|
||||
|
|
|
@ -39,4 +39,8 @@ def oauth_consumer_template do
|
|||
implementation().oauth_consumer_template() ||
|
||||
Pleroma.Config.get([:auth, :oauth_consumer_template], "consumer.html")
|
||||
end
|
||||
|
||||
@impl true
|
||||
def change_password(user, password, new_password, new_password_confirmation),
|
||||
do: implementation().change_password(user, password, new_password, new_password_confirmation)
|
||||
end
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.Feed.TagController do
|
|||
alias Pleroma.Web.Feed.FeedView
|
||||
|
||||
def feed(conn, params) do
|
||||
if Config.get!([:instance, :public]) do
|
||||
if not Config.restrict_unauthenticated_access?(:timelines, :local) do
|
||||
render_feed(conn, params)
|
||||
else
|
||||
render_error(conn, :not_found, "Not found")
|
||||
|
@ -18,10 +18,12 @@ def feed(conn, params) do
|
|||
end
|
||||
|
||||
defp render_feed(conn, %{"tag" => raw_tag} = params) do
|
||||
local_only = Config.restrict_unauthenticated_access?(:timelines, :federated)
|
||||
|
||||
{format, tag} = parse_tag(raw_tag)
|
||||
|
||||
activities =
|
||||
%{type: ["Create"], tag: tag}
|
||||
%{type: ["Create"], tag: tag, local_only: local_only}
|
||||
|> Pleroma.Maps.put_if_present(:max_id, params["max_id"])
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
|
||||
|
|
|
@ -8,10 +8,10 @@ defmodule Pleroma.Web.MastodonAPI.FollowRequestController do
|
|||
import Pleroma.Web.ControllerHelper,
|
||||
only: [add_link_headers: 2]
|
||||
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Pagination
|
||||
|
||||
plug(Pleroma.Web.ApiSpec.CastAndValidate, replace_params: false)
|
||||
plug(:assign_follower when action != :index)
|
||||
|
|
|
@ -12,6 +12,7 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
|||
alias Pleroma.Web.ActivityPub.Visibility
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Workers.PollWorker
|
||||
|
||||
action_fallback(Pleroma.Web.MastodonAPI.FallbackController)
|
||||
|
||||
|
@ -27,12 +28,16 @@ defmodule Pleroma.Web.MastodonAPI.PollController do
|
|||
defdelegate open_api_operation(action), to: Pleroma.Web.ApiSpec.PollOperation
|
||||
|
||||
@cachex Pleroma.Config.get([:cachex, :provider], Cachex)
|
||||
@poll_refresh_interval 120
|
||||
|
||||
@doc "GET /api/v1/polls/:id"
|
||||
def show(%{assigns: %{user: user}, private: %{open_api_spex: %{params: %{id: id}}}} = conn, _) do
|
||||
with %Object{} = object <- Object.get_by_id_and_maybe_refetch(id, interval: 60),
|
||||
%Activity{} = activity <- Activity.get_create_by_object_ap_id(object.data["id"]),
|
||||
with %Object{} = object <- Object.get_by_id(id),
|
||||
%Activity{} = activity <-
|
||||
Activity.get_create_by_object_ap_id_with_object(object.data["id"]),
|
||||
true <- Visibility.visible_for_user?(activity, user) do
|
||||
maybe_refresh_poll(activity)
|
||||
|
||||
try_render(conn, "show.json", %{object: object, for: user})
|
||||
else
|
||||
error when is_nil(error) or error == false ->
|
||||
|
@ -70,4 +75,13 @@ defp get_cached_vote_or_vote(object, user, choices) do
|
|||
end
|
||||
end)
|
||||
end
|
||||
|
||||
defp maybe_refresh_poll(%Activity{object: %Object{} = object} = activity) do
|
||||
with false <- activity.local,
|
||||
{:ok, end_time} <- NaiveDateTime.from_iso8601(object.data["closed"]),
|
||||
{_, :lt} <- {:closed_compare, NaiveDateTime.compare(object.updated_at, end_time)} do
|
||||
PollWorker.new(%{"op" => "refresh", "activity_id" => activity.id})
|
||||
|> Oban.insert(unique: [period: @poll_refresh_interval])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,9 +2,9 @@ defmodule Pleroma.Web.MastodonAPI.TagController do
|
|||
@moduledoc "Hashtag routes for mastodon API"
|
||||
use Pleroma.Web, :controller
|
||||
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Hashtag
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.User
|
||||
|
||||
import Pleroma.Web.ControllerHelper,
|
||||
only: [
|
||||
|
|
|
@ -65,14 +65,14 @@ def get_notifications(user, params \\ %{}) do
|
|||
cast_params(params) |> Map.update(:include_types, [], fn include_types -> include_types end)
|
||||
|
||||
options =
|
||||
if ("pleroma:report" not in options.include_types and
|
||||
if ("admin.report" not in options.include_types and
|
||||
User.privileged?(user, :reports_manage_reports)) or
|
||||
User.privileged?(user, :reports_manage_reports) do
|
||||
options
|
||||
else
|
||||
options
|
||||
|> Map.update(:exclude_types, ["pleroma:report"], fn current_exclude_types ->
|
||||
current_exclude_types ++ ["pleroma:report"]
|
||||
|> Map.update(:exclude_types, ["admin.report"], fn current_exclude_types ->
|
||||
current_exclude_types ++ ["admin.report"]
|
||||
end)
|
||||
end
|
||||
|
||||
|
|
|
@ -219,10 +219,10 @@ defp do_render("show.json", %{user: user} = opts) do
|
|||
|
||||
avatar = User.avatar_url(user) |> MediaProxy.url()
|
||||
avatar_static = User.avatar_url(user) |> MediaProxy.preview_url(static: true)
|
||||
avatar_description = image_description(user.avatar)
|
||||
avatar_description = User.image_description(user.avatar)
|
||||
header = User.banner_url(user) |> MediaProxy.url()
|
||||
header_static = User.banner_url(user) |> MediaProxy.preview_url(static: true)
|
||||
header_description = image_description(user.banner)
|
||||
header_description = User.image_description(user.banner)
|
||||
|
||||
following_count =
|
||||
if !user.hide_follows_count or !user.hide_follows or self,
|
||||
|
@ -352,10 +352,6 @@ defp username_from_nickname(string) when is_binary(string) do
|
|||
|
||||
defp username_from_nickname(_), do: nil
|
||||
|
||||
defp image_description(%{"name" => name}), do: name
|
||||
|
||||
defp image_description(_), do: ""
|
||||
|
||||
defp maybe_put_follow_requests_count(
|
||||
data,
|
||||
%User{id: user_id} = user,
|
||||
|
|
|
@ -78,6 +78,11 @@ def render("show2.json", _) do
|
|||
email: Keyword.get(instance, :email),
|
||||
account: contact_account(Keyword.get(instance, :contact_username))
|
||||
},
|
||||
api_versions: %{
|
||||
"mastodon" => 2137,
|
||||
"social.pleroma" => 420,
|
||||
"pl.mkljczk.pl" => 69
|
||||
},
|
||||
# Extra (not present in Mastodon):
|
||||
pleroma: pleroma_configuration2(instance)
|
||||
# soapbox: %{
|
||||
|
@ -186,6 +191,13 @@ def features do
|
|||
"events",
|
||||
"multitenancy",
|
||||
"pleroma:bites",
|
||||
if !Enum.empty?(Config.get([:instance, :local_bubble], [])) do
|
||||
"bubble_timeline"
|
||||
end,
|
||||
# Akkoma compatibility
|
||||
if Pleroma.Language.Translation.configured?() do
|
||||
"akkoma:machine_translation"
|
||||
end,
|
||||
"pleroma:multi_language",
|
||||
"bigbuffet"
|
||||
]
|
||||
|
|
|
@ -95,6 +95,7 @@ def render(
|
|||
|
||||
response = %{
|
||||
id: to_string(notification.id),
|
||||
group_key: "ungrouped-" <> to_string(notification.id),
|
||||
type: notification.type,
|
||||
created_at: CommonAPI.Utils.to_masto_date(notification.inserted_at),
|
||||
account: account,
|
||||
|
@ -131,7 +132,7 @@ def render(
|
|||
"pleroma:chat_mention" ->
|
||||
put_chat_message(response, activity, reading_user, status_render_opts)
|
||||
|
||||
"pleroma:report" ->
|
||||
"admin.report" ->
|
||||
put_report(response, activity)
|
||||
|
||||
"pleroma:participation_accepted" ->
|
||||
|
|
|
@ -10,7 +10,7 @@ defmodule Pleroma.Web.Metadata.Providers.Feed do
|
|||
@behaviour Provider
|
||||
|
||||
@impl Provider
|
||||
def build_tags(%{user: user}) do
|
||||
def build_tags(%{user: %{local: true} = user}) do
|
||||
[
|
||||
{:link,
|
||||
[
|
||||
|
@ -20,4 +20,6 @@ def build_tags(%{user: user}) do
|
|||
], []}
|
||||
]
|
||||
end
|
||||
|
||||
def build_tags(_), do: []
|
||||
end
|
||||
|
|
|
@ -75,6 +75,7 @@ def get_nodeinfo("2.0") do
|
|||
restrictedNicknames: Config.get([Pleroma.User, :restricted_nicknames]),
|
||||
skipThreadContainment: Config.get([:instance, :skip_thread_containment], false),
|
||||
federatedTimelineAvailable: Config.get([:instance, :federated_timeline_available], true),
|
||||
localBubbleInstances: Config.get([:instance, :local_bubble], []),
|
||||
publicTimelineVisibility: %{
|
||||
federated:
|
||||
!Config.restrict_unauthenticated_access?(:timelines, :federated) &&
|
||||
|
|
|
@ -47,6 +47,11 @@ def checkpw(password, "$pbkdf2" <> _ = password_hash) do
|
|||
Pleroma.Password.Pbkdf2.verify_pass(password, password_hash)
|
||||
end
|
||||
|
||||
def checkpw(password, "$argon2" <> _ = password_hash) do
|
||||
# Handle argon2 passwords for Akkoma migration
|
||||
Argon2.verify_pass(password, password_hash)
|
||||
end
|
||||
|
||||
def checkpw(_password, _password_hash) do
|
||||
Logger.error("Password hash not recognized")
|
||||
false
|
||||
|
@ -56,6 +61,10 @@ def maybe_update_password(%User{password_hash: "$2" <> _} = user, password) do
|
|||
do_update_password(user, password)
|
||||
end
|
||||
|
||||
def maybe_update_password(%User{password_hash: "$argon2" <> _} = user, password) do
|
||||
do_update_password(user, password)
|
||||
end
|
||||
|
||||
def maybe_update_password(user, _), do: {:ok, user}
|
||||
|
||||
defp do_update_password(user, password) do
|
||||
|
|
|
@ -92,7 +92,7 @@ defp csp_string do
|
|||
static_url = Pleroma.Web.Endpoint.static_url()
|
||||
websocket_url = Pleroma.Web.Endpoint.websocket_url()
|
||||
report_uri = @config_impl.get([:http_security, :report_uri])
|
||||
sentry_dsn = @config_impl.get([:frontend_configurations, :soapbox_fe, "sentryDsn"])
|
||||
sentry_dsn = @config_impl.get([:frontend_configurations, :pl_fe, "sentryDsn"])
|
||||
|
||||
img_src = "img-src 'self' data: blob:"
|
||||
media_src = "media-src 'self'"
|
||||
|
@ -200,7 +200,7 @@ defp build_csp_multimedia_source_list do
|
|||
|
||||
defp map_tile_server do
|
||||
with tile_server when is_binary(tile_server) <-
|
||||
@config_impl.get([:frontend_configurations, :soapbox_fe, "tileServer"]),
|
||||
@config_impl.get([:frontend_configurations, :pl_fe, "tileServer"]),
|
||||
%{host: host} <- URI.parse(tile_server) do
|
||||
["*.#{host}"]
|
||||
else
|
||||
|
|
|
@ -731,6 +731,13 @@ defmodule Pleroma.Web.Router do
|
|||
end
|
||||
end
|
||||
|
||||
scope "/", Pleroma.Web do
|
||||
pipe_through(:api)
|
||||
|
||||
get("/api/v1/akkoma/translation/languages", AkkomaCompatController, :translation_languages)
|
||||
get("/api/v1/statuses/:id/translations/:language", AkkomaCompatController, :translate)
|
||||
end
|
||||
|
||||
scope "/api/v1", Pleroma.Web.MastodonAPI do
|
||||
pipe_through(:authenticated_api)
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
:root {
|
||||
<%= Pleroma.Web.Utils.Colors.shades_to_css(
|
||||
"primary",
|
||||
Pleroma.Config.get([:frontend_configurations, :soapbox_fe, "brandColor"], "#0482d8"),
|
||||
Pleroma.Config.get([:frontend_configurations, :soapbox_fe, "colors", "primary"], %{})
|
||||
Pleroma.Config.get([:frontend_configurations, :pl_fe, "brandColor"], "#d80482"),
|
||||
Pleroma.Config.get([:frontend_configurations, :pl_fe, "colors", "primary"], %{})
|
||||
) %>
|
||||
<%= Pleroma.Web.Utils.Colors.shades_to_css(
|
||||
"accent",
|
||||
Pleroma.Config.get([:frontend_configurations, :soapbox_fe, "accentColor"], "#2bd110"),
|
||||
Pleroma.Config.get([:frontend_configurations, :soapbox_fe, "colors", "accent"], %{})
|
||||
Pleroma.Config.get([:frontend_configurations, :pl_fe, "accentColor"], "#d110b4"),
|
||||
Pleroma.Config.get([:frontend_configurations, :pl_fe, "colors", "accent"], %{})
|
||||
) %>
|
||||
}
|
||||
</style>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<body>
|
||||
<div class="instance-header">
|
||||
<a class="instance-header__content" href="/">
|
||||
<img class="instance-header__thumbnail" src="<%= Pleroma.Config.get([:frontend_configurations, :soapbox_fe, "logo"], Pleroma.Config.get([:instance, :instance_thumbnail])) %>">
|
||||
<img class="instance-header__thumbnail" src="<%= Pleroma.Config.get([:frontend_configurations, :pl_fe, "logo"], Pleroma.Config.get([:instance, :instance_thumbnail])) %>">
|
||||
<h1 class="instance-header__title"><%= Pleroma.Config.get([:instance, :name]) %></h1>
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
@ -13,6 +13,7 @@ defmodule Pleroma.Web.TwitterAPI.UtilController do
|
|||
alias Pleroma.Healthcheck
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.Auth.WrapperAuthenticator, as: Authenticator
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Pleroma.Web.Plugs.OAuthScopesPlug
|
||||
alias Pleroma.Web.WebFinger
|
||||
|
@ -195,19 +196,21 @@ def change_password(
|
|||
%{assigns: %{user: user}, private: %{open_api_spex: %{body_params: body_params}}} = conn,
|
||||
_
|
||||
) do
|
||||
case CommonAPI.Utils.confirm_current_password(user, body_params.password) do
|
||||
{:ok, user} ->
|
||||
with {:ok, _user} <-
|
||||
User.reset_password(user, %{
|
||||
password: body_params.new_password,
|
||||
password_confirmation: body_params.new_password_confirmation
|
||||
}) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
{:error, changeset} ->
|
||||
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
||||
json(conn, %{error: "New password #{error}."})
|
||||
end
|
||||
with {:ok, %User{}} <-
|
||||
Authenticator.change_password(
|
||||
user,
|
||||
body_params.password,
|
||||
body_params.new_password,
|
||||
body_params.new_password_confirmation
|
||||
) do
|
||||
json(conn, %{status: "success"})
|
||||
else
|
||||
{:error, %Ecto.Changeset{} = changeset} ->
|
||||
{_, {error, _}} = Enum.at(changeset.errors, 0)
|
||||
json(conn, %{error: "New password #{error}."})
|
||||
|
||||
{:error, :password_confirmation} ->
|
||||
json(conn, %{error: "New password does not match confirmation."})
|
||||
|
||||
{:error, msg} ->
|
||||
json(conn, %{error: msg})
|
||||
|
|
|
@ -51,7 +51,7 @@ def get_shades("#" <> base_color, overrides) do
|
|||
shades
|
||||
end
|
||||
|
||||
def get_shades(_, overrides), do: get_shades("#0482d8", overrides)
|
||||
def get_shades(_, overrides), do: get_shades("#d80482", overrides)
|
||||
|
||||
defp get_override(level, overrides) do
|
||||
if Map.has_key?(overrides, "#{level}") do
|
||||
|
|
|
@ -12,9 +12,14 @@ def render("manifest.json", _params) do
|
|||
name: Config.get([:instance, :name]),
|
||||
description: Config.get([:instance, :description]),
|
||||
icons: Config.get([:manifest, :icons]),
|
||||
theme_color: Config.get([:manifest, :theme_color]),
|
||||
theme_color:
|
||||
Config.get(
|
||||
[:frontend_configurations, :pl_fe, "brandColor"],
|
||||
Config.get([:manifest, :theme_color])
|
||||
),
|
||||
background_color: Config.get([:manifest, :background_color]),
|
||||
display: "standalone",
|
||||
display_override: ["window-controls-overlay"],
|
||||
scope: Endpoint.url(),
|
||||
start_url: "/",
|
||||
categories: [
|
||||
|
|
|
@ -11,27 +11,46 @@ defmodule Pleroma.Workers.PollWorker do
|
|||
alias Pleroma.Activity
|
||||
alias Pleroma.Notification
|
||||
alias Pleroma.Object
|
||||
alias Pleroma.Object.Fetcher
|
||||
|
||||
@stream_out_impl Pleroma.Config.get(
|
||||
[__MODULE__, :stream_out],
|
||||
Pleroma.Web.ActivityPub.ActivityPub
|
||||
)
|
||||
|
||||
@impl true
|
||||
def perform(%Job{args: %{"op" => "poll_end", "activity_id" => activity_id}}) do
|
||||
with %Activity{} = activity <- find_poll_activity(activity_id),
|
||||
with {_, %Activity{} = activity} <- {:activity, Activity.get_by_id(activity_id)},
|
||||
{:ok, notifications} <- Notification.create_poll_notifications(activity) do
|
||||
unless activity.local do
|
||||
# Schedule a final refresh
|
||||
__MODULE__.new(%{"op" => "refresh", "activity_id" => activity_id})
|
||||
|> Oban.insert()
|
||||
end
|
||||
|
||||
Notification.stream(notifications)
|
||||
else
|
||||
{:error, :poll_activity_not_found} = e -> {:cancel, e}
|
||||
{:activity, nil} -> {:cancel, :poll_activity_not_found}
|
||||
e -> {:error, e}
|
||||
end
|
||||
end
|
||||
|
||||
def perform(%Job{args: %{"op" => "refresh", "activity_id" => activity_id}}) do
|
||||
with {_, %Activity{object: object}} <-
|
||||
{:activity, Activity.get_by_id_with_object(activity_id)},
|
||||
{_, {:ok, _object}} <- {:refetch, Fetcher.refetch_object(object)} do
|
||||
stream_update(activity_id)
|
||||
|
||||
:ok
|
||||
else
|
||||
{:activity, nil} -> {:cancel, :poll_activity_not_found}
|
||||
{:refetch, _} = e -> {:cancel, e}
|
||||
end
|
||||
end
|
||||
|
||||
@impl true
|
||||
def timeout(_job), do: :timer.seconds(5)
|
||||
|
||||
defp find_poll_activity(activity_id) do
|
||||
with nil <- Activity.get_by_id(activity_id) do
|
||||
{:error, :poll_activity_not_found}
|
||||
end
|
||||
end
|
||||
|
||||
def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} = activity) do
|
||||
with %Object{data: %{"type" => "Question", "closed" => closed}} when is_binary(closed) <-
|
||||
Object.normalize(activity),
|
||||
|
@ -49,4 +68,10 @@ def schedule_poll_end(%Activity{data: %{"type" => "Create"}, id: activity_id} =
|
|||
end
|
||||
|
||||
def schedule_poll_end(activity), do: {:error, activity}
|
||||
|
||||
defp stream_update(activity_id) do
|
||||
Activity.get_by_id(activity_id)
|
||||
|> Activity.normalize()
|
||||
|> @stream_out_impl.stream_out()
|
||||
end
|
||||
end
|
||||
|
|
7
mix.exs
7
mix.exs
|
@ -9,7 +9,7 @@ def project do
|
|||
name: "pl",
|
||||
compat_name: "Pleroma",
|
||||
version: version("2.7.0"),
|
||||
elixir: "~> 1.13",
|
||||
elixir: "~> 1.14",
|
||||
elixirc_paths: elixirc_paths(Mix.env()),
|
||||
compilers: Mix.compilers(),
|
||||
elixirc_options: [warnings_as_errors: warnings_as_errors(), prune_code_paths: false],
|
||||
|
@ -156,7 +156,7 @@ defp deps do
|
|||
{:cachex, "~> 3.2"},
|
||||
{:csv, "~> 2.4"},
|
||||
{:tesla, "~> 1.11"},
|
||||
{:castore, "~> 0.1"},
|
||||
{:castore, "~> 1.0"},
|
||||
{:cowlib, "~> 2.9", override: true},
|
||||
{:gun, "~> 2.0.0-rc.1", override: true},
|
||||
{:finch, "~> 0.15"},
|
||||
|
@ -172,6 +172,8 @@ defp deps do
|
|||
{:swoosh, "~> 1.16.9"},
|
||||
{:phoenix_swoosh, "~> 1.1"},
|
||||
{:gen_smtp, "~> 0.13"},
|
||||
{:mua, "~> 0.2.0"},
|
||||
{:mail, "~> 0.3.0"},
|
||||
{:ex_syslogger, "~> 1.4"},
|
||||
{:floki, "~> 0.35"},
|
||||
{:timex, "~> 3.6"},
|
||||
|
@ -208,6 +210,7 @@ defp deps do
|
|||
{:multipart, "~> 0.4.0", optional: true},
|
||||
{:icalendar, "~> 1.1"},
|
||||
{:geospatial, "~> 0.3.1"},
|
||||
{:argon2_elixir, "~> 4.0"},
|
||||
|
||||
## dev & test
|
||||
{:phoenix_live_reload, "~> 1.3.3", only: :dev},
|
||||
|
|
7
mix.lock
7
mix.lock
|
@ -1,5 +1,6 @@
|
|||
%{
|
||||
"accept": {:hex, :accept, "0.3.5", "b33b127abca7cc948bbe6caa4c263369abf1347cfa9d8e699c6d214660f10cd1", [:rebar3], [], "hexpm", "11b18c220bcc2eab63b5470c038ef10eb6783bcb1fcdb11aa4137defa5ac1bb8"},
|
||||
"argon2_elixir": {:hex, :argon2_elixir, "4.0.0", "7f6cd2e4a93a37f61d58a367d82f830ad9527082ff3c820b8197a8a736648941", [:make, :mix], [{:comeonin, "~> 5.3", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "f9da27cf060c9ea61b1bd47837a28d7e48a8f6fa13a745e252556c14f9132c7f"},
|
||||
"bandit": {:hex, :bandit, "1.5.5", "df28f1c41f745401fe9e85a6882033f5f3442ab6d30c8a2948554062a4ab56e0", [:mix], [{:hpax, "~> 0.2.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f21579a29ea4bc08440343b2b5f16f7cddf2fea5725d31b72cf973ec729079e1"},
|
||||
"base62": {:hex, :base62, "1.2.2", "85c6627eb609317b70f555294045895ffaaeb1758666ab9ef9ca38865b11e629", [:mix], [{:custom_base, "~> 0.2.1", [hex: :custom_base, repo: "hexpm", optional: false]}], "hexpm", "d41336bda8eaa5be197f1e4592400513ee60518e5b9f4dcf38f4b4dae6f377bb"},
|
||||
"bbcode_pleroma": {:hex, :bbcode_pleroma, "0.2.0", "d36f5bca6e2f62261c45be30fa9b92725c0655ad45c99025cb1c3e28e25803ef", [:mix], [{:nimble_parsec, "~> 0.5", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "19851074419a5fedb4ef49e1f01b30df504bb5dbb6d6adfc135238063bebd1c3"},
|
||||
|
@ -10,7 +11,7 @@
|
|||
"cachex": {:hex, :cachex, "3.6.0", "14a1bfbeee060dd9bec25a5b6f4e4691e3670ebda28c8ba2884b12fe30b36bf8", [:mix], [{:eternal, "~> 1.2", [hex: :eternal, repo: "hexpm", optional: false]}, {:jumper, "~> 1.0", [hex: :jumper, repo: "hexpm", optional: false]}, {:sleeplocks, "~> 1.1", [hex: :sleeplocks, repo: "hexpm", optional: false]}, {:unsafe, "~> 1.0", [hex: :unsafe, repo: "hexpm", optional: false]}], "hexpm", "ebf24e373883bc8e0c8d894a63bbe102ae13d918f790121f5cfe6e485cc8e2e2"},
|
||||
"calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.5.20 or ~> 0.1.201603 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"},
|
||||
"captcha": {:git, "https://git.pleroma.social/pleroma/elixir-libraries/elixir-captcha.git", "6630c42aaaab124e697b4e513190c89d8b64e410", [ref: "6630c42aaaab124e697b4e513190c89d8b64e410"]},
|
||||
"castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"},
|
||||
"castore": {:hex, :castore, "1.0.8", "dedcf20ea746694647f883590b82d9e96014057aff1d44d03ec90f36a5c0dc6e", [:mix], [], "hexpm", "0b2b66d2ee742cb1d9cb8c8be3b43c3a70ee8651f37b75a8b982e036752983f1"},
|
||||
"cc_precompiler": {:hex, :cc_precompiler, "0.1.9", "e8d3364f310da6ce6463c3dd20cf90ae7bbecbf6c5203b98bf9b48035592649b", [:mix], [{:elixir_make, "~> 0.7", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "9dcab3d0f3038621f1601f13539e7a9ee99843862e66ad62827b0c42b2f58a54"},
|
||||
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
|
||||
"combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"},
|
||||
|
@ -79,6 +80,7 @@
|
|||
"jumper": {:hex, :jumper, "1.0.2", "68cdcd84472a00ac596b4e6459a41b3062d4427cbd4f1e8c8793c5b54f1406a7", [:mix], [], "hexpm", "9b7782409021e01ab3c08270e26f36eb62976a38c1aa64b2eaf6348422f165e1"},
|
||||
"linkify": {:git, "https://gitlab.com/mkljczk/linkify", "b854b2f1701a5e13dfea50add729a8525a549f64", [branch: "master"]},
|
||||
"logger_backends": {:hex, :logger_backends, "1.0.0", "09c4fad6202e08cb0fbd37f328282f16539aca380f512523ce9472b28edc6bdf", [:mix], [], "hexpm", "1faceb3e7ec3ef66a8f5746c5afd020e63996df6fd4eb8cdb789e5665ae6c9ce"},
|
||||
"mail": {:hex, :mail, "0.3.1", "cb0a14e4ed8904e4e5a08214e686ccf6f9099346885db17d8c309381f865cc5c", [:mix], [], "hexpm", "1db701e89865c1d5fa296b2b57b1cd587587cca8d8a1a22892b35ef5a8e352a6"},
|
||||
"majic": {:hex, :majic, "1.0.0", "37e50648db5f5c2ff0c9fb46454d034d11596c03683807b9fb3850676ffdaab3", [:make, :mix], [{:elixir_make, "~> 0.6.1", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "7905858f76650d49695f14ea55cd9aaaee0c6654fa391671d4cf305c275a0a9e"},
|
||||
"makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"},
|
||||
"makeup_elixir": {:hex, :makeup_elixir, "0.14.1", "4f0e96847c63c17841d42c08107405a005a2680eb9c7ccadfd757bd31dabccfb", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "f2438b1a80eaec9ede832b5c41cd4f373b38fd7aa33e3b22d9db79e640cbde11"},
|
||||
|
@ -92,6 +94,7 @@
|
|||
"mock": {:hex, :mock, "0.3.8", "7046a306b71db2488ef54395eeb74df0a7f335a7caca4a3d3875d1fc81c884dd", [:mix], [{:meck, "~> 0.9.2", [hex: :meck, repo: "hexpm", optional: false]}], "hexpm", "7fa82364c97617d79bb7d15571193fc0c4fe5afd0c932cef09426b3ee6fe2022"},
|
||||
"mogrify": {:hex, :mogrify, "0.9.3", "238c782f00271dace01369ad35ae2e9dd020feee3443b9299ea5ea6bed559841", [:mix], [], "hexpm", "0189b1e1de27455f2b9ae8cf88239cefd23d38de9276eb5add7159aea51731e6"},
|
||||
"mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"},
|
||||
"mua": {:hex, :mua, "0.2.3", "46b29b7b2bb14105c0b7be9526f7c452df17a7841b30b69871c024a822ff551c", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}], "hexpm", "7fe861a87fcc06a980d3941bbcb2634e5f0f30fd6ad15ef6c0423ff9dc7e46de"},
|
||||
"multipart": {:hex, :multipart, "0.4.0", "634880a2148d4555d050963373d0e3bbb44a55b2badd87fa8623166172e9cda0", [:mix], [{:mime, "~> 1.2 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm", "3c5604bc2fb17b3137e5d2abdf5dacc2647e60c5cc6634b102cf1aef75a06f0a"},
|
||||
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
|
||||
"nimble_parsec": {:hex, :nimble_parsec, "0.6.0", "32111b3bf39137144abd7ba1cce0914533b2d16ef35e8abc5ec8be6122944263", [:mix], [], "hexpm", "27eac315a94909d4dc68bc07a4a83e06c8379237c5ea528a9acff4ca1c873c52"},
|
||||
|
@ -99,7 +102,7 @@
|
|||
"nodex": {:git, "https://git.pleroma.social/pleroma/nodex", "cb6730f943cfc6aad674c92161be23a8411f15d1", [ref: "cb6730f943cfc6aad674c92161be23a8411f15d1"]},
|
||||
"oauth2": {:hex, :oauth2, "0.9.4", "632e8e8826a45e33ac2ea5ac66dcc019ba6bb5a0d2ba77e342d33e3b7b252c6e", [:mix], [{:hackney, "~> 1.7", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "407c6b9f60aa0d01b915e2347dc6be78adca706a37f0c530808942da3b62e7af"},
|
||||
"oauther": {:hex, :oauther, "1.3.0", "82b399607f0ca9d01c640438b34d74ebd9e4acd716508f868e864537ecdb1f76", [:mix], [], "hexpm", "78eb888ea875c72ca27b0864a6f550bc6ee84f2eeca37b093d3d833fbcaec04e"},
|
||||
"oban": {:hex, :oban, "2.18.2", "583e78965ee15263ac968e38c983bad169ae55eadaa8e1e39912562badff93ba", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9dd25fd35883a91ed995e9fe516e479344d3a8623dfe2b8c3fc8e5be0228ec3a"},
|
||||
"oban": {:hex, :oban, "2.18.3", "1608c04f8856c108555c379f2f56bc0759149d35fa9d3b825cb8a6769f8ae926", [:mix], [{:ecto_sql, "~> 3.10", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ecto_sqlite3, "~> 0.9", [hex: :ecto_sqlite3, repo: "hexpm", optional: true]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "36ca6ca84ef6518f9c2c759ea88efd438a3c81d667ba23b02b062a0aa785475e"},
|
||||
"oban_live_dashboard": {:hex, :oban_live_dashboard, "0.1.1", "8aa4ceaf381c818f7d5c8185cc59942b8ac82ef0cf559881aacf8d3f8ac7bdd3", [:mix], [{:oban, "~> 2.15", [hex: :oban, repo: "hexpm", optional: false]}, {:phoenix_live_dashboard, "~> 0.7", [hex: :phoenix_live_dashboard, repo: "hexpm", optional: false]}], "hexpm", "16dc4ce9c9a95aa2e655e35ed4e675652994a8def61731a18af85e230e1caa63"},
|
||||
"octo_fetch": {:hex, :octo_fetch, "0.4.0", "074b5ecbc08be10b05b27e9db08bc20a3060142769436242702931c418695b19", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm", "cf8be6f40cd519d7000bb4e84adcf661c32e59369ca2827c4e20042eda7a7fc6"},
|
||||
"open_api_spex": {:hex, :open_api_spex, "3.18.2", "8c855e83bfe8bf81603d919d6e892541eafece3720f34d1700b58024dadde247", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:plug, "~> 1.7", [hex: :plug, repo: "hexpm", optional: false]}, {:poison, "~> 3.0 or ~> 4.0 or ~> 5.0", [hex: :poison, repo: "hexpm", optional: true]}, {:ymlr, "~> 2.0 or ~> 3.0 or ~> 4.0", [hex: :ymlr, repo: "hexpm", optional: true]}], "hexpm", "aa3e6dcfc0ad6a02596b2172662da21c9dd848dac145ea9e603f54e3d81b8d2b"},
|
||||
|
|
|
@ -7,8 +7,8 @@ defmodule Pleroma.Repo.Migrations.InstancesAddMetadata do
|
|||
|
||||
def change do
|
||||
alter table(:instances) do
|
||||
add(:metadata, :map)
|
||||
add(:metadata_updated_at, :utc_datetime)
|
||||
add_if_not_exists(:metadata, :map)
|
||||
add_if_not_exists(:metadata_updated_at, :utc_datetime)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,101 @@
|
|||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2024 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.RenamePleromaReportNotificationTypeToAdminReport do
|
||||
use Ecto.Migration
|
||||
|
||||
def up do
|
||||
alter table(:notifications) do
|
||||
modify(:type, :string)
|
||||
end
|
||||
|
||||
"""
|
||||
update notifications
|
||||
set type = 'admin.report'
|
||||
where type = 'pleroma:report'
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
drop type if exists notification_type
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
create type notification_type as enum (
|
||||
'follow',
|
||||
'follow_request',
|
||||
'mention',
|
||||
'move',
|
||||
'pleroma:emoji_reaction',
|
||||
'pleroma:chat_mention',
|
||||
'reblog',
|
||||
'favourite',
|
||||
'admin.report',
|
||||
'poll',
|
||||
'status',
|
||||
'update',
|
||||
'pleroma:participation_accepted',
|
||||
'pleroma:participation_request',
|
||||
'pleroma:event_reminder',
|
||||
'pleroma:event_update',
|
||||
'bite'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter table notifications
|
||||
alter column type type notification_type using (type::notification_type)
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
|
||||
def down do
|
||||
alter table(:notifications) do
|
||||
modify(:type, :string)
|
||||
end
|
||||
|
||||
"""
|
||||
update notifications
|
||||
set type = 'pleroma:report'
|
||||
where type = 'admin.report'
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
drop type if exists notification_type
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
create type notification_type as enum (
|
||||
'follow',
|
||||
'follow_request',
|
||||
'mention',
|
||||
'move',
|
||||
'pleroma:emoji_reaction',
|
||||
'pleroma:chat_mention',
|
||||
'reblog',
|
||||
'favourite',
|
||||
'pleroma:report',
|
||||
'poll',
|
||||
'status',
|
||||
'update',
|
||||
'pleroma:participation_accepted',
|
||||
'pleroma:participation_request',
|
||||
'pleroma:event_reminder',
|
||||
'pleroma:event_update',
|
||||
'bite'
|
||||
)
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
"""
|
||||
alter table notifications
|
||||
alter column type type notification_type using (type::notification_type)
|
||||
"""
|
||||
|> execute()
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
# Adapted from Akkoma
|
||||
# https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/priv/repo/migrations/20220108213213_add_mastofe_settings.exs
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.AddMastofeSettings do
|
||||
use Ecto.Migration
|
||||
|
||||
def up, do: :ok
|
||||
|
||||
def down do
|
||||
"""
|
||||
UPDATE users SET pleroma_settings_store = jsonb_set(
|
||||
pleroma_settings_store,
|
||||
'{glitch-lily}',
|
||||
mastofe_settings
|
||||
)
|
||||
WHERE mastofe_settings IS NOT NULL;
|
||||
"""
|
||||
|> execute()
|
||||
|
||||
alter table(:users) do
|
||||
remove_if_exists(:mastofe_settings, :map)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
defmodule Pleroma.Repo.Migrations.UpgradeObanToV11 do
|
||||
use Ecto.Migration
|
||||
|
||||
def change, do: :ok
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
defmodule Pleroma.Repo.Migrations.RemoveRemoteCancelledFollowRequests do
|
||||
use Ecto.Migration
|
||||
|
||||
def change, do: :ok
|
||||
end
|
|
@ -0,0 +1,5 @@
|
|||
defmodule Pleroma.Repo.Migrations.RemoveLocalCancelledFollows do
|
||||
use Ecto.Migration
|
||||
|
||||
def change, do: :ok
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# Adapted from Akkoma
|
||||
# https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/priv/repo/migrations/20220911195347_add_user_frontend_profiles.exs
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.AddUserFrontendProfiles do
|
||||
use Ecto.Migration
|
||||
|
||||
def up, do: :ok
|
||||
|
||||
def down do
|
||||
drop_if_exists(table("user_frontend_setting_profiles"))
|
||||
drop_if_exists(index(:user_frontend_setting_profiles, [:user_id, :frontend_name]))
|
||||
|
||||
drop_if_exists(
|
||||
unique_index(:user_frontend_setting_profiles, [:user_id, :frontend_name, :profile_name])
|
||||
)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,14 @@
|
|||
# Adapted from Akkoma
|
||||
# https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/priv/repo/migrations/20220916115149_ensure_mastofe_settings.exs
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.EnsureMastofeSettings do
|
||||
use Ecto.Migration
|
||||
|
||||
def up, do: :ok
|
||||
|
||||
def down do
|
||||
alter table(:users) do
|
||||
remove_if_exists(:mastofe_settings, :map)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# Adapted from Akkoma
|
||||
# https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/priv/repo/migrations/20221020135943_add_nodeinfo.exs
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.AddNodeinfo do
|
||||
use Ecto.Migration
|
||||
|
||||
def up, do: :ok
|
||||
|
||||
def down do
|
||||
alter table(:instances) do
|
||||
remove_if_exists(:nodeinfo, :map)
|
||||
remove_if_exists(:metadata_updated_at, :naive_datetime)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
# Adapted from Akkoma
|
||||
# https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/priv/repo/migrations/20221123221956_add_has_request_signatures.exs
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.AddHasRequestSignatures do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:instances) do
|
||||
add(:has_request_signatures, :boolean, default: false, null: false)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
# Adapted from Akkoma
|
||||
# https://akkoma.dev/AkkomaGang/akkoma/src/branch/develop/priv/repo/migrations/20221128103145_add_per_user_post_expiry.exs
|
||||
|
||||
defmodule Pleroma.Repo.Migrations.AddPerUserPostExpiry do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:users) do
|
||||
add(:status_ttl_days, :integer, null: true)
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue