initial commit to baseless
This commit is contained in:
commit
799d7c4634
137
.credo.exs
Normal file
137
.credo.exs
Normal file
@ -0,0 +1,137 @@
|
||||
# This file contains the configuration for Credo and you are probably reading
|
||||
# this after creating it with `mix credo.gen.config`.
|
||||
#
|
||||
# If you find anything wrong or unclear in this file, please report an
|
||||
# issue on GitHub: https://github.com/rrrene/credo/issues
|
||||
#
|
||||
%{
|
||||
#
|
||||
# You can have as many configs as you like in the `configs:` field.
|
||||
configs: [
|
||||
%{
|
||||
#
|
||||
# Run any config using `mix credo -C <name>`. If no config name is given
|
||||
# "default" is used.
|
||||
name: "default",
|
||||
#
|
||||
# These are the files included in the analysis:
|
||||
files: %{
|
||||
#
|
||||
# You can give explicit globs or simply directories.
|
||||
# In the latter case `**/*.{ex,exs}` will be used.
|
||||
included: ["lib/", "src/", "web/", "apps/", "test/"],
|
||||
excluded: [~r"/_build/", ~r"/deps/"]
|
||||
},
|
||||
#
|
||||
# If you create your own checks, you must specify the source files for
|
||||
# them here, so they can be loaded by Credo before running the analysis.
|
||||
requires: ["./test/credo/check/consistency/file_location.ex"],
|
||||
#
|
||||
# Credo automatically checks for updates, like e.g. Hex does.
|
||||
# You can disable this behaviour below:
|
||||
check_for_updates: true,
|
||||
#
|
||||
# If you want to enforce a style guide and need a more traditional linting
|
||||
# experience, you can change `strict` to `true` below:
|
||||
strict: false,
|
||||
#
|
||||
# If you want to use uncolored output by default, you can change `color`
|
||||
# to `false` below:
|
||||
color: true,
|
||||
#
|
||||
# You can customize the parameters of any check by adding a second element
|
||||
# to the tuple.
|
||||
#
|
||||
# To disable a check put `false` as second element:
|
||||
#
|
||||
# {Credo.Check.Design.DuplicatedCode, false}
|
||||
#
|
||||
checks: [
|
||||
{Credo.Check.Consistency.ExceptionNames},
|
||||
{Credo.Check.Consistency.LineEndings},
|
||||
{Credo.Check.Consistency.MultiAliasImportRequireUse},
|
||||
{Credo.Check.Consistency.ParameterPatternMatching},
|
||||
{Credo.Check.Consistency.SpaceAroundOperators},
|
||||
{Credo.Check.Consistency.SpaceInParentheses},
|
||||
{Credo.Check.Consistency.TabsOrSpaces},
|
||||
|
||||
# For some checks, like AliasUsage, you can only customize the priority
|
||||
# Priority values are: `low, normal, high, higher`
|
||||
{Credo.Check.Design.AliasUsage, priority: :low, if_called_more_often_than: 3},
|
||||
|
||||
# For others you can set parameters
|
||||
|
||||
# If you don't want the `setup` and `test` macro calls in ExUnit tests
|
||||
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
|
||||
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
|
||||
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
|
||||
|
||||
# You can also customize the exit_status of each check.
|
||||
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||
# set this value to 0 (zero).
|
||||
{Credo.Check.Design.TagTODO, exit_status: 0},
|
||||
{Credo.Check.Design.TagFIXME, exit_status: 0},
|
||||
{Credo.Check.Readability.FunctionNames},
|
||||
{Credo.Check.Readability.LargeNumbers},
|
||||
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 100},
|
||||
{Credo.Check.Readability.ModuleAttributeNames},
|
||||
{Credo.Check.Readability.ModuleDoc, false},
|
||||
{Credo.Check.Readability.ModuleNames},
|
||||
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
|
||||
{Credo.Check.Readability.ParenthesesInCondition},
|
||||
{Credo.Check.Readability.PredicateFunctionNames},
|
||||
# lanodan: I think PreferImplicitTry should be consistency, and the behaviour seems
|
||||
# inconsistent, see: https://github.com/rrrene/credo/issues/224
|
||||
{Credo.Check.Readability.PreferImplicitTry, false},
|
||||
{Credo.Check.Readability.PipeIntoAnonymousFunctions, exit_status: 0},
|
||||
{Credo.Check.Readability.RedundantBlankLines},
|
||||
{Credo.Check.Readability.StringSigils},
|
||||
{Credo.Check.Readability.TrailingBlankLine},
|
||||
{Credo.Check.Readability.TrailingWhiteSpace},
|
||||
{Credo.Check.Readability.VariableNames},
|
||||
{Credo.Check.Readability.Semicolons},
|
||||
{Credo.Check.Readability.SpaceAfterCommas},
|
||||
{Credo.Check.Readability.WithSingleClause, exit_status: 0},
|
||||
{Credo.Check.Refactor.DoubleBooleanNegation},
|
||||
{Credo.Check.Refactor.CondStatements},
|
||||
{Credo.Check.Refactor.CyclomaticComplexity},
|
||||
{Credo.Check.Refactor.FunctionArity},
|
||||
{Credo.Check.Refactor.MatchInCondition},
|
||||
{Credo.Check.Refactor.NegatedConditionsInUnless},
|
||||
{Credo.Check.Refactor.NegatedConditionsWithElse},
|
||||
{Credo.Check.Refactor.Nesting},
|
||||
{Credo.Check.Refactor.PipeChainStart},
|
||||
{Credo.Check.Refactor.UnlessWithElse},
|
||||
{Credo.Check.Warning.BoolOperationOnSameValues},
|
||||
{Credo.Check.Warning.IExPry},
|
||||
{Credo.Check.Warning.IoInspect},
|
||||
# Got too much of them, not sure if relevant
|
||||
{Credo.Check.Warning.LazyLogging, false},
|
||||
{Credo.Check.Warning.OperationOnSameValues},
|
||||
{Credo.Check.Warning.OperationWithConstantResult},
|
||||
{Credo.Check.Warning.UnusedEnumOperation},
|
||||
{Credo.Check.Warning.UnusedFileOperation},
|
||||
{Credo.Check.Warning.UnusedKeywordOperation},
|
||||
{Credo.Check.Warning.UnusedListOperation},
|
||||
{Credo.Check.Warning.UnusedPathOperation},
|
||||
{Credo.Check.Warning.UnusedRegexOperation},
|
||||
{Credo.Check.Warning.UnusedStringOperation},
|
||||
{Credo.Check.Warning.UnusedTupleOperation},
|
||||
|
||||
# Controversial and experimental checks (opt-in, just remove `, false`)
|
||||
#
|
||||
{Credo.Check.Refactor.ABCSize, false},
|
||||
{Credo.Check.Refactor.AppendSingleItem, false},
|
||||
{Credo.Check.Refactor.VariableRebinding, false},
|
||||
{Credo.Check.Warning.MapGetUnsafePass, false},
|
||||
|
||||
# Deprecated checks (these will be deleted after a grace period)
|
||||
{Credo.Check.Readability.Specs, false},
|
||||
|
||||
# Custom checks can be created using `mix credo.gen.check`.
|
||||
#
|
||||
{Credo.Check.Consistency.FileLocation}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
73
.dockerignore
Normal file
73
.dockerignore
Normal file
@ -0,0 +1,73 @@
|
||||
.*
|
||||
*.md
|
||||
AGPL-3
|
||||
CC-BY-SA-4.0
|
||||
COPYING
|
||||
*file
|
||||
elixir_buildpack.config
|
||||
test/
|
||||
|
||||
# Required to get version
|
||||
!.git
|
||||
|
||||
# App artifacts
|
||||
/_build
|
||||
/db
|
||||
/deps
|
||||
/*.ez
|
||||
/test/instance
|
||||
/test/uploads
|
||||
/.elixir_ls
|
||||
/test/fixtures/DSCN0010_tmp.jpg
|
||||
/test/fixtures/test_tmp.txt
|
||||
/test/fixtures/image_tmp.jpg
|
||||
/test/tmp/
|
||||
/test/frontend_static_test/
|
||||
/doc
|
||||
/instance
|
||||
/priv/ssh_keys
|
||||
|
||||
# Prevent committing custom emojis
|
||||
/priv/static/emoji/custom/*
|
||||
|
||||
# Generated on crash by the VM
|
||||
erl_crash.dump
|
||||
|
||||
# Files matching config/*.secret.exs pattern contain sensitive
|
||||
# data and you should not commit them into version control.
|
||||
#
|
||||
# Alternatively, you may comment the line below and commit the
|
||||
# secrets files as long as you replace their contents by environment
|
||||
# variables.
|
||||
/config/*.secret.exs
|
||||
/config/generated_config.exs
|
||||
/config/runtime.exs
|
||||
/config/*.env
|
||||
|
||||
|
||||
# Database setup file, some may forget to delete it
|
||||
/config/setup_db*.psql
|
||||
|
||||
# Whitelist Landing FE
|
||||
!/instance/static/frontends/landing-fe/vendor/**
|
||||
|
||||
.DS_Store
|
||||
.env
|
||||
|
||||
# Editor config
|
||||
/.vscode/
|
||||
|
||||
# Prevent committing docs files
|
||||
/priv/static/doc/*
|
||||
docs/generated_config.md
|
||||
|
||||
# Code test coverage
|
||||
/cover
|
||||
/Elixir.*.coverdata
|
||||
|
||||
.idea
|
||||
pleroma.iml
|
||||
|
||||
# Editor temp files
|
||||
/*~
|
||||
/*#
|
3
.formatter.exs
Normal file
3
.formatter.exs
Normal file
@ -0,0 +1,3 @@
|
||||
[
|
||||
inputs: ["mix.exs", "{config,lib,test}/**/*.{ex,exs}", "priv/repo/migrations/*.exs", "priv/repo/optional_migrations/**/*.exs", "priv/scrubbers/*.ex"]
|
||||
]
|
10
.gitattributes
vendored
Normal file
10
.gitattributes
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
*.ex diff=elixir
|
||||
*.exs diff=elixir
|
||||
|
||||
priv/static/instance/static.css diff=css
|
||||
|
||||
# Most of js/css files included in the repo are minified bundles,
|
||||
# and we don't want to search/diff those as text files.
|
||||
*.js binary
|
||||
*.js.map binary
|
||||
*.css binary
|
59
.gitignore
vendored
Normal file
59
.gitignore
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
# App artifacts
|
||||
/_build
|
||||
/db
|
||||
/deps
|
||||
/*.ez
|
||||
/test/instance
|
||||
/test/uploads
|
||||
/.elixir_ls
|
||||
/test/fixtures/DSCN0010_tmp.jpg
|
||||
/test/fixtures/test_tmp.txt
|
||||
/test/fixtures/image_tmp.jpg
|
||||
/test/tmp/
|
||||
/test/frontend_static_test/
|
||||
/doc
|
||||
/instance
|
||||
/priv/ssh_keys
|
||||
|
||||
# Prevent committing custom emojis
|
||||
/priv/static/emoji/custom/*
|
||||
|
||||
# Generated on crash by the VM
|
||||
erl_crash.dump
|
||||
|
||||
# Files matching config/*.secret.exs pattern contain sensitive
|
||||
# data and you should not commit them into version control.
|
||||
#
|
||||
# Alternatively, you may comment the line below and commit the
|
||||
# secrets files as long as you replace their contents by environment
|
||||
# variables.
|
||||
/config/*.secret.exs
|
||||
/config/generated_config.exs
|
||||
/config/runtime.exs
|
||||
/config/*.env
|
||||
|
||||
|
||||
# Database setup file, some may forget to delete it
|
||||
/config/setup_db*.psql
|
||||
|
||||
.DS_Store
|
||||
.env
|
||||
|
||||
# Editor config
|
||||
/.vscode/
|
||||
|
||||
# Prevent committing docs files
|
||||
/priv/static/doc/*
|
||||
docs/generated_config.md
|
||||
|
||||
# Code test coverage
|
||||
/cover
|
||||
/Elixir.*.coverdata
|
||||
/coverage.xml
|
||||
|
||||
.idea
|
||||
pleroma.iml
|
||||
|
||||
# Editor temp files
|
||||
/*~
|
||||
/*#
|
161
.gitlab-ci.yml
Normal file
161
.gitlab-ci.yml
Normal file
@ -0,0 +1,161 @@
|
||||
image: registry.gitlab.com/soapbox-pub/rebased/ci
|
||||
|
||||
variables: &global_variables
|
||||
POSTGRES_DB: pleroma_test
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_PASSWORD: postgres
|
||||
DB_HOST: postgres
|
||||
DB_PORT: 5432
|
||||
MIX_ENV: test
|
||||
# Needed for Dokku deployment.
|
||||
# https://github.com/dokku/dokku/issues/2514#issuecomment-616775470
|
||||
GIT_DEPTH: 0
|
||||
|
||||
cache: &cache
|
||||
key:
|
||||
files:
|
||||
- mix.lock
|
||||
paths:
|
||||
- deps
|
||||
- _build
|
||||
policy: pull
|
||||
|
||||
stages:
|
||||
- deps
|
||||
- test
|
||||
- deploy
|
||||
|
||||
deps:
|
||||
stage: deps
|
||||
script:
|
||||
- mix deps.get
|
||||
- mix deps.compile
|
||||
cache:
|
||||
<<: *cache
|
||||
policy: pull-push
|
||||
only:
|
||||
changes:
|
||||
- mix.lock
|
||||
|
||||
openapi:
|
||||
stage: test
|
||||
only:
|
||||
changes:
|
||||
- ".gitlab-ci.yml"
|
||||
- "lib/pleroma/web/api_spec/**/*.ex"
|
||||
- "lib/pleroma/web/api_spec.ex"
|
||||
artifacts:
|
||||
paths:
|
||||
- spec.json
|
||||
script:
|
||||
- mix pleroma.openapi_spec spec.json
|
||||
|
||||
test:
|
||||
stage: test
|
||||
only:
|
||||
changes: &elixir-changes
|
||||
- ".gitlab-ci.yml"
|
||||
- "**/*.ex"
|
||||
- "**/*.exs"
|
||||
- "mix.lock"
|
||||
services: &db-services
|
||||
- name: postgres:13-alpine
|
||||
alias: postgres
|
||||
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||
before_script: &db-setup
|
||||
- mix ecto.create
|
||||
- mix ecto.migrate
|
||||
script:
|
||||
- mix test --cover --preload-modules
|
||||
coverage: '/^Line total: ([^ ]*%)$/'
|
||||
artifacts:
|
||||
reports:
|
||||
coverage_report:
|
||||
coverage_format: cobertura
|
||||
path: coverage.xml
|
||||
|
||||
test-erratic:
|
||||
stage: test
|
||||
allow_failure: true
|
||||
only:
|
||||
changes: *elixir-changes
|
||||
services: *db-services
|
||||
before_script: *db-setup
|
||||
script:
|
||||
- mix test --only=erratic
|
||||
|
||||
lint:
|
||||
stage: test
|
||||
only:
|
||||
changes: *elixir-changes
|
||||
script:
|
||||
- mix format --check-formatted
|
||||
|
||||
# analysis:
|
||||
# stage: test
|
||||
# only:
|
||||
# changes: *elixir-changes
|
||||
# script:
|
||||
# - mix credo --strict --only=warnings,todo,fixme,consistency,readability
|
||||
|
||||
cycles:
|
||||
stage: test
|
||||
only:
|
||||
changes: *elixir-changes
|
||||
script:
|
||||
- mix xref graph --format cycles --label compile | awk '{print $0} END{exit ($0 != "No cycles found")}'
|
||||
|
||||
# Deploy with Dokku
|
||||
# https://github.com/dokku/gitlab-ci
|
||||
# https://github.com/dokku/ci-docker-image
|
||||
review:
|
||||
image: dokku/ci-docker-image
|
||||
stage: test
|
||||
environment:
|
||||
name: review/$CI_COMMIT_REF_NAME
|
||||
url: https://rebased-$CI_COMMIT_REF_SLUG.dokku.soapbox.pub
|
||||
only:
|
||||
- branches
|
||||
except:
|
||||
- main
|
||||
variables:
|
||||
GIT_REMOTE_URL: ssh://dokku@$DOKKU_HOST/rebased-$CI_COMMIT_REF_SLUG
|
||||
GIT_PUSH_FLAGS: --force
|
||||
script: dokku-deploy
|
||||
allow_failure: true
|
||||
|
||||
release:
|
||||
stage: deploy
|
||||
variables:
|
||||
MIX_ENV: prod
|
||||
PLEROMA_BUILD_BRANCH: $CI_COMMIT_REF_NAME
|
||||
script:
|
||||
- mix deps.get
|
||||
- mkdir release
|
||||
- mix release --path release
|
||||
artifacts:
|
||||
name: "rebased-$CI_COMMIT_REF_NAME-$CI_COMMIT_SHORT_SHA-$CI_JOB_NAME"
|
||||
paths:
|
||||
- release/*
|
||||
cache: {}
|
||||
only:
|
||||
refs:
|
||||
- main
|
||||
changes: *elixir-changes
|
||||
|
||||
docker:
|
||||
stage: deploy
|
||||
image: docker:20.10.17
|
||||
cache: {}
|
||||
services:
|
||||
- docker:20.10.17-dind
|
||||
tags:
|
||||
- dind
|
||||
# https://medium.com/devops-with-valentine/how-to-build-a-docker-image-and-push-it-to-the-gitlab-container-registry-from-a-gitlab-ci-pipeline-acac0d1f26df
|
||||
script:
|
||||
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER $CI_REGISTRY --password-stdin
|
||||
- docker build -t $CI_REGISTRY_IMAGE .
|
||||
- docker push $CI_REGISTRY_IMAGE
|
||||
only:
|
||||
refs:
|
||||
- main
|
18
.gitlab/issue_templates/Bug.md
Normal file
18
.gitlab/issue_templates/Bug.md
Normal file
@ -0,0 +1,18 @@
|
||||
<!--
|
||||
### Precheck
|
||||
|
||||
* For support use https://git.pleroma.social/pleroma/pleroma-support or [community channels](https://git.pleroma.social/pleroma/pleroma#community-channels).
|
||||
* Please do a quick search to ensure no similar bug has been reported before. If the bug has not been addressed after 2 weeks, it's fine to bump it.
|
||||
* Try to ensure that the bug is actually related to the Pleroma backend. For example, if a bug happens in Pleroma-FE but not in Mastodon-FE or mobile clients, it's likely that the bug should be filed in [Pleroma-FE](https://git.pleroma.social/pleroma/pleroma-fe/issues/new) repository.
|
||||
-->
|
||||
|
||||
### Environment
|
||||
|
||||
* Installation type (OTP or From Source):
|
||||
* Pleroma version (could be found in the "Version" tab of settings in Pleroma-FE):
|
||||
* Elixir version (`elixir -v` for from source installations, N/A for OTP):
|
||||
* Operating system:
|
||||
* PostgreSQL version (`psql -V`):
|
||||
|
||||
|
||||
### Bug description
|
10
.gitlab/merge_request_templates/Default.md
Normal file
10
.gitlab/merge_request_templates/Default.md
Normal file
@ -0,0 +1,10 @@
|
||||
### Checklist
|
||||
- [ ] Adding a changelog: In the `changelog.d` directory, create a file named `<code>.<type>`.
|
||||
|
||||
`<code>` can be anything, but we recommend using a more or less unique identifier to avoid collisions, such as the branch name.
|
||||
|
||||
`<type>` can be `add`, `change`, `remove`, `fix`, `security` or `skip`. `skip` is only used if there is no user-visible change in the MR (for example, only editing comments in the code). Otherwise, choose a type that corresponds to your change.
|
||||
|
||||
In the file, write the changelog entry. For example, if an MR adds group functionality, we can create a file named `group.add` and write `Add group functionality` in it.
|
||||
|
||||
If one changelog entry is not enough, you may add more. But that might mean you can split it into two MRs. Only use more than one changelog entry if you really need to (for example, when one change in the code fix two different bugs, or when refactoring).
|
8
.gitlab/merge_request_templates/Release.md
Normal file
8
.gitlab/merge_request_templates/Release.md
Normal file
@ -0,0 +1,8 @@
|
||||
### Release checklist
|
||||
* [ ] Bump version in `mix.exs`
|
||||
* [ ] Compile a changelog with the `tools/collect-changelog` script
|
||||
* [ ] Create an MR with an announcement to pleroma.social
|
||||
#### post-merge
|
||||
* [ ] Tag the release on the merge commit
|
||||
* [ ] Make the tag into a Gitlab Release™
|
||||
* [ ] Merge `stable` into `develop` (in case the fixes are already in develop, use `git merge -s ours --no-commit` and manually merge the changelogs)
|
3
.mailmap
Normal file
3
.mailmap
Normal file
@ -0,0 +1,3 @@
|
||||
Ariadne Conill <ariadne@dereferenced.org> <nenolod@dereferenced.org>
|
||||
Ariadne Conill <ariadne@dereferenced.org> <nenolod@gmail.com>
|
||||
rinpatch <rin@patch.cx> <rinpatch@sdf.org>
|
2
.tool-versions
Normal file
2
.tool-versions
Normal file
@ -0,0 +1,2 @@
|
||||
elixir 1.13
|
||||
erlang 24.3.4.2
|
661
AGPL-3
Normal file
661
AGPL-3
Normal file
@ -0,0 +1,661 @@
|
||||
GNU AFFERO GENERAL PUBLIC LICENSE
|
||||
Version 3, 19 November 2007
|
||||
|
||||
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The GNU Affero General Public License is a free, copyleft license for
|
||||
software and other kinds of works, specifically designed to ensure
|
||||
cooperation with the community in the case of network server software.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
our General Public Licenses are intended to guarantee your freedom to
|
||||
share and change all versions of a program--to make sure it remains free
|
||||
software for all its users.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
them if you wish), that you receive source code or can get it if you
|
||||
want it, that you can change the software or use pieces of it in new
|
||||
free programs, and that you know you can do these things.
|
||||
|
||||
Developers that use our General Public Licenses protect your rights
|
||||
with two steps: (1) assert copyright on the software, and (2) offer
|
||||
you this License which gives you legal permission to copy, distribute
|
||||
and/or modify the software.
|
||||
|
||||
A secondary benefit of defending all users' freedom is that
|
||||
improvements made in alternate versions of the program, if they
|
||||
receive widespread use, become available for other developers to
|
||||
incorporate. Many developers of free software are heartened and
|
||||
encouraged by the resulting cooperation. However, in the case of
|
||||
software used on network servers, this result may fail to come about.
|
||||
The GNU General Public License permits making a modified version and
|
||||
letting the public access it on a server without ever releasing its
|
||||
source code to the public.
|
||||
|
||||
The GNU Affero General Public License is designed specifically to
|
||||
ensure that, in such cases, the modified source code becomes available
|
||||
to the community. It requires the operator of a network server to
|
||||
provide the source code of the modified version running there to the
|
||||
users of that server. Therefore, public use of a modified version, on
|
||||
a publicly accessible server, gives the public access to the source
|
||||
code of the modified version.
|
||||
|
||||
An older license, called the Affero General Public License and
|
||||
published by Affero, was designed to accomplish similar goals. This is
|
||||
a different license, not a version of the Affero GPL, but Affero has
|
||||
released a new version of the Affero GPL which permits relicensing under
|
||||
this license.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
TERMS AND CONDITIONS
|
||||
|
||||
0. Definitions.
|
||||
|
||||
"This License" refers to version 3 of the GNU Affero General Public License.
|
||||
|
||||
"Copyright" also means copyright-like laws that apply to other kinds of
|
||||
works, such as semiconductor masks.
|
||||
|
||||
"The Program" refers to any copyrightable work licensed under this
|
||||
License. Each licensee is addressed as "you". "Licensees" and
|
||||
"recipients" may be individuals or organizations.
|
||||
|
||||
To "modify" a work means to copy from or adapt all or part of the work
|
||||
in a fashion requiring copyright permission, other than the making of an
|
||||
exact copy. The resulting work is called a "modified version" of the
|
||||
earlier work or a work "based on" the earlier work.
|
||||
|
||||
A "covered work" means either the unmodified Program or a work based
|
||||
on the Program.
|
||||
|
||||
To "propagate" a work means to do anything with it that, without
|
||||
permission, would make you directly or secondarily liable for
|
||||
infringement under applicable copyright law, except executing it on a
|
||||
computer or modifying a private copy. Propagation includes copying,
|
||||
distribution (with or without modification), making available to the
|
||||
public, and in some countries other activities as well.
|
||||
|
||||
To "convey" a work means any kind of propagation that enables other
|
||||
parties to make or receive copies. Mere interaction with a user through
|
||||
a computer network, with no transfer of a copy, is not conveying.
|
||||
|
||||
An interactive user interface displays "Appropriate Legal Notices"
|
||||
to the extent that it includes a convenient and prominently visible
|
||||
feature that (1) displays an appropriate copyright notice, and (2)
|
||||
tells the user that there is no warranty for the work (except to the
|
||||
extent that warranties are provided), that licensees may convey the
|
||||
work under this License, and how to view a copy of this License. If
|
||||
the interface presents a list of user commands or options, such as a
|
||||
menu, a prominent item in the list meets this criterion.
|
||||
|
||||
1. Source Code.
|
||||
|
||||
The "source code" for a work means the preferred form of the work
|
||||
for making modifications to it. "Object code" means any non-source
|
||||
form of a work.
|
||||
|
||||
A "Standard Interface" means an interface that either is an official
|
||||
standard defined by a recognized standards body, or, in the case of
|
||||
interfaces specified for a particular programming language, one that
|
||||
is widely used among developers working in that language.
|
||||
|
||||
The "System Libraries" of an executable work include anything, other
|
||||
than the work as a whole, that (a) is included in the normal form of
|
||||
packaging a Major Component, but which is not part of that Major
|
||||
Component, and (b) serves only to enable use of the work with that
|
||||
Major Component, or to implement a Standard Interface for which an
|
||||
implementation is available to the public in source code form. A
|
||||
"Major Component", in this context, means a major essential component
|
||||
(kernel, window system, and so on) of the specific operating system
|
||||
(if any) on which the executable work runs, or a compiler used to
|
||||
produce the work, or an object code interpreter used to run it.
|
||||
|
||||
The "Corresponding Source" for a work in object code form means all
|
||||
the source code needed to generate, install, and (for an executable
|
||||
work) run the object code and to modify the work, including scripts to
|
||||
control those activities. However, it does not include the work's
|
||||
System Libraries, or general-purpose tools or generally available free
|
||||
programs which are used unmodified in performing those activities but
|
||||
which are not part of the work. For example, Corresponding Source
|
||||
includes interface definition files associated with source files for
|
||||
the work, and the source code for shared libraries and dynamically
|
||||
linked subprograms that the work is specifically designed to require,
|
||||
such as by intimate data communication or control flow between those
|
||||
subprograms and other parts of the work.
|
||||
|
||||
The Corresponding Source need not include anything that users
|
||||
can regenerate automatically from other parts of the Corresponding
|
||||
Source.
|
||||
|
||||
The Corresponding Source for a work in source code form is that
|
||||
same work.
|
||||
|
||||
2. Basic Permissions.
|
||||
|
||||
All rights granted under this License are granted for the term of
|
||||
copyright on the Program, and are irrevocable provided the stated
|
||||
conditions are met. This License explicitly affirms your unlimited
|
||||
permission to run the unmodified Program. The output from running a
|
||||
covered work is covered by this License only if the output, given its
|
||||
content, constitutes a covered work. This License acknowledges your
|
||||
rights of fair use or other equivalent, as provided by copyright law.
|
||||
|
||||
You may make, run and propagate covered works that you do not
|
||||
convey, without conditions so long as your license otherwise remains
|
||||
in force. You may convey covered works to others for the sole purpose
|
||||
of having them make modifications exclusively for you, or provide you
|
||||
with facilities for running those works, provided that you comply with
|
||||
the terms of this License in conveying all material for which you do
|
||||
not control copyright. Those thus making or running the covered works
|
||||
for you must do so exclusively on your behalf, under your direction
|
||||
and control, on terms that prohibit them from making any copies of
|
||||
your copyrighted material outside their relationship with you.
|
||||
|
||||
Conveying under any other circumstances is permitted solely under
|
||||
the conditions stated below. Sublicensing is not allowed; section 10
|
||||
makes it unnecessary.
|
||||
|
||||
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
|
||||
|
||||
No covered work shall be deemed part of an effective technological
|
||||
measure under any applicable law fulfilling obligations under article
|
||||
11 of the WIPO copyright treaty adopted on 20 December 1996, or
|
||||
similar laws prohibiting or restricting circumvention of such
|
||||
measures.
|
||||
|
||||
When you convey a covered work, you waive any legal power to forbid
|
||||
circumvention of technological measures to the extent such circumvention
|
||||
is effected by exercising rights under this License with respect to
|
||||
the covered work, and you disclaim any intention to limit operation or
|
||||
modification of the work as a means of enforcing, against the work's
|
||||
users, your or third parties' legal rights to forbid circumvention of
|
||||
technological measures.
|
||||
|
||||
4. Conveying Verbatim Copies.
|
||||
|
||||
You may convey verbatim copies of the Program's source code as you
|
||||
receive it, in any medium, provided that you conspicuously and
|
||||
appropriately publish on each copy an appropriate copyright notice;
|
||||
keep intact all notices stating that this License and any
|
||||
non-permissive terms added in accord with section 7 apply to the code;
|
||||
keep intact all notices of the absence of any warranty; and give all
|
||||
recipients a copy of this License along with the Program.
|
||||
|
||||
You may charge any price or no price for each copy that you convey,
|
||||
and you may offer support or warranty protection for a fee.
|
||||
|
||||
5. Conveying Modified Source Versions.
|
||||
|
||||
You may convey a work based on the Program, or the modifications to
|
||||
produce it from the Program, in the form of source code under the
|
||||
terms of section 4, provided that you also meet all of these conditions:
|
||||
|
||||
a) The work must carry prominent notices stating that you modified
|
||||
it, and giving a relevant date.
|
||||
|
||||
b) The work must carry prominent notices stating that it is
|
||||
released under this License and any conditions added under section
|
||||
7. This requirement modifies the requirement in section 4 to
|
||||
"keep intact all notices".
|
||||
|
||||
c) You must license the entire work, as a whole, under this
|
||||
License to anyone who comes into possession of a copy. This
|
||||
License will therefore apply, along with any applicable section 7
|
||||
additional terms, to the whole of the work, and all its parts,
|
||||
regardless of how they are packaged. This License gives no
|
||||
permission to license the work in any other way, but it does not
|
||||
invalidate such permission if you have separately received it.
|
||||
|
||||
d) If the work has interactive user interfaces, each must display
|
||||
Appropriate Legal Notices; however, if the Program has interactive
|
||||
interfaces that do not display Appropriate Legal Notices, your
|
||||
work need not make them do so.
|
||||
|
||||
A compilation of a covered work with other separate and independent
|
||||
works, which are not by their nature extensions of the covered work,
|
||||
and which are not combined with it such as to form a larger program,
|
||||
in or on a volume of a storage or distribution medium, is called an
|
||||
"aggregate" if the compilation and its resulting copyright are not
|
||||
used to limit the access or legal rights of the compilation's users
|
||||
beyond what the individual works permit. Inclusion of a covered work
|
||||
in an aggregate does not cause this License to apply to the other
|
||||
parts of the aggregate.
|
||||
|
||||
6. Conveying Non-Source Forms.
|
||||
|
||||
You may convey a covered work in object code form under the terms
|
||||
of sections 4 and 5, provided that you also convey the
|
||||
machine-readable Corresponding Source under the terms of this License,
|
||||
in one of these ways:
|
||||
|
||||
a) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by the
|
||||
Corresponding Source fixed on a durable physical medium
|
||||
customarily used for software interchange.
|
||||
|
||||
b) Convey the object code in, or embodied in, a physical product
|
||||
(including a physical distribution medium), accompanied by a
|
||||
written offer, valid for at least three years and valid for as
|
||||
long as you offer spare parts or customer support for that product
|
||||
model, to give anyone who possesses the object code either (1) a
|
||||
copy of the Corresponding Source for all the software in the
|
||||
product that is covered by this License, on a durable physical
|
||||
medium customarily used for software interchange, for a price no
|
||||
more than your reasonable cost of physically performing this
|
||||
conveying of source, or (2) access to copy the
|
||||
Corresponding Source from a network server at no charge.
|
||||
|
||||
c) Convey individual copies of the object code with a copy of the
|
||||
written offer to provide the Corresponding Source. This
|
||||
alternative is allowed only occasionally and noncommercially, and
|
||||
only if you received the object code with such an offer, in accord
|
||||
with subsection 6b.
|
||||
|
||||
d) Convey the object code by offering access from a designated
|
||||
place (gratis or for a charge), and offer equivalent access to the
|
||||
Corresponding Source in the same way through the same place at no
|
||||
further charge. You need not require recipients to copy the
|
||||
Corresponding Source along with the object code. If the place to
|
||||
copy the object code is a network server, the Corresponding Source
|
||||
may be on a different server (operated by you or a third party)
|
||||
that supports equivalent copying facilities, provided you maintain
|
||||
clear directions next to the object code saying where to find the
|
||||
Corresponding Source. Regardless of what server hosts the
|
||||
Corresponding Source, you remain obligated to ensure that it is
|
||||
available for as long as needed to satisfy these requirements.
|
||||
|
||||
e) Convey the object code using peer-to-peer transmission, provided
|
||||
you inform other peers where the object code and Corresponding
|
||||
Source of the work are being offered to the general public at no
|
||||
charge under subsection 6d.
|
||||
|
||||
A separable portion of the object code, whose source code is excluded
|
||||
from the Corresponding Source as a System Library, need not be
|
||||
included in conveying the object code work.
|
||||
|
||||
A "User Product" is either (1) a "consumer product", which means any
|
||||
tangible personal property which is normally used for personal, family,
|
||||
or household purposes, or (2) anything designed or sold for incorporation
|
||||
into a dwelling. In determining whether a product is a consumer product,
|
||||
doubtful cases shall be resolved in favor of coverage. For a particular
|
||||
product received by a particular user, "normally used" refers to a
|
||||
typical or common use of that class of product, regardless of the status
|
||||
of the particular user or of the way in which the particular user
|
||||
actually uses, or expects or is expected to use, the product. A product
|
||||
is a consumer product regardless of whether the product has substantial
|
||||
commercial, industrial or non-consumer uses, unless such uses represent
|
||||
the only significant mode of use of the product.
|
||||
|
||||
"Installation Information" for a User Product means any methods,
|
||||
procedures, authorization keys, or other information required to install
|
||||
and execute modified versions of a covered work in that User Product from
|
||||
a modified version of its Corresponding Source. The information must
|
||||
suffice to ensure that the continued functioning of the modified object
|
||||
code is in no case prevented or interfered with solely because
|
||||
modification has been made.
|
||||
|
||||
If you convey an object code work under this section in, or with, or
|
||||
specifically for use in, a User Product, and the conveying occurs as
|
||||
part of a transaction in which the right of possession and use of the
|
||||
User Product is transferred to the recipient in perpetuity or for a
|
||||
fixed term (regardless of how the transaction is characterized), the
|
||||
Corresponding Source conveyed under this section must be accompanied
|
||||
by the Installation Information. But this requirement does not apply
|
||||
if neither you nor any third party retains the ability to install
|
||||
modified object code on the User Product (for example, the work has
|
||||
been installed in ROM).
|
||||
|
||||
The requirement to provide Installation Information does not include a
|
||||
requirement to continue to provide support service, warranty, or updates
|
||||
for a work that has been modified or installed by the recipient, or for
|
||||
the User Product in which it has been modified or installed. Access to a
|
||||
network may be denied when the modification itself materially and
|
||||
adversely affects the operation of the network or violates the rules and
|
||||
protocols for communication across the network.
|
||||
|
||||
Corresponding Source conveyed, and Installation Information provided,
|
||||
in accord with this section must be in a format that is publicly
|
||||
documented (and with an implementation available to the public in
|
||||
source code form), and must require no special password or key for
|
||||
unpacking, reading or copying.
|
||||
|
||||
7. Additional Terms.
|
||||
|
||||
"Additional permissions" are terms that supplement the terms of this
|
||||
License by making exceptions from one or more of its conditions.
|
||||
Additional permissions that are applicable to the entire Program shall
|
||||
be treated as though they were included in this License, to the extent
|
||||
that they are valid under applicable law. If additional permissions
|
||||
apply only to part of the Program, that part may be used separately
|
||||
under those permissions, but the entire Program remains governed by
|
||||
this License without regard to the additional permissions.
|
||||
|
||||
When you convey a copy of a covered work, you may at your option
|
||||
remove any additional permissions from that copy, or from any part of
|
||||
it. (Additional permissions may be written to require their own
|
||||
removal in certain cases when you modify the work.) You may place
|
||||
additional permissions on material, added by you to a covered work,
|
||||
for which you have or can give appropriate copyright permission.
|
||||
|
||||
Notwithstanding any other provision of this License, for material you
|
||||
add to a covered work, you may (if authorized by the copyright holders of
|
||||
that material) supplement the terms of this License with terms:
|
||||
|
||||
a) Disclaiming warranty or limiting liability differently from the
|
||||
terms of sections 15 and 16 of this License; or
|
||||
|
||||
b) Requiring preservation of specified reasonable legal notices or
|
||||
author attributions in that material or in the Appropriate Legal
|
||||
Notices displayed by works containing it; or
|
||||
|
||||
c) Prohibiting misrepresentation of the origin of that material, or
|
||||
requiring that modified versions of such material be marked in
|
||||
reasonable ways as different from the original version; or
|
||||
|
||||
d) Limiting the use for publicity purposes of names of licensors or
|
||||
authors of the material; or
|
||||
|
||||
e) Declining to grant rights under trademark law for use of some
|
||||
trade names, trademarks, or service marks; or
|
||||
|
||||
f) Requiring indemnification of licensors and authors of that
|
||||
material by anyone who conveys the material (or modified versions of
|
||||
it) with contractual assumptions of liability to the recipient, for
|
||||
any liability that these contractual assumptions directly impose on
|
||||
those licensors and authors.
|
||||
|
||||
All other non-permissive additional terms are considered "further
|
||||
restrictions" within the meaning of section 10. If the Program as you
|
||||
received it, or any part of it, contains a notice stating that it is
|
||||
governed by this License along with a term that is a further
|
||||
restriction, you may remove that term. If a license document contains
|
||||
a further restriction but permits relicensing or conveying under this
|
||||
License, you may add to a covered work material governed by the terms
|
||||
of that license document, provided that the further restriction does
|
||||
not survive such relicensing or conveying.
|
||||
|
||||
If you add terms to a covered work in accord with this section, you
|
||||
must place, in the relevant source files, a statement of the
|
||||
additional terms that apply to those files, or a notice indicating
|
||||
where to find the applicable terms.
|
||||
|
||||
Additional terms, permissive or non-permissive, may be stated in the
|
||||
form of a separately written license, or stated as exceptions;
|
||||
the above requirements apply either way.
|
||||
|
||||
8. Termination.
|
||||
|
||||
You may not propagate or modify a covered work except as expressly
|
||||
provided under this License. Any attempt otherwise to propagate or
|
||||
modify it is void, and will automatically terminate your rights under
|
||||
this License (including any patent licenses granted under the third
|
||||
paragraph of section 11).
|
||||
|
||||
However, if you cease all violation of this License, then your
|
||||
license from a particular copyright holder is reinstated (a)
|
||||
provisionally, unless and until the copyright holder explicitly and
|
||||
finally terminates your license, and (b) permanently, if the copyright
|
||||
holder fails to notify you of the violation by some reasonable means
|
||||
prior to 60 days after the cessation.
|
||||
|
||||
Moreover, your license from a particular copyright holder is
|
||||
reinstated permanently if the copyright holder notifies you of the
|
||||
violation by some reasonable means, this is the first time you have
|
||||
received notice of violation of this License (for any work) from that
|
||||
copyright holder, and you cure the violation prior to 30 days after
|
||||
your receipt of the notice.
|
||||
|
||||
Termination of your rights under this section does not terminate the
|
||||
licenses of parties who have received copies or rights from you under
|
||||
this License. If your rights have been terminated and not permanently
|
||||
reinstated, you do not qualify to receive new licenses for the same
|
||||
material under section 10.
|
||||
|
||||
9. Acceptance Not Required for Having Copies.
|
||||
|
||||
You are not required to accept this License in order to receive or
|
||||
run a copy of the Program. Ancillary propagation of a covered work
|
||||
occurring solely as a consequence of using peer-to-peer transmission
|
||||
to receive a copy likewise does not require acceptance. However,
|
||||
nothing other than this License grants you permission to propagate or
|
||||
modify any covered work. These actions infringe copyright if you do
|
||||
not accept this License. Therefore, by modifying or propagating a
|
||||
covered work, you indicate your acceptance of this License to do so.
|
||||
|
||||
10. Automatic Licensing of Downstream Recipients.
|
||||
|
||||
Each time you convey a covered work, the recipient automatically
|
||||
receives a license from the original licensors, to run, modify and
|
||||
propagate that work, subject to this License. You are not responsible
|
||||
for enforcing compliance by third parties with this License.
|
||||
|
||||
An "entity transaction" is a transaction transferring control of an
|
||||
organization, or substantially all assets of one, or subdividing an
|
||||
organization, or merging organizations. If propagation of a covered
|
||||
work results from an entity transaction, each party to that
|
||||
transaction who receives a copy of the work also receives whatever
|
||||
licenses to the work the party's predecessor in interest had or could
|
||||
give under the previous paragraph, plus a right to possession of the
|
||||
Corresponding Source of the work from the predecessor in interest, if
|
||||
the predecessor has it or can get it with reasonable efforts.
|
||||
|
||||
You may not impose any further restrictions on the exercise of the
|
||||
rights granted or affirmed under this License. For example, you may
|
||||
not impose a license fee, royalty, or other charge for exercise of
|
||||
rights granted under this License, and you may not initiate litigation
|
||||
(including a cross-claim or counterclaim in a lawsuit) alleging that
|
||||
any patent claim is infringed by making, using, selling, offering for
|
||||
sale, or importing the Program or any portion of it.
|
||||
|
||||
11. Patents.
|
||||
|
||||
A "contributor" is a copyright holder who authorizes use under this
|
||||
License of the Program or a work on which the Program is based. The
|
||||
work thus licensed is called the contributor's "contributor version".
|
||||
|
||||
A contributor's "essential patent claims" are all patent claims
|
||||
owned or controlled by the contributor, whether already acquired or
|
||||
hereafter acquired, that would be infringed by some manner, permitted
|
||||
by this License, of making, using, or selling its contributor version,
|
||||
but do not include claims that would be infringed only as a
|
||||
consequence of further modification of the contributor version. For
|
||||
purposes of this definition, "control" includes the right to grant
|
||||
patent sublicenses in a manner consistent with the requirements of
|
||||
this License.
|
||||
|
||||
Each contributor grants you a non-exclusive, worldwide, royalty-free
|
||||
patent license under the contributor's essential patent claims, to
|
||||
make, use, sell, offer for sale, import and otherwise run, modify and
|
||||
propagate the contents of its contributor version.
|
||||
|
||||
In the following three paragraphs, a "patent license" is any express
|
||||
agreement or commitment, however denominated, not to enforce a patent
|
||||
(such as an express permission to practice a patent or covenant not to
|
||||
sue for patent infringement). To "grant" such a patent license to a
|
||||
party means to make such an agreement or commitment not to enforce a
|
||||
patent against the party.
|
||||
|
||||
If you convey a covered work, knowingly relying on a patent license,
|
||||
and the Corresponding Source of the work is not available for anyone
|
||||
to copy, free of charge and under the terms of this License, through a
|
||||
publicly available network server or other readily accessible means,
|
||||
then you must either (1) cause the Corresponding Source to be so
|
||||
available, or (2) arrange to deprive yourself of the benefit of the
|
||||
patent license for this particular work, or (3) arrange, in a manner
|
||||
consistent with the requirements of this License, to extend the patent
|
||||
license to downstream recipients. "Knowingly relying" means you have
|
||||
actual knowledge that, but for the patent license, your conveying the
|
||||
covered work in a country, or your recipient's use of the covered work
|
||||
in a country, would infringe one or more identifiable patents in that
|
||||
country that you have reason to believe are valid.
|
||||
|
||||
If, pursuant to or in connection with a single transaction or
|
||||
arrangement, you convey, or propagate by procuring conveyance of, a
|
||||
covered work, and grant a patent license to some of the parties
|
||||
receiving the covered work authorizing them to use, propagate, modify
|
||||
or convey a specific copy of the covered work, then the patent license
|
||||
you grant is automatically extended to all recipients of the covered
|
||||
work and works based on it.
|
||||
|
||||
A patent license is "discriminatory" if it does not include within
|
||||
the scope of its coverage, prohibits the exercise of, or is
|
||||
conditioned on the non-exercise of one or more of the rights that are
|
||||
specifically granted under this License. You may not convey a covered
|
||||
work if you are a party to an arrangement with a third party that is
|
||||
in the business of distributing software, under which you make payment
|
||||
to the third party based on the extent of your activity of conveying
|
||||
the work, and under which the third party grants, to any of the
|
||||
parties who would receive the covered work from you, a discriminatory
|
||||
patent license (a) in connection with copies of the covered work
|
||||
conveyed by you (or copies made from those copies), or (b) primarily
|
||||
for and in connection with specific products or compilations that
|
||||
contain the covered work, unless you entered into that arrangement,
|
||||
or that patent license was granted, prior to 28 March 2007.
|
||||
|
||||
Nothing in this License shall be construed as excluding or limiting
|
||||
any implied license or other defenses to infringement that may
|
||||
otherwise be available to you under applicable patent law.
|
||||
|
||||
12. No Surrender of Others' Freedom.
|
||||
|
||||
If conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot convey a
|
||||
covered work so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you may
|
||||
not convey it at all. For example, if you agree to terms that obligate you
|
||||
to collect a royalty for further conveying from those to whom you convey
|
||||
the Program, the only way you could satisfy both those terms and this
|
||||
License would be to refrain entirely from conveying the Program.
|
||||
|
||||
13. Remote Network Interaction; Use with the GNU General Public License.
|
||||
|
||||
Notwithstanding any other provision of this License, if you modify the
|
||||
Program, your modified version must prominently offer all users
|
||||
interacting with it remotely through a computer network (if your version
|
||||
supports such interaction) an opportunity to receive the Corresponding
|
||||
Source of your version by providing access to the Corresponding Source
|
||||
from a network server at no charge, through some standard or customary
|
||||
means of facilitating copying of software. This Corresponding Source
|
||||
shall include the Corresponding Source for any work covered by version 3
|
||||
of the GNU General Public License that is incorporated pursuant to the
|
||||
following paragraph.
|
||||
|
||||
Notwithstanding any other provision of this License, you have
|
||||
permission to link or combine any covered work with a work licensed
|
||||
under version 3 of the GNU General Public License into a single
|
||||
combined work, and to convey the resulting work. The terms of this
|
||||
License will continue to apply to the part which is the covered work,
|
||||
but the work with which it is combined will remain governed by version
|
||||
3 of the GNU General Public License.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU Affero General Public License from time to time. Such new versions
|
||||
will be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the
|
||||
Program specifies that a certain numbered version of the GNU Affero General
|
||||
Public License "or any later version" applies to it, you have the
|
||||
option of following the terms and conditions either of that numbered
|
||||
version or of any later version published by the Free Software
|
||||
Foundation. If the Program does not specify a version number of the
|
||||
GNU Affero General Public License, you may choose any version ever published
|
||||
by the Free Software Foundation.
|
||||
|
||||
If the Program specifies that a proxy can decide which future
|
||||
versions of the GNU Affero General Public License can be used, that proxy's
|
||||
public statement of acceptance of a version permanently authorizes you
|
||||
to choose that version for the Program.
|
||||
|
||||
Later license versions may give you additional or different
|
||||
permissions. However, no additional obligations are imposed on any
|
||||
author or copyright holder as a result of your choosing to follow a
|
||||
later version.
|
||||
|
||||
15. Disclaimer of Warranty.
|
||||
|
||||
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
|
||||
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
|
||||
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
|
||||
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
|
||||
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
|
||||
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
|
||||
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. Limitation of Liability.
|
||||
|
||||
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
|
||||
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
|
||||
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
|
||||
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
|
||||
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
|
||||
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
|
||||
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGES.
|
||||
|
||||
17. Interpretation of Sections 15 and 16.
|
||||
|
||||
If the disclaimer of warranty and limitation of liability provided
|
||||
above cannot be given local legal effect according to their terms,
|
||||
reviewing courts shall apply local law that most closely approximates
|
||||
an absolute waiver of all civil liability in connection with the
|
||||
Program, unless a warranty or assumption of liability accompanies a
|
||||
copy of the Program in return for a fee.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
state the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
Pleroma
|
||||
Copyright (C) 2017 Roger Braun
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If your software can interact with users remotely through a computer
|
||||
network, you should also make sure that it provides a way for users to
|
||||
get its source. For example, if your program is a web application, its
|
||||
interface could display a "Source" link that leads users to an archive
|
||||
of the code. There are many ways you could offer source, and different
|
||||
solutions will be better for different programs; see section 13 for the
|
||||
specific requirements.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or school,
|
||||
if any, to sign a "copyright disclaimer" for the program, if necessary.
|
||||
For more information on this, and how to apply and follow the GNU AGPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
395
CC-BY-4.0
Normal file
395
CC-BY-4.0
Normal file
@ -0,0 +1,395 @@
|
||||
Attribution 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution 4.0 International Public License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution 4.0 International Public License ("Public License"). To the
|
||||
extent this Public License may be interpreted as a contract, You are
|
||||
granted the Licensed Rights in consideration of Your acceptance of
|
||||
these terms and conditions, and the Licensor grants You such rights in
|
||||
consideration of benefits the Licensor receives from making the
|
||||
Licensed Material available under these terms and conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
d. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
e. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
f. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
g. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
h. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
i. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
j. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
k. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
4. If You Share Adapted Material You produce, the Adapter's
|
||||
License You apply must not prevent recipients of the Adapted
|
||||
Material from complying with this Public License.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material; and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
427
CC-BY-SA-4.0
Normal file
427
CC-BY-SA-4.0
Normal file
@ -0,0 +1,427 @@
|
||||
Attribution-ShareAlike 4.0 International
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Corporation ("Creative Commons") is not a law firm and
|
||||
does not provide legal services or legal advice. Distribution of
|
||||
Creative Commons public licenses does not create a lawyer-client or
|
||||
other relationship. Creative Commons makes its licenses and related
|
||||
information available on an "as-is" basis. Creative Commons gives no
|
||||
warranties regarding its licenses, any material licensed under their
|
||||
terms and conditions, or any related information. Creative Commons
|
||||
disclaims all liability for damages resulting from their use to the
|
||||
fullest extent possible.
|
||||
|
||||
Using Creative Commons Public Licenses
|
||||
|
||||
Creative Commons public licenses provide a standard set of terms and
|
||||
conditions that creators and other rights holders may use to share
|
||||
original works of authorship and other material subject to copyright
|
||||
and certain other rights specified in the public license below. The
|
||||
following considerations are for informational purposes only, are not
|
||||
exhaustive, and do not form part of our licenses.
|
||||
|
||||
Considerations for licensors: Our public licenses are
|
||||
intended for use by those authorized to give the public
|
||||
permission to use material in ways otherwise restricted by
|
||||
copyright and certain other rights. Our licenses are
|
||||
irrevocable. Licensors should read and understand the terms
|
||||
and conditions of the license they choose before applying it.
|
||||
Licensors should also secure all rights necessary before
|
||||
applying our licenses so that the public can reuse the
|
||||
material as expected. Licensors should clearly mark any
|
||||
material not subject to the license. This includes other CC-
|
||||
licensed material, or material used under an exception or
|
||||
limitation to copyright. More considerations for licensors:
|
||||
wiki.creativecommons.org/Considerations_for_licensors
|
||||
|
||||
Considerations for the public: By using one of our public
|
||||
licenses, a licensor grants the public permission to use the
|
||||
licensed material under specified terms and conditions. If
|
||||
the licensor's permission is not necessary for any reason--for
|
||||
example, because of any applicable exception or limitation to
|
||||
copyright--then that use is not regulated by the license. Our
|
||||
licenses grant only permissions under copyright and certain
|
||||
other rights that a licensor has authority to grant. Use of
|
||||
the licensed material may still be restricted for other
|
||||
reasons, including because others have copyright or other
|
||||
rights in the material. A licensor may make special requests,
|
||||
such as asking that all changes be marked or described.
|
||||
Although not required by our licenses, you are encouraged to
|
||||
respect those requests where reasonable. More considerations
|
||||
for the public:
|
||||
wiki.creativecommons.org/Considerations_for_licensees
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons Attribution-ShareAlike 4.0 International Public
|
||||
License
|
||||
|
||||
By exercising the Licensed Rights (defined below), You accept and agree
|
||||
to be bound by the terms and conditions of this Creative Commons
|
||||
Attribution-ShareAlike 4.0 International Public License ("Public
|
||||
License"). To the extent this Public License may be interpreted as a
|
||||
contract, You are granted the Licensed Rights in consideration of Your
|
||||
acceptance of these terms and conditions, and the Licensor grants You
|
||||
such rights in consideration of benefits the Licensor receives from
|
||||
making the Licensed Material available under these terms and
|
||||
conditions.
|
||||
|
||||
|
||||
Section 1 -- Definitions.
|
||||
|
||||
a. Adapted Material means material subject to Copyright and Similar
|
||||
Rights that is derived from or based upon the Licensed Material
|
||||
and in which the Licensed Material is translated, altered,
|
||||
arranged, transformed, or otherwise modified in a manner requiring
|
||||
permission under the Copyright and Similar Rights held by the
|
||||
Licensor. For purposes of this Public License, where the Licensed
|
||||
Material is a musical work, performance, or sound recording,
|
||||
Adapted Material is always produced where the Licensed Material is
|
||||
synched in timed relation with a moving image.
|
||||
|
||||
b. Adapter's License means the license You apply to Your Copyright
|
||||
and Similar Rights in Your contributions to Adapted Material in
|
||||
accordance with the terms and conditions of this Public License.
|
||||
|
||||
c. BY-SA Compatible License means a license listed at
|
||||
creativecommons.org/compatiblelicenses, approved by Creative
|
||||
Commons as essentially the equivalent of this Public License.
|
||||
|
||||
d. Copyright and Similar Rights means copyright and/or similar rights
|
||||
closely related to copyright including, without limitation,
|
||||
performance, broadcast, sound recording, and Sui Generis Database
|
||||
Rights, without regard to how the rights are labeled or
|
||||
categorized. For purposes of this Public License, the rights
|
||||
specified in Section 2(b)(1)-(2) are not Copyright and Similar
|
||||
Rights.
|
||||
|
||||
e. Effective Technological Measures means those measures that, in the
|
||||
absence of proper authority, may not be circumvented under laws
|
||||
fulfilling obligations under Article 11 of the WIPO Copyright
|
||||
Treaty adopted on December 20, 1996, and/or similar international
|
||||
agreements.
|
||||
|
||||
f. Exceptions and Limitations means fair use, fair dealing, and/or
|
||||
any other exception or limitation to Copyright and Similar Rights
|
||||
that applies to Your use of the Licensed Material.
|
||||
|
||||
g. License Elements means the license attributes listed in the name
|
||||
of a Creative Commons Public License. The License Elements of this
|
||||
Public License are Attribution and ShareAlike.
|
||||
|
||||
h. Licensed Material means the artistic or literary work, database,
|
||||
or other material to which the Licensor applied this Public
|
||||
License.
|
||||
|
||||
i. Licensed Rights means the rights granted to You subject to the
|
||||
terms and conditions of this Public License, which are limited to
|
||||
all Copyright and Similar Rights that apply to Your use of the
|
||||
Licensed Material and that the Licensor has authority to license.
|
||||
|
||||
j. Licensor means the individual(s) or entity(ies) granting rights
|
||||
under this Public License.
|
||||
|
||||
k. Share means to provide material to the public by any means or
|
||||
process that requires permission under the Licensed Rights, such
|
||||
as reproduction, public display, public performance, distribution,
|
||||
dissemination, communication, or importation, and to make material
|
||||
available to the public including in ways that members of the
|
||||
public may access the material from a place and at a time
|
||||
individually chosen by them.
|
||||
|
||||
l. Sui Generis Database Rights means rights other than copyright
|
||||
resulting from Directive 96/9/EC of the European Parliament and of
|
||||
the Council of 11 March 1996 on the legal protection of databases,
|
||||
as amended and/or succeeded, as well as other essentially
|
||||
equivalent rights anywhere in the world.
|
||||
|
||||
m. You means the individual or entity exercising the Licensed Rights
|
||||
under this Public License. Your has a corresponding meaning.
|
||||
|
||||
|
||||
Section 2 -- Scope.
|
||||
|
||||
a. License grant.
|
||||
|
||||
1. Subject to the terms and conditions of this Public License,
|
||||
the Licensor hereby grants You a worldwide, royalty-free,
|
||||
non-sublicensable, non-exclusive, irrevocable license to
|
||||
exercise the Licensed Rights in the Licensed Material to:
|
||||
|
||||
a. reproduce and Share the Licensed Material, in whole or
|
||||
in part; and
|
||||
|
||||
b. produce, reproduce, and Share Adapted Material.
|
||||
|
||||
2. Exceptions and Limitations. For the avoidance of doubt, where
|
||||
Exceptions and Limitations apply to Your use, this Public
|
||||
License does not apply, and You do not need to comply with
|
||||
its terms and conditions.
|
||||
|
||||
3. Term. The term of this Public License is specified in Section
|
||||
6(a).
|
||||
|
||||
4. Media and formats; technical modifications allowed. The
|
||||
Licensor authorizes You to exercise the Licensed Rights in
|
||||
all media and formats whether now known or hereafter created,
|
||||
and to make technical modifications necessary to do so. The
|
||||
Licensor waives and/or agrees not to assert any right or
|
||||
authority to forbid You from making technical modifications
|
||||
necessary to exercise the Licensed Rights, including
|
||||
technical modifications necessary to circumvent Effective
|
||||
Technological Measures. For purposes of this Public License,
|
||||
simply making modifications authorized by this Section 2(a)
|
||||
(4) never produces Adapted Material.
|
||||
|
||||
5. Downstream recipients.
|
||||
|
||||
a. Offer from the Licensor -- Licensed Material. Every
|
||||
recipient of the Licensed Material automatically
|
||||
receives an offer from the Licensor to exercise the
|
||||
Licensed Rights under the terms and conditions of this
|
||||
Public License.
|
||||
|
||||
b. Additional offer from the Licensor -- Adapted Material.
|
||||
Every recipient of Adapted Material from You
|
||||
automatically receives an offer from the Licensor to
|
||||
exercise the Licensed Rights in the Adapted Material
|
||||
under the conditions of the Adapter's License You apply.
|
||||
|
||||
c. No downstream restrictions. You may not offer or impose
|
||||
any additional or different terms or conditions on, or
|
||||
apply any Effective Technological Measures to, the
|
||||
Licensed Material if doing so restricts exercise of the
|
||||
Licensed Rights by any recipient of the Licensed
|
||||
Material.
|
||||
|
||||
6. No endorsement. Nothing in this Public License constitutes or
|
||||
may be construed as permission to assert or imply that You
|
||||
are, or that Your use of the Licensed Material is, connected
|
||||
with, or sponsored, endorsed, or granted official status by,
|
||||
the Licensor or others designated to receive attribution as
|
||||
provided in Section 3(a)(1)(A)(i).
|
||||
|
||||
b. Other rights.
|
||||
|
||||
1. Moral rights, such as the right of integrity, are not
|
||||
licensed under this Public License, nor are publicity,
|
||||
privacy, and/or other similar personality rights; however, to
|
||||
the extent possible, the Licensor waives and/or agrees not to
|
||||
assert any such rights held by the Licensor to the limited
|
||||
extent necessary to allow You to exercise the Licensed
|
||||
Rights, but not otherwise.
|
||||
|
||||
2. Patent and trademark rights are not licensed under this
|
||||
Public License.
|
||||
|
||||
3. To the extent possible, the Licensor waives any right to
|
||||
collect royalties from You for the exercise of the Licensed
|
||||
Rights, whether directly or through a collecting society
|
||||
under any voluntary or waivable statutory or compulsory
|
||||
licensing scheme. In all other cases the Licensor expressly
|
||||
reserves any right to collect such royalties.
|
||||
|
||||
|
||||
Section 3 -- License Conditions.
|
||||
|
||||
Your exercise of the Licensed Rights is expressly made subject to the
|
||||
following conditions.
|
||||
|
||||
a. Attribution.
|
||||
|
||||
1. If You Share the Licensed Material (including in modified
|
||||
form), You must:
|
||||
|
||||
a. retain the following if it is supplied by the Licensor
|
||||
with the Licensed Material:
|
||||
|
||||
i. identification of the creator(s) of the Licensed
|
||||
Material and any others designated to receive
|
||||
attribution, in any reasonable manner requested by
|
||||
the Licensor (including by pseudonym if
|
||||
designated);
|
||||
|
||||
ii. a copyright notice;
|
||||
|
||||
iii. a notice that refers to this Public License;
|
||||
|
||||
iv. a notice that refers to the disclaimer of
|
||||
warranties;
|
||||
|
||||
v. a URI or hyperlink to the Licensed Material to the
|
||||
extent reasonably practicable;
|
||||
|
||||
b. indicate if You modified the Licensed Material and
|
||||
retain an indication of any previous modifications; and
|
||||
|
||||
c. indicate the Licensed Material is licensed under this
|
||||
Public License, and include the text of, or the URI or
|
||||
hyperlink to, this Public License.
|
||||
|
||||
2. You may satisfy the conditions in Section 3(a)(1) in any
|
||||
reasonable manner based on the medium, means, and context in
|
||||
which You Share the Licensed Material. For example, it may be
|
||||
reasonable to satisfy the conditions by providing a URI or
|
||||
hyperlink to a resource that includes the required
|
||||
information.
|
||||
|
||||
3. If requested by the Licensor, You must remove any of the
|
||||
information required by Section 3(a)(1)(A) to the extent
|
||||
reasonably practicable.
|
||||
|
||||
b. ShareAlike.
|
||||
|
||||
In addition to the conditions in Section 3(a), if You Share
|
||||
Adapted Material You produce, the following conditions also apply.
|
||||
|
||||
1. The Adapter's License You apply must be a Creative Commons
|
||||
license with the same License Elements, this version or
|
||||
later, or a BY-SA Compatible License.
|
||||
|
||||
2. You must include the text of, or the URI or hyperlink to, the
|
||||
Adapter's License You apply. You may satisfy this condition
|
||||
in any reasonable manner based on the medium, means, and
|
||||
context in which You Share Adapted Material.
|
||||
|
||||
3. You may not offer or impose any additional or different terms
|
||||
or conditions on, or apply any Effective Technological
|
||||
Measures to, Adapted Material that restrict exercise of the
|
||||
rights granted under the Adapter's License You apply.
|
||||
|
||||
|
||||
Section 4 -- Sui Generis Database Rights.
|
||||
|
||||
Where the Licensed Rights include Sui Generis Database Rights that
|
||||
apply to Your use of the Licensed Material:
|
||||
|
||||
a. for the avoidance of doubt, Section 2(a)(1) grants You the right
|
||||
to extract, reuse, reproduce, and Share all or a substantial
|
||||
portion of the contents of the database;
|
||||
|
||||
b. if You include all or a substantial portion of the database
|
||||
contents in a database in which You have Sui Generis Database
|
||||
Rights, then the database in which You have Sui Generis Database
|
||||
Rights (but not its individual contents) is Adapted Material,
|
||||
including for purposes of Section 3(b); and
|
||||
|
||||
c. You must comply with the conditions in Section 3(a) if You Share
|
||||
all or a substantial portion of the contents of the database.
|
||||
|
||||
For the avoidance of doubt, this Section 4 supplements and does not
|
||||
replace Your obligations under this Public License where the Licensed
|
||||
Rights include other Copyright and Similar Rights.
|
||||
|
||||
|
||||
Section 5 -- Disclaimer of Warranties and Limitation of Liability.
|
||||
|
||||
a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
|
||||
EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
|
||||
AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
|
||||
ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
|
||||
IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
|
||||
WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
|
||||
ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
|
||||
KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
|
||||
ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
|
||||
|
||||
b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
|
||||
TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
|
||||
NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
|
||||
INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
|
||||
COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
|
||||
USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
|
||||
ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
|
||||
DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
|
||||
IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
|
||||
|
||||
c. The disclaimer of warranties and limitation of liability provided
|
||||
above shall be interpreted in a manner that, to the extent
|
||||
possible, most closely approximates an absolute disclaimer and
|
||||
waiver of all liability.
|
||||
|
||||
|
||||
Section 6 -- Term and Termination.
|
||||
|
||||
a. This Public License applies for the term of the Copyright and
|
||||
Similar Rights licensed here. However, if You fail to comply with
|
||||
this Public License, then Your rights under this Public License
|
||||
terminate automatically.
|
||||
|
||||
b. Where Your right to use the Licensed Material has terminated under
|
||||
Section 6(a), it reinstates:
|
||||
|
||||
1. automatically as of the date the violation is cured, provided
|
||||
it is cured within 30 days of Your discovery of the
|
||||
violation; or
|
||||
|
||||
2. upon express reinstatement by the Licensor.
|
||||
|
||||
For the avoidance of doubt, this Section 6(b) does not affect any
|
||||
right the Licensor may have to seek remedies for Your violations
|
||||
of this Public License.
|
||||
|
||||
c. For the avoidance of doubt, the Licensor may also offer the
|
||||
Licensed Material under separate terms or conditions or stop
|
||||
distributing the Licensed Material at any time; however, doing so
|
||||
will not terminate this Public License.
|
||||
|
||||
d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
|
||||
License.
|
||||
|
||||
|
||||
Section 7 -- Other Terms and Conditions.
|
||||
|
||||
a. The Licensor shall not be bound by any additional or different
|
||||
terms or conditions communicated by You unless expressly agreed.
|
||||
|
||||
b. Any arrangements, understandings, or agreements regarding the
|
||||
Licensed Material not stated herein are separate from and
|
||||
independent of the terms and conditions of this Public License.
|
||||
|
||||
|
||||
Section 8 -- Interpretation.
|
||||
|
||||
a. For the avoidance of doubt, this Public License does not, and
|
||||
shall not be interpreted to, reduce, limit, restrict, or impose
|
||||
conditions on any use of the Licensed Material that could lawfully
|
||||
be made without permission under this Public License.
|
||||
|
||||
b. To the extent possible, if any provision of this Public License is
|
||||
deemed unenforceable, it shall be automatically reformed to the
|
||||
minimum extent necessary to make it enforceable. If the provision
|
||||
cannot be reformed, it shall be severed from this Public License
|
||||
without affecting the enforceability of the remaining terms and
|
||||
conditions.
|
||||
|
||||
c. No term or condition of this Public License will be waived and no
|
||||
failure to comply consented to unless expressly agreed to by the
|
||||
Licensor.
|
||||
|
||||
d. Nothing in this Public License constitutes or may be interpreted
|
||||
as a limitation upon, or waiver of, any privileges and immunities
|
||||
that apply to the Licensor or You, including from the legal
|
||||
processes of any jurisdiction or authority.
|
||||
|
||||
|
||||
=======================================================================
|
||||
|
||||
Creative Commons is not a party to its public
|
||||
licenses. Notwithstanding, Creative Commons may elect to apply one of
|
||||
its public licenses to material it publishes and in those instances
|
||||
will be considered the “Licensor.” The text of the Creative Commons
|
||||
public licenses is dedicated to the public domain under the CC0 Public
|
||||
Domain Dedication. Except for the limited purpose of indicating that
|
||||
material is shared under a Creative Commons public license or as
|
||||
otherwise permitted by the Creative Commons policies published at
|
||||
creativecommons.org/policies, Creative Commons does not authorize the
|
||||
use of the trademark "Creative Commons" or any other trademark or logo
|
||||
of Creative Commons without its prior written consent including,
|
||||
without limitation, in connection with any unauthorized modifications
|
||||
to any of its public licenses or any other arrangements,
|
||||
understandings, or agreements concerning use of licensed material. For
|
||||
the avoidance of doubt, this paragraph does not form part of the
|
||||
public licenses.
|
||||
|
||||
Creative Commons may be contacted at creativecommons.org.
|
1383
CHANGELOG.md
Normal file
1383
CHANGELOG.md
Normal file
File diff suppressed because it is too large
Load Diff
66
CHANGELOG_soapbox.md
Normal file
66
CHANGELOG_soapbox.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
This file is only for changes to Soapbox.
|
||||
For changes to Pleroma, see `CHANGELOG.md`
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
|
||||
## Unreleased
|
||||
|
||||
- Added `AntiDuplicationPolicy` and `AntiMentionSpamPolicy` for spam.
|
||||
|
||||
### Fixed
|
||||
- Link previews not working for some websites. Let rich media user-agent be configurable (with `:pleroma, :rich_media, user_agent: "whatever"`).
|
||||
|
||||
## [3.0.0] - 2022-05-24
|
||||
|
||||
Based on Pleroma 2.4 develop.
|
||||
|
||||
TODO: a full changelog
|
||||
|
||||
### Added
|
||||
|
||||
- Events
|
||||
- Translations
|
||||
|
||||
## [1.1.1] - 2021-05-21
|
||||
|
||||
Based on Pleroma 2.3.0-stable.
|
||||
|
||||
### Fixed
|
||||
- Broken frontend due to not all files being in the repo.
|
||||
|
||||
## [1.1.0] - 2021-05-18
|
||||
|
||||
Based on Pleroma 2.3.0-stable.
|
||||
|
||||
### Added
|
||||
- Retain uploaded image aspect ratios. ([!18](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/18))
|
||||
- Blurhash support. ([!21](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/21))
|
||||
|
||||
### Fixed
|
||||
- Rich media not working for certain links. ([!19](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/19), [!20](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/20))
|
||||
|
||||
### Changed
|
||||
- Prepared installation files for use with asdf version manager. ([!22](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/22), [!23](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/23), [!24](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/24))
|
||||
- **BREAKING:** `imagemagick` and `exiftool` are required to be installed on the system.
|
||||
|
||||
## [1.0.0] - 2021-05-11
|
||||
|
||||
Based on Pleroma 2.3.0-stable.
|
||||
|
||||
### Added
|
||||
- Rich media embeds for sites like YouTube, etc. ([!13](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/13))
|
||||
- Twitter-like block behavior, configured under "ActivityPub > Blockers visible" in AdminFE. ([!9](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/9))
|
||||
- The Soapbox version in `/api/v1/instance` ([!6](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/6))
|
||||
|
||||
### Changed
|
||||
- Soapbox FE is set as the default frontend. ([!16](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/16))
|
||||
- Twitter-like block behavior is now the default. ([!9](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/9))
|
||||
|
||||
### Fixed
|
||||
- Domain blocks: reposts from a blocked domain are now correctly blocked. ([!11](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/11))
|
||||
- Fixed some (not all) Markdown issues, such as broken trailing slash in links. ([!10](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/10))
|
||||
- Don't crash so hard when email settings are invalid. ([!12](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/12))
|
||||
- Return OpenGraph metadata on Soapbox FE routes. ([!14](https://gitlab.com/soapbox-pub/soapbox/-/merge_requests/14))
|
56
COPYING
Normal file
56
COPYING
Normal file
@ -0,0 +1,56 @@
|
||||
Unless otherwise stated this repository is copyright © 2017-2022
|
||||
Pleroma Authors <https://pleroma.social/>, and is distributed under
|
||||
The GNU Affero General Public License Version 3, you should have received a
|
||||
copy of the license file as AGPL-3.
|
||||
|
||||
---
|
||||
|
||||
Files inside docs directory are copyright © 2022 Pleroma Authors
|
||||
<https://pleroma.social/>, and are distributed under the Creative Commons
|
||||
Attribution 4.0 International license, you should have received
|
||||
a copy of the license file as CC-BY-4.0.
|
||||
|
||||
---
|
||||
|
||||
The following files are copyright © 2019 shitposter.club, and are distributed
|
||||
under the Creative Commons Attribution-ShareAlike 4.0 International license,
|
||||
you should have received a copy of the license file as CC-BY-SA-4.0.
|
||||
|
||||
priv/static/images/pleroma-fox-tan.png
|
||||
priv/static/images/pleroma-fox-tan-smol.png
|
||||
priv/static/images/pleroma-tan.png
|
||||
|
||||
---
|
||||
|
||||
The following files are copyright © 2019 shitposter.club, and are distributed
|
||||
under the Creative Commons Attribution 4.0 International license, you should
|
||||
have received a copy of the license file as CC-BY-4.0.
|
||||
|
||||
priv/static/images/pleroma-fox-tan-shy.png
|
||||
|
||||
---
|
||||
|
||||
The following files are copyright © 2017-2022 Pleroma Authors
|
||||
<https://pleroma.social/>, and are distributed under the Creative Commons
|
||||
Attribution-ShareAlike 4.0 International license, you should have received
|
||||
a copy of the license file as CC-BY-SA-4.0.
|
||||
|
||||
priv/static/images/avi.png
|
||||
priv/static/images/banner.png
|
||||
priv/static/instance/thumbnail.jpeg
|
||||
|
||||
---
|
||||
|
||||
All photos published on Unsplash can be used for free. You can use them for
|
||||
commercial and noncommercial purposes. You do not need to ask permission from
|
||||
or provide credit to the photographer or Unsplash, although it is appreciated
|
||||
when possible.
|
||||
|
||||
More precisely, Unsplash grants you an irrevocable, nonexclusive, worldwide
|
||||
copyright license to download, copy, modify, distribute, perform, and use
|
||||
photos from Unsplash for free, including for commercial purposes, without
|
||||
permission from or attributing the photographer or Unsplash. This license
|
||||
does not include the right to compile photos from Unsplash to replicate
|
||||
a similar or competing service.
|
||||
|
||||
priv/static/images/city.jpg
|
61
Dockerfile
Normal file
61
Dockerfile
Normal file
@ -0,0 +1,61 @@
|
||||
FROM ubuntu:22.04 as build
|
||||
|
||||
ARG MIX_ENV=prod \
|
||||
OAUTH_CONSUMER_STRATEGIES="twitter facebook google microsoft slack github keycloak:ueberauth_keycloak_strategy"
|
||||
|
||||
WORKDIR /src
|
||||
|
||||
RUN apt-get update &&\
|
||||
apt-get install -y git elixir erlang-dev erlang-nox build-essential cmake libssl-dev libmagic-dev automake autoconf libncurses5-dev &&\
|
||||
mix local.hex --force &&\
|
||||
mix local.rebar --force
|
||||
|
||||
COPY . /src
|
||||
|
||||
RUN cd /src &&\
|
||||
mix deps.get --only prod &&\
|
||||
mkdir release &&\
|
||||
mix release --path release
|
||||
|
||||
FROM ubuntu:22.04
|
||||
|
||||
ARG BUILD_DATE
|
||||
ARG VCS_REF
|
||||
|
||||
ARG DEBIAN_FRONTEND="noninteractive"
|
||||
ENV TZ="Etc/UTC"
|
||||
|
||||
LABEL maintainer="hello@soapbox.pub" \
|
||||
org.opencontainers.image.title="rebased" \
|
||||
org.opencontainers.image.description="Rebased" \
|
||||
org.opencontainers.image.authors="hello@soapbox.pub" \
|
||||
org.opencontainers.image.vendor="soapbox.pub" \
|
||||
org.opencontainers.image.documentation="https://gitlab.com/soapbox-pub/rebased" \
|
||||
org.opencontainers.image.licenses="AGPL-3.0" \
|
||||
org.opencontainers.image.url="https://soapbox.pub" \
|
||||
org.opencontainers.image.revision=$VCS_REF \
|
||||
org.opencontainers.image.created=$BUILD_DATE
|
||||
|
||||
ARG HOME=/opt/pleroma
|
||||
ARG DATA=/var/lib/pleroma
|
||||
|
||||
RUN apt-get update &&\
|
||||
apt-get install -y --no-install-recommends curl ca-certificates imagemagick libmagic-dev ffmpeg libimage-exiftool-perl libncurses5 postgresql-client fasttext &&\
|
||||
adduser --system --shell /bin/false --home ${HOME} pleroma &&\
|
||||
mkdir -p ${DATA}/uploads &&\
|
||||
mkdir -p ${DATA}/static &&\
|
||||
chown -R pleroma ${DATA} &&\
|
||||
mkdir -p /etc/pleroma &&\
|
||||
chown -R pleroma /etc/pleroma &&\
|
||||
mkdir -p /usr/share/fasttext &&\
|
||||
curl -L https://dl.fbaipublicfiles.com/fasttext/supervised-models/lid.176.ftz -o /usr/share/fasttext/lid.176.ftz &&\
|
||||
chmod 0644 /usr/share/fasttext/lid.176.ftz
|
||||
|
||||
USER pleroma
|
||||
|
||||
COPY --from=build --chown=pleroma:0 /src/release ${HOME}
|
||||
|
||||
COPY --chown=pleroma --chmod=640 ./config/docker.exs /etc/pleroma/config.exs
|
||||
COPY ./docker-entrypoint.sh ${HOME}
|
||||
|
||||
ENTRYPOINT ["/opt/pleroma/docker-entrypoint.sh"]
|
33
README.md
Normal file
33
README.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Rebased
|
||||
|
||||

|
||||
|
||||
**Rebased** is a Fediverse backend written in Elixir.
|
||||
It's compatible with the Mastodon API and is the recommended backend for Soapbox.
|
||||
|
||||
## Your social media server
|
||||
|
||||
Rebased empowers people to take control of their social media experience.
|
||||
Hosting your own server means that *you* get to decide the rules.
|
||||
|
||||
Rebased connects to over 4,000 other servers on the Fediverse.
|
||||
It is designed to spread your message far and wide, while being resilient to deplatforming.
|
||||
|
||||
## Installation
|
||||
|
||||
See [the installation guide](https://soapbox.pub/install/).
|
||||
|
||||
## License
|
||||
|
||||
Rebased is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Rebased is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with Rebased. If not, see <https://www.gnu.org/licenses/>.
|
16
SECURITY.md
Normal file
16
SECURITY.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Pleroma backend security policy
|
||||
|
||||
## Supported versions
|
||||
|
||||
Currently, Pleroma offers bugfixes and security patches only for the latest minor release.
|
||||
|
||||
| Version | Support
|
||||
|---------| --------
|
||||
| 2.2 | Bugfixes and security patches
|
||||
|
||||
## Reporting a vulnerability
|
||||
|
||||
Please use confidential issues (tick the "This issue is confidential and should only be visible to team members with at least Reporter access." box when submitting) at our [bugtracker](https://git.pleroma.social/pleroma/pleroma/-/issues/new) for reporting vulnerabilities.
|
||||
## Announcements
|
||||
|
||||
New releases are announced at [pleroma.social](https://pleroma.social/announcements/). All security releases are tagged with ["Security"](https://pleroma.social/announcements/tags/security/). You can be notified of them by subscribing to an Atom feed at <https://pleroma.social/announcements/tags/security/feed.xml>.
|
13
app.json
Normal file
13
app.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"name": "Rebased",
|
||||
"description": "Rebased, the recommended backend for Soapbox written in Elixir.",
|
||||
"keywords": [
|
||||
"fediverse"
|
||||
],
|
||||
"website": "https://soapbox.pub",
|
||||
"dokku": {
|
||||
"plugins": [
|
||||
"postgres"
|
||||
]
|
||||
}
|
||||
}
|
595
benchmarks/load_testing/activities.ex
Normal file
595
benchmarks/load_testing/activities.ex
Normal file
@ -0,0 +1,595 @@
|
||||
defmodule Pleroma.LoadTesting.Activities do
|
||||
@moduledoc """
|
||||
Module for generating different activities.
|
||||
"""
|
||||
import Ecto.Query
|
||||
import Pleroma.LoadTesting.Helper, only: [to_sec: 1]
|
||||
|
||||
alias Ecto.UUID
|
||||
alias Pleroma.Constants
|
||||
alias Pleroma.LoadTesting.Users
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.Web.CommonAPI
|
||||
|
||||
require Constants
|
||||
|
||||
@defaults [
|
||||
iterations: 170,
|
||||
friends_used: 20,
|
||||
non_friends_used: 20
|
||||
]
|
||||
|
||||
@max_concurrency 10
|
||||
|
||||
@visibility ~w(public private direct unlisted)
|
||||
@types [
|
||||
:simple,
|
||||
:simple_filtered,
|
||||
:emoji,
|
||||
:mentions,
|
||||
:hell_thread,
|
||||
:attachment,
|
||||
:tag,
|
||||
:like,
|
||||
:reblog,
|
||||
:simple_thread
|
||||
]
|
||||
@groups [:friends_local, :friends_remote, :non_friends_local, :non_friends_local]
|
||||
@remote_groups [:friends_remote, :non_friends_remote]
|
||||
@friends_groups [:friends_local, :friends_remote]
|
||||
@non_friends_groups [:non_friends_local, :non_friends_remote]
|
||||
|
||||
@spec generate(User.t(), keyword()) :: :ok
|
||||
def generate(user, opts \\ []) do
|
||||
{:ok, _} =
|
||||
Agent.start_link(fn -> %{} end,
|
||||
name: :benchmark_state
|
||||
)
|
||||
|
||||
opts = Keyword.merge(@defaults, opts)
|
||||
|
||||
users = Users.prepare_users(user, opts)
|
||||
|
||||
{:ok, _} = Agent.start_link(fn -> users[:non_friends_remote] end, name: :non_friends_remote)
|
||||
|
||||
task_data =
|
||||
for visibility <- @visibility,
|
||||
type <- @types,
|
||||
group <- [:user | @groups],
|
||||
do: {visibility, type, group}
|
||||
|
||||
IO.puts("Starting generating #{opts[:iterations]} iterations of activities...")
|
||||
|
||||
public_long_thread = fn ->
|
||||
generate_long_thread("public", users, opts)
|
||||
end
|
||||
|
||||
private_long_thread = fn ->
|
||||
generate_long_thread("private", users, opts)
|
||||
end
|
||||
|
||||
iterations = opts[:iterations]
|
||||
|
||||
{time, _} =
|
||||
:timer.tc(fn ->
|
||||
Enum.each(
|
||||
1..iterations,
|
||||
fn
|
||||
i when i == iterations - 2 ->
|
||||
spawn(public_long_thread)
|
||||
spawn(private_long_thread)
|
||||
generate_activities(users, Enum.shuffle(task_data), opts)
|
||||
|
||||
_ ->
|
||||
generate_activities(users, Enum.shuffle(task_data), opts)
|
||||
end
|
||||
)
|
||||
end)
|
||||
|
||||
IO.puts("Generating iterations of activities took #{to_sec(time)} sec.\n")
|
||||
:ok
|
||||
end
|
||||
|
||||
def generate_power_intervals(opts \\ []) do
|
||||
count = Keyword.get(opts, :count, 20)
|
||||
power = Keyword.get(opts, :power, 2)
|
||||
IO.puts("Generating #{count} intervals for a power #{power} series...")
|
||||
counts = Enum.map(1..count, fn n -> :math.pow(n, power) end)
|
||||
sum = Enum.sum(counts)
|
||||
|
||||
densities =
|
||||
Enum.map(counts, fn c ->
|
||||
c / sum
|
||||
end)
|
||||
|
||||
densities
|
||||
|> Enum.reduce(0, fn density, acc ->
|
||||
if acc == 0 do
|
||||
[{0, density}]
|
||||
else
|
||||
[{_, lower} | _] = acc
|
||||
[{lower, lower + density} | acc]
|
||||
end
|
||||
end)
|
||||
|> Enum.reverse()
|
||||
end
|
||||
|
||||
def generate_tagged_activities(opts \\ []) do
|
||||
tag_count = Keyword.get(opts, :tag_count, 20)
|
||||
users = Keyword.get(opts, :users, Repo.all(Pleroma.User))
|
||||
activity_count = Keyword.get(opts, :count, 200_000)
|
||||
|
||||
intervals = generate_power_intervals(count: tag_count)
|
||||
|
||||
IO.puts(
|
||||
"Generating #{activity_count} activities using #{tag_count} different tags of format `tag_n`, starting at tag_0"
|
||||
)
|
||||
|
||||
Enum.each(1..activity_count, fn _ ->
|
||||
random = :rand.uniform()
|
||||
i = Enum.find_index(intervals, fn {lower, upper} -> lower <= random && upper > random end)
|
||||
CommonAPI.post(Enum.random(users), %{status: "a post with the tag #tag_#{i}"})
|
||||
end)
|
||||
end
|
||||
|
||||
defp generate_long_thread(visibility, users, _opts) do
|
||||
group =
|
||||
if visibility == "public",
|
||||
do: :friends_local,
|
||||
else: :user
|
||||
|
||||
tasks = get_reply_tasks(visibility, group) |> Stream.cycle() |> Enum.take(50)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(users[:user], %{
|
||||
status: "Start of #{visibility} long thread",
|
||||
visibility: visibility
|
||||
})
|
||||
|
||||
Agent.update(:benchmark_state, fn state ->
|
||||
key =
|
||||
if visibility == "public",
|
||||
do: :public_thread,
|
||||
else: :private_thread
|
||||
|
||||
Map.put(state, key, activity)
|
||||
end)
|
||||
|
||||
acc = {activity.id, ["@" <> users[:user].nickname, "reply to long thread"]}
|
||||
insert_replies_for_long_thread(tasks, visibility, users, acc)
|
||||
IO.puts("Generating #{visibility} long thread ended\n")
|
||||
end
|
||||
|
||||
defp insert_replies_for_long_thread(tasks, visibility, users, acc) do
|
||||
Enum.reduce(tasks, acc, fn
|
||||
:user, {id, data} ->
|
||||
user = users[:user]
|
||||
insert_reply(user, List.delete(data, "@" <> user.nickname), id, visibility)
|
||||
|
||||
group, {id, data} ->
|
||||
replier = Enum.random(users[group])
|
||||
insert_reply(replier, List.delete(data, "@" <> replier.nickname), id, visibility)
|
||||
end)
|
||||
end
|
||||
|
||||
defp generate_activities(users, task_data, opts) do
|
||||
Task.async_stream(
|
||||
task_data,
|
||||
fn {visibility, type, group} ->
|
||||
insert_activity(type, visibility, group, users, opts)
|
||||
end,
|
||||
max_concurrency: @max_concurrency,
|
||||
timeout: 30_000
|
||||
)
|
||||
|> Stream.run()
|
||||
end
|
||||
|
||||
defp insert_local_activity(visibility, group, users, status) do
|
||||
{:ok, _} =
|
||||
group
|
||||
|> get_actor(users)
|
||||
|> CommonAPI.post(%{status: status, visibility: visibility})
|
||||
end
|
||||
|
||||
defp insert_remote_activity(visibility, group, users, status) do
|
||||
actor = get_actor(group, users)
|
||||
{act_data, obj_data} = prepare_activity_data(actor, visibility, users[:user])
|
||||
{activity_data, object_data} = other_data(actor, status)
|
||||
|
||||
activity_data
|
||||
|> Map.merge(act_data)
|
||||
|> Map.put("object", Map.merge(object_data, obj_data))
|
||||
|> Pleroma.Web.ActivityPub.ActivityPub.insert(false)
|
||||
end
|
||||
|
||||
defp user_mentions(users) do
|
||||
user_mentions =
|
||||
Enum.reduce(
|
||||
@groups,
|
||||
[],
|
||||
fn group, acc ->
|
||||
acc ++ get_random_mentions(users[group], Enum.random(0..2))
|
||||
end
|
||||
)
|
||||
|
||||
if Enum.random([true, false]),
|
||||
do: ["@" <> users[:user].nickname | user_mentions],
|
||||
else: user_mentions
|
||||
end
|
||||
|
||||
defp hell_thread_mentions(users) do
|
||||
with {:ok, nil} <- Cachex.get(:user_cache, "hell_thread_mentions") do
|
||||
cached =
|
||||
@groups
|
||||
|> Enum.reduce([users[:user]], fn group, acc ->
|
||||
acc ++ Enum.take(users[group], 5)
|
||||
end)
|
||||
|> Enum.map(&"@#{&1.nickname}")
|
||||
|> Enum.join(", ")
|
||||
|
||||
Cachex.put(:user_cache, "hell_thread_mentions", cached)
|
||||
cached
|
||||
else
|
||||
{:ok, cached} -> cached
|
||||
end
|
||||
end
|
||||
|
||||
defp insert_activity(:simple, visibility, group, users, _opts)
|
||||
when group in @remote_groups do
|
||||
insert_remote_activity(visibility, group, users, "Remote status")
|
||||
end
|
||||
|
||||
defp insert_activity(:simple, visibility, group, users, _opts) do
|
||||
insert_local_activity(visibility, group, users, "Simple status")
|
||||
end
|
||||
|
||||
defp insert_activity(:simple_filtered, visibility, group, users, _opts)
|
||||
when group in @remote_groups do
|
||||
insert_remote_activity(visibility, group, users, "Remote status which must be filtered")
|
||||
end
|
||||
|
||||
defp insert_activity(:simple_filtered, visibility, group, users, _opts) do
|
||||
insert_local_activity(visibility, group, users, "Simple status which must be filtered")
|
||||
end
|
||||
|
||||
defp insert_activity(:emoji, visibility, group, users, _opts)
|
||||
when group in @remote_groups do
|
||||
insert_remote_activity(visibility, group, users, "Remote status with emoji :firefox:")
|
||||
end
|
||||
|
||||
defp insert_activity(:emoji, visibility, group, users, _opts) do
|
||||
insert_local_activity(visibility, group, users, "Simple status with emoji :firefox:")
|
||||
end
|
||||
|
||||
defp insert_activity(:mentions, visibility, group, users, _opts)
|
||||
when group in @remote_groups do
|
||||
mentions = user_mentions(users)
|
||||
|
||||
status = Enum.join(mentions, ", ") <> " remote status with mentions"
|
||||
|
||||
insert_remote_activity(visibility, group, users, status)
|
||||
end
|
||||
|
||||
defp insert_activity(:mentions, visibility, group, users, _opts) do
|
||||
mentions = user_mentions(users)
|
||||
|
||||
status = Enum.join(mentions, ", ") <> " simple status with mentions"
|
||||
insert_remote_activity(visibility, group, users, status)
|
||||
end
|
||||
|
||||
defp insert_activity(:hell_thread, visibility, group, users, _)
|
||||
when group in @remote_groups do
|
||||
mentions = hell_thread_mentions(users)
|
||||
insert_remote_activity(visibility, group, users, mentions <> " remote hell thread status")
|
||||
end
|
||||
|
||||
defp insert_activity(:hell_thread, visibility, group, users, _opts) do
|
||||
mentions = hell_thread_mentions(users)
|
||||
|
||||
insert_local_activity(visibility, group, users, mentions <> " hell thread status")
|
||||
end
|
||||
|
||||
defp insert_activity(:attachment, visibility, group, users, _opts) do
|
||||
actor = get_actor(group, users)
|
||||
|
||||
obj_data = %{
|
||||
"actor" => actor.ap_id,
|
||||
"name" => "4467-11.jpg",
|
||||
"type" => "Document",
|
||||
"url" => [
|
||||
%{
|
||||
"href" =>
|
||||
"#{Pleroma.Web.Endpoint.url()}/media/b1b873552422a07bf53af01f3c231c841db4dfc42c35efde681abaf0f2a4eab7.jpg",
|
||||
"mediaType" => "image/jpeg",
|
||||
"type" => "Link"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
object = Repo.insert!(%Pleroma.Object{data: obj_data})
|
||||
|
||||
{:ok, _activity} =
|
||||
CommonAPI.post(actor, %{
|
||||
status: "Post with attachment",
|
||||
visibility: visibility,
|
||||
media_ids: [object.id]
|
||||
})
|
||||
end
|
||||
|
||||
defp insert_activity(:tag, visibility, group, users, _opts) do
|
||||
insert_local_activity(visibility, group, users, "Status with #tag")
|
||||
end
|
||||
|
||||
defp insert_activity(:like, visibility, group, users, opts) do
|
||||
actor = get_actor(group, users)
|
||||
|
||||
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
||||
{:ok, _activity} <- CommonAPI.favorite(actor, activity_id) do
|
||||
:ok
|
||||
else
|
||||
{:error, _} ->
|
||||
insert_activity(:like, visibility, group, users, opts)
|
||||
|
||||
nil ->
|
||||
Process.sleep(15)
|
||||
insert_activity(:like, visibility, group, users, opts)
|
||||
end
|
||||
end
|
||||
|
||||
defp insert_activity(:reblog, visibility, group, users, opts) do
|
||||
actor = get_actor(group, users)
|
||||
|
||||
with activity_id when not is_nil(activity_id) <- get_random_create_activity_id(),
|
||||
{:ok, _activity} <- CommonAPI.repeat(activity_id, actor) do
|
||||
:ok
|
||||
else
|
||||
{:error, _} ->
|
||||
insert_activity(:reblog, visibility, group, users, opts)
|
||||
|
||||
nil ->
|
||||
Process.sleep(15)
|
||||
insert_activity(:reblog, visibility, group, users, opts)
|
||||
end
|
||||
end
|
||||
|
||||
defp insert_activity(:simple_thread, "direct", group, users, _opts) do
|
||||
actor = get_actor(group, users)
|
||||
tasks = get_reply_tasks("direct", group)
|
||||
|
||||
list =
|
||||
case group do
|
||||
:user ->
|
||||
group = Enum.random(@friends_groups)
|
||||
Enum.take(users[group], 3)
|
||||
|
||||
_ ->
|
||||
Enum.take(users[group], 3)
|
||||
end
|
||||
|
||||
data = Enum.map(list, &("@" <> &1.nickname))
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(actor, %{
|
||||
status: Enum.join(data, ", ") <> "simple status",
|
||||
visibility: "direct"
|
||||
})
|
||||
|
||||
acc = {activity.id, ["@" <> users[:user].nickname | data] ++ ["reply to status"]}
|
||||
insert_direct_replies(tasks, users[:user], list, acc)
|
||||
end
|
||||
|
||||
defp insert_activity(:simple_thread, visibility, group, users, _opts) do
|
||||
actor = get_actor(group, users)
|
||||
tasks = get_reply_tasks(visibility, group)
|
||||
|
||||
{:ok, activity} =
|
||||
CommonAPI.post(users[:user], %{status: "Simple status", visibility: visibility})
|
||||
|
||||
acc = {activity.id, ["@" <> actor.nickname, "reply to status"]}
|
||||
insert_replies(tasks, visibility, users, acc)
|
||||
end
|
||||
|
||||
defp get_actor(:user, %{user: user}), do: user
|
||||
defp get_actor(group, users), do: Enum.random(users[group])
|
||||
|
||||
defp other_data(actor, content) do
|
||||
%{host: host} = URI.parse(actor.ap_id)
|
||||
datetime = DateTime.utc_now() |> to_string()
|
||||
context_id = "https://#{host}/contexts/#{UUID.generate()}"
|
||||
activity_id = "https://#{host}/activities/#{UUID.generate()}"
|
||||
object_id = "https://#{host}/objects/#{UUID.generate()}"
|
||||
|
||||
activity_data = %{
|
||||
"actor" => actor.ap_id,
|
||||
"context" => context_id,
|
||||
"id" => activity_id,
|
||||
"published" => datetime,
|
||||
"type" => "Create",
|
||||
"directMessage" => false
|
||||
}
|
||||
|
||||
object_data = %{
|
||||
"actor" => actor.ap_id,
|
||||
"attachment" => [],
|
||||
"attributedTo" => actor.ap_id,
|
||||
"bcc" => [],
|
||||
"bto" => [],
|
||||
"content" => content,
|
||||
"context" => context_id,
|
||||
"conversation" => context_id,
|
||||
"emoji" => %{},
|
||||
"id" => object_id,
|
||||
"published" => datetime,
|
||||
"sensitive" => false,
|
||||
"summary" => "",
|
||||
"tag" => [],
|
||||
"to" => ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
"type" => "Note"
|
||||
}
|
||||
|
||||
{activity_data, object_data}
|
||||
end
|
||||
|
||||
defp prepare_activity_data(actor, "public", _mention) do
|
||||
obj_data = %{
|
||||
"cc" => [actor.follower_address],
|
||||
"to" => [Constants.as_public()]
|
||||
}
|
||||
|
||||
act_data = %{
|
||||
"cc" => [actor.follower_address],
|
||||
"to" => [Constants.as_public()]
|
||||
}
|
||||
|
||||
{act_data, obj_data}
|
||||
end
|
||||
|
||||
defp prepare_activity_data(actor, "private", _mention) do
|
||||
obj_data = %{
|
||||
"cc" => [],
|
||||
"to" => [actor.follower_address]
|
||||
}
|
||||
|
||||
act_data = %{
|
||||
"cc" => [],
|
||||
"to" => [actor.follower_address]
|
||||
}
|
||||
|
||||
{act_data, obj_data}
|
||||
end
|
||||
|
||||
defp prepare_activity_data(actor, "unlisted", _mention) do
|
||||
obj_data = %{
|
||||
"cc" => [Constants.as_public()],
|
||||
"to" => [actor.follower_address]
|
||||
}
|
||||
|
||||
act_data = %{
|
||||
"cc" => [Constants.as_public()],
|
||||
"to" => [actor.follower_address]
|
||||
}
|
||||
|
||||
{act_data, obj_data}
|
||||
end
|
||||
|
||||
defp prepare_activity_data(_actor, "direct", mention) do
|
||||
%{host: mentioned_host} = URI.parse(mention.ap_id)
|
||||
|
||||
obj_data = %{
|
||||
"cc" => [],
|
||||
"content" =>
|
||||
"<span class=\"h-card\"><a class=\"u-url mention\" href=\"#{mention.ap_id}\" rel=\"ugc\">@<span>#{
|
||||
mention.nickname
|
||||
}</span></a></span> direct message",
|
||||
"tag" => [
|
||||
%{
|
||||
"href" => mention.ap_id,
|
||||
"name" => "@#{mention.nickname}@#{mentioned_host}",
|
||||
"type" => "Mention"
|
||||
}
|
||||
],
|
||||
"to" => [mention.ap_id]
|
||||
}
|
||||
|
||||
act_data = %{
|
||||
"cc" => [],
|
||||
"directMessage" => true,
|
||||
"to" => [mention.ap_id]
|
||||
}
|
||||
|
||||
{act_data, obj_data}
|
||||
end
|
||||
|
||||
defp get_reply_tasks("public", :user) do
|
||||
[:friends_local, :friends_remote, :non_friends_local, :non_friends_remote, :user]
|
||||
end
|
||||
|
||||
defp get_reply_tasks("public", group) when group in @friends_groups do
|
||||
[:non_friends_local, :non_friends_remote, :user, :friends_local, :friends_remote]
|
||||
end
|
||||
|
||||
defp get_reply_tasks("public", group) when group in @non_friends_groups do
|
||||
[:user, :friends_local, :friends_remote, :non_friends_local, :non_friends_remote]
|
||||
end
|
||||
|
||||
defp get_reply_tasks(visibility, :user) when visibility in ["unlisted", "private"] do
|
||||
[:friends_local, :friends_remote, :user, :friends_local, :friends_remote]
|
||||
end
|
||||
|
||||
defp get_reply_tasks(visibility, group)
|
||||
when visibility in ["unlisted", "private"] and group in @friends_groups do
|
||||
[:user, :friends_remote, :friends_local, :user]
|
||||
end
|
||||
|
||||
defp get_reply_tasks(visibility, group)
|
||||
when visibility in ["unlisted", "private"] and
|
||||
group in @non_friends_groups,
|
||||
do: []
|
||||
|
||||
defp get_reply_tasks("direct", :user), do: [:friends_local, :user, :friends_remote]
|
||||
|
||||
defp get_reply_tasks("direct", group) when group in @friends_groups,
|
||||
do: [:user, group, :user]
|
||||
|
||||
defp get_reply_tasks("direct", group) when group in @non_friends_groups do
|
||||
[:user, :non_friends_remote, :user, :non_friends_local]
|
||||
end
|
||||
|
||||
defp insert_replies(tasks, visibility, users, acc) do
|
||||
Enum.reduce(tasks, acc, fn
|
||||
:user, {id, data} ->
|
||||
insert_reply(users[:user], data, id, visibility)
|
||||
|
||||
group, {id, data} ->
|
||||
replier = Enum.random(users[group])
|
||||
insert_reply(replier, data, id, visibility)
|
||||
end)
|
||||
end
|
||||
|
||||
defp insert_direct_replies(tasks, user, list, acc) do
|
||||
Enum.reduce(tasks, acc, fn
|
||||
:user, {id, data} ->
|
||||
{reply_id, _} = insert_reply(user, List.delete(data, "@" <> user.nickname), id, "direct")
|
||||
{reply_id, data}
|
||||
|
||||
_, {id, data} ->
|
||||
actor = Enum.random(list)
|
||||
|
||||
{reply_id, _} =
|
||||
insert_reply(actor, List.delete(data, "@" <> actor.nickname), id, "direct")
|
||||
|
||||
{reply_id, data}
|
||||
end)
|
||||
end
|
||||
|
||||
defp insert_reply(actor, data, activity_id, visibility) do
|
||||
{:ok, reply} =
|
||||
CommonAPI.post(actor, %{
|
||||
status: Enum.join(data, ", "),
|
||||
visibility: visibility,
|
||||
in_reply_to_status_id: activity_id
|
||||
})
|
||||
|
||||
{reply.id, ["@" <> actor.nickname | data]}
|
||||
end
|
||||
|
||||
defp get_random_mentions(_users, count) when count == 0, do: []
|
||||
|
||||
defp get_random_mentions(users, count) do
|
||||
users
|
||||
|> Enum.shuffle()
|
||||
|> Enum.take(count)
|
||||
|> Enum.map(&"@#{&1.nickname}")
|
||||
end
|
||||
|
||||
defp get_random_create_activity_id do
|
||||
Repo.one(
|
||||
from(a in Pleroma.Activity,
|
||||
where: fragment("(?)->>'type' = ?", a.data, ^"Create"),
|
||||
order_by: fragment("RANDOM()"),
|
||||
limit: 1,
|
||||
select: a.id
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
624
benchmarks/load_testing/fetcher.ex
Normal file
624
benchmarks/load_testing/fetcher.ex
Normal file
@ -0,0 +1,624 @@
|
||||
defmodule Pleroma.LoadTesting.Fetcher do
|
||||
alias Pleroma.Activity
|
||||
alias Pleroma.Pagination
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||
alias Pleroma.Web.MastodonAPI.MastodonAPI
|
||||
alias Pleroma.Web.MastodonAPI.StatusView
|
||||
|
||||
@spec run_benchmarks(User.t()) :: any()
|
||||
def run_benchmarks(user) do
|
||||
fetch_user(user)
|
||||
fetch_timelines(user)
|
||||
render_views(user)
|
||||
end
|
||||
|
||||
defp formatters do
|
||||
[
|
||||
Benchee.Formatters.Console
|
||||
]
|
||||
end
|
||||
|
||||
defp fetch_user(user) do
|
||||
Benchee.run(
|
||||
%{
|
||||
"By id" => fn -> Repo.get_by(User, id: user.id) end,
|
||||
"By ap_id" => fn -> Repo.get_by(User, ap_id: user.ap_id) end,
|
||||
"By email" => fn -> Repo.get_by(User, email: user.email) end,
|
||||
"By nickname" => fn -> Repo.get_by(User, nickname: user.nickname) end
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp create_filter(user) do
|
||||
Pleroma.Filter.create(%{
|
||||
user_id: user.id,
|
||||
phrase: "must be filtered",
|
||||
hide: true,
|
||||
context: ["home"]
|
||||
})
|
||||
end
|
||||
|
||||
defp delete_filter(filter), do: Repo.delete(filter)
|
||||
|
||||
defp fetch_timelines(user) do
|
||||
fetch_home_timeline(user)
|
||||
fetch_home_timeline_with_filter(user)
|
||||
fetch_direct_timeline(user)
|
||||
fetch_public_timeline(user)
|
||||
fetch_public_timeline_with_filter(user)
|
||||
fetch_public_timeline(user, :with_blocks)
|
||||
fetch_public_timeline(user, :local)
|
||||
fetch_public_timeline(user, :tag)
|
||||
fetch_notifications(user)
|
||||
fetch_favourites(user)
|
||||
fetch_long_thread(user)
|
||||
fetch_timelines_with_reply_filtering(user)
|
||||
end
|
||||
|
||||
defp render_views(user) do
|
||||
render_timelines(user)
|
||||
render_long_thread(user)
|
||||
end
|
||||
|
||||
defp opts_for_home_timeline(user) do
|
||||
%{
|
||||
blocking_user: user,
|
||||
count: "20",
|
||||
muting_user: user,
|
||||
type: ["Create", "Announce"],
|
||||
user: user,
|
||||
with_muted: true
|
||||
}
|
||||
end
|
||||
|
||||
defp fetch_home_timeline(user, title_end \\ "") do
|
||||
opts = opts_for_home_timeline(user)
|
||||
|
||||
recipients = [user.ap_id | User.following(user)]
|
||||
|
||||
first_page_last =
|
||||
ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse() |> List.last()
|
||||
|
||||
second_page_last =
|
||||
ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, first_page_last.id))
|
||||
|> Enum.reverse()
|
||||
|> List.last()
|
||||
|
||||
third_page_last =
|
||||
ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, second_page_last.id))
|
||||
|> Enum.reverse()
|
||||
|> List.last()
|
||||
|
||||
forth_page_last =
|
||||
ActivityPub.fetch_activities(recipients, Map.put(opts, :max_id, third_page_last.id))
|
||||
|> Enum.reverse()
|
||||
|> List.last()
|
||||
|
||||
title = "home timeline " <> title_end
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
title => fn opts -> ActivityPub.fetch_activities(recipients, opts) end
|
||||
},
|
||||
inputs: %{
|
||||
"1 page" => opts,
|
||||
"2 page" => Map.put(opts, :max_id, first_page_last.id),
|
||||
"3 page" => Map.put(opts, :max_id, second_page_last.id),
|
||||
"4 page" => Map.put(opts, :max_id, third_page_last.id),
|
||||
"5 page" => Map.put(opts, :max_id, forth_page_last.id),
|
||||
"1 page only media" => Map.put(opts, :only_media, true),
|
||||
"2 page only media" =>
|
||||
Map.put(opts, :max_id, first_page_last.id) |> Map.put(:only_media, true),
|
||||
"3 page only media" =>
|
||||
Map.put(opts, :max_id, second_page_last.id) |> Map.put(:only_media, true),
|
||||
"4 page only media" =>
|
||||
Map.put(opts, :max_id, third_page_last.id) |> Map.put(:only_media, true),
|
||||
"5 page only media" =>
|
||||
Map.put(opts, :max_id, forth_page_last.id) |> Map.put(:only_media, true)
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp fetch_home_timeline_with_filter(user) do
|
||||
{:ok, filter} = create_filter(user)
|
||||
|
||||
fetch_home_timeline(user, "with filters")
|
||||
|
||||
delete_filter(filter)
|
||||
end
|
||||
|
||||
defp opts_for_direct_timeline(user) do
|
||||
%{
|
||||
visibility: "direct",
|
||||
blocking_user: user,
|
||||
count: "20",
|
||||
type: "Create",
|
||||
user: user,
|
||||
with_muted: true
|
||||
}
|
||||
end
|
||||
|
||||
defp fetch_direct_timeline(user) do
|
||||
recipients = [user.ap_id]
|
||||
|
||||
opts = opts_for_direct_timeline(user)
|
||||
|
||||
first_page_last =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities_query(opts)
|
||||
|> Pagination.fetch_paginated(opts)
|
||||
|> List.last()
|
||||
|
||||
opts2 = Map.put(opts, :max_id, first_page_last.id)
|
||||
|
||||
second_page_last =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities_query(opts2)
|
||||
|> Pagination.fetch_paginated(opts2)
|
||||
|> List.last()
|
||||
|
||||
opts3 = Map.put(opts, :max_id, second_page_last.id)
|
||||
|
||||
third_page_last =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities_query(opts3)
|
||||
|> Pagination.fetch_paginated(opts3)
|
||||
|> List.last()
|
||||
|
||||
opts4 = Map.put(opts, :max_id, third_page_last.id)
|
||||
|
||||
forth_page_last =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities_query(opts4)
|
||||
|> Pagination.fetch_paginated(opts4)
|
||||
|> List.last()
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"direct timeline" => fn opts ->
|
||||
ActivityPub.fetch_activities_query(recipients, opts) |> Pagination.fetch_paginated(opts)
|
||||
end
|
||||
},
|
||||
inputs: %{
|
||||
"1 page" => opts,
|
||||
"2 page" => opts2,
|
||||
"3 page" => opts3,
|
||||
"4 page" => opts4,
|
||||
"5 page" => Map.put(opts4, :max_id, forth_page_last.id)
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp opts_for_public_timeline(user) do
|
||||
%{
|
||||
type: ["Create", "Announce"],
|
||||
local_only: false,
|
||||
blocking_user: user,
|
||||
muting_user: user
|
||||
}
|
||||
end
|
||||
|
||||
defp opts_for_public_timeline(user, :local) do
|
||||
%{
|
||||
type: ["Create", "Announce"],
|
||||
local_only: true,
|
||||
blocking_user: user,
|
||||
muting_user: user
|
||||
}
|
||||
end
|
||||
|
||||
defp opts_for_public_timeline(user, :tag) do
|
||||
%{
|
||||
blocking_user: user,
|
||||
count: "20",
|
||||
local_only: nil,
|
||||
muting_user: user,
|
||||
tag: ["tag"],
|
||||
tag_all: [],
|
||||
tag_reject: [],
|
||||
type: "Create",
|
||||
user: user,
|
||||
with_muted: true
|
||||
}
|
||||
end
|
||||
|
||||
defp fetch_public_timeline(user) do
|
||||
opts = opts_for_public_timeline(user)
|
||||
|
||||
fetch_public_timeline(opts, "public timeline")
|
||||
end
|
||||
|
||||
defp fetch_public_timeline_with_filter(user) do
|
||||
{:ok, filter} = create_filter(user)
|
||||
opts = opts_for_public_timeline(user)
|
||||
|
||||
fetch_public_timeline(opts, "public timeline with filters")
|
||||
delete_filter(filter)
|
||||
end
|
||||
|
||||
defp fetch_public_timeline(user, :local) do
|
||||
opts = opts_for_public_timeline(user, :local)
|
||||
|
||||
fetch_public_timeline(opts, "public timeline only local")
|
||||
end
|
||||
|
||||
defp fetch_public_timeline(user, :tag) do
|
||||
opts = opts_for_public_timeline(user, :tag)
|
||||
|
||||
fetch_public_timeline(opts, "hashtag timeline")
|
||||
end
|
||||
|
||||
defp fetch_public_timeline(user, :only_media) do
|
||||
opts = opts_for_public_timeline(user) |> Map.put(:only_media, true)
|
||||
|
||||
fetch_public_timeline(opts, "public timeline only media")
|
||||
end
|
||||
|
||||
defp fetch_public_timeline(user, :with_blocks) do
|
||||
opts = opts_for_public_timeline(user)
|
||||
|
||||
remote_non_friends = Agent.get(:non_friends_remote, & &1)
|
||||
|
||||
Benchee.run(%{
|
||||
"public timeline without blocks" => fn ->
|
||||
ActivityPub.fetch_public_activities(opts)
|
||||
end
|
||||
})
|
||||
|
||||
Enum.each(remote_non_friends, fn non_friend ->
|
||||
{:ok, _} = User.block(user, non_friend)
|
||||
end)
|
||||
|
||||
user = User.get_by_id(user.id)
|
||||
|
||||
opts = Map.put(opts, :blocking_user, user)
|
||||
|
||||
Benchee.run(%{
|
||||
"public timeline with user block" => fn ->
|
||||
ActivityPub.fetch_public_activities(opts)
|
||||
end
|
||||
})
|
||||
|
||||
domains =
|
||||
Enum.reduce(remote_non_friends, [], fn non_friend, domains ->
|
||||
{:ok, _user} = User.unblock(user, non_friend)
|
||||
%{host: host} = URI.parse(non_friend.ap_id)
|
||||
[host | domains]
|
||||
end)
|
||||
|
||||
domains = Enum.uniq(domains)
|
||||
|
||||
Enum.each(domains, fn domain ->
|
||||
{:ok, _} = User.block_domain(user, domain)
|
||||
end)
|
||||
|
||||
user = User.get_by_id(user.id)
|
||||
opts = Map.put(opts, :blocking_user, user)
|
||||
|
||||
Benchee.run(%{
|
||||
"public timeline with domain block" => fn ->
|
||||
ActivityPub.fetch_public_activities(opts)
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
defp fetch_public_timeline(opts, title) when is_binary(title) do
|
||||
first_page_last = ActivityPub.fetch_public_activities(opts) |> List.last()
|
||||
|
||||
second_page_last =
|
||||
ActivityPub.fetch_public_activities(Map.put(opts, :max_id, first_page_last.id))
|
||||
|> List.last()
|
||||
|
||||
third_page_last =
|
||||
ActivityPub.fetch_public_activities(Map.put(opts, :max_id, second_page_last.id))
|
||||
|> List.last()
|
||||
|
||||
forth_page_last =
|
||||
ActivityPub.fetch_public_activities(Map.put(opts, :max_id, third_page_last.id))
|
||||
|> List.last()
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
title => fn opts ->
|
||||
ActivityPub.fetch_public_activities(opts)
|
||||
end
|
||||
},
|
||||
inputs: %{
|
||||
"1 page" => opts,
|
||||
"2 page" => Map.put(opts, :max_id, first_page_last.id),
|
||||
"3 page" => Map.put(opts, :max_id, second_page_last.id),
|
||||
"4 page" => Map.put(opts, :max_id, third_page_last.id),
|
||||
"5 page" => Map.put(opts, :max_id, forth_page_last.id)
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp opts_for_notifications do
|
||||
%{count: "20", with_muted: true}
|
||||
end
|
||||
|
||||
defp fetch_notifications(user) do
|
||||
opts = opts_for_notifications()
|
||||
|
||||
first_page_last = MastodonAPI.get_notifications(user, opts) |> List.last()
|
||||
|
||||
second_page_last =
|
||||
MastodonAPI.get_notifications(user, Map.put(opts, :max_id, first_page_last.id))
|
||||
|> List.last()
|
||||
|
||||
third_page_last =
|
||||
MastodonAPI.get_notifications(user, Map.put(opts, :max_id, second_page_last.id))
|
||||
|> List.last()
|
||||
|
||||
forth_page_last =
|
||||
MastodonAPI.get_notifications(user, Map.put(opts, :max_id, third_page_last.id))
|
||||
|> List.last()
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Notifications" => fn opts ->
|
||||
MastodonAPI.get_notifications(user, opts)
|
||||
end
|
||||
},
|
||||
inputs: %{
|
||||
"1 page" => opts,
|
||||
"2 page" => Map.put(opts, :max_id, first_page_last.id),
|
||||
"3 page" => Map.put(opts, :max_id, second_page_last.id),
|
||||
"4 page" => Map.put(opts, :max_id, third_page_last.id),
|
||||
"5 page" => Map.put(opts, :max_id, forth_page_last.id)
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp fetch_favourites(user) do
|
||||
first_page_last = ActivityPub.fetch_favourites(user) |> List.last()
|
||||
|
||||
second_page_last =
|
||||
ActivityPub.fetch_favourites(user, %{:max_id => first_page_last.id}) |> List.last()
|
||||
|
||||
third_page_last =
|
||||
ActivityPub.fetch_favourites(user, %{:max_id => second_page_last.id}) |> List.last()
|
||||
|
||||
forth_page_last =
|
||||
ActivityPub.fetch_favourites(user, %{:max_id => third_page_last.id}) |> List.last()
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Favourites" => fn opts ->
|
||||
ActivityPub.fetch_favourites(user, opts)
|
||||
end
|
||||
},
|
||||
inputs: %{
|
||||
"1 page" => %{},
|
||||
"2 page" => %{:max_id => first_page_last.id},
|
||||
"3 page" => %{:max_id => second_page_last.id},
|
||||
"4 page" => %{:max_id => third_page_last.id},
|
||||
"5 page" => %{:max_id => forth_page_last.id}
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp opts_for_long_thread(user) do
|
||||
%{
|
||||
blocking_user: user,
|
||||
user: user
|
||||
}
|
||||
end
|
||||
|
||||
defp fetch_long_thread(user) do
|
||||
%{public_thread: public, private_thread: private} =
|
||||
Agent.get(:benchmark_state, fn state -> state end)
|
||||
|
||||
opts = opts_for_long_thread(user)
|
||||
|
||||
private_input = {private.data["context"], Map.put(opts, :exclude_id, private.id)}
|
||||
|
||||
public_input = {public.data["context"], Map.put(opts, :exclude_id, public.id)}
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"fetch context" => fn {context, opts} ->
|
||||
ActivityPub.fetch_activities_for_context(context, opts)
|
||||
end
|
||||
},
|
||||
inputs: %{
|
||||
"Private long thread" => private_input,
|
||||
"Public long thread" => public_input
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp render_timelines(user) do
|
||||
opts = opts_for_home_timeline(user)
|
||||
|
||||
recipients = [user.ap_id | User.following(user)]
|
||||
|
||||
home_activities = ActivityPub.fetch_activities(recipients, opts) |> Enum.reverse()
|
||||
|
||||
recipients = [user.ap_id]
|
||||
|
||||
opts = opts_for_direct_timeline(user)
|
||||
|
||||
direct_activities =
|
||||
recipients
|
||||
|> ActivityPub.fetch_activities_query(opts)
|
||||
|> Pagination.fetch_paginated(opts)
|
||||
|
||||
opts = opts_for_public_timeline(user)
|
||||
|
||||
public_activities = ActivityPub.fetch_public_activities(opts)
|
||||
|
||||
opts = opts_for_public_timeline(user, :tag)
|
||||
|
||||
tag_activities = ActivityPub.fetch_public_activities(opts)
|
||||
|
||||
opts = opts_for_notifications()
|
||||
|
||||
notifications = MastodonAPI.get_notifications(user, opts)
|
||||
|
||||
favourites = ActivityPub.fetch_favourites(user)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Rendering home timeline" => fn ->
|
||||
StatusView.render("index.json", %{
|
||||
activities: home_activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end,
|
||||
"Rendering direct timeline" => fn ->
|
||||
StatusView.render("index.json", %{
|
||||
activities: direct_activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end,
|
||||
"Rendering public timeline" => fn ->
|
||||
StatusView.render("index.json", %{
|
||||
activities: public_activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end,
|
||||
"Rendering tag timeline" => fn ->
|
||||
StatusView.render("index.json", %{
|
||||
activities: tag_activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end,
|
||||
"Rendering notifications" => fn ->
|
||||
Pleroma.Web.MastodonAPI.NotificationView.render("index.json", %{
|
||||
notifications: notifications,
|
||||
for: user
|
||||
})
|
||||
end,
|
||||
"Rendering favourites timeline" => fn ->
|
||||
StatusView.render("index.json", %{
|
||||
activities: favourites,
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp render_long_thread(user) do
|
||||
%{public_thread: public, private_thread: private} =
|
||||
Agent.get(:benchmark_state, fn state -> state end)
|
||||
|
||||
opts = %{for: user}
|
||||
public_activity = Activity.get_by_id_with_object(public.id)
|
||||
private_activity = Activity.get_by_id_with_object(private.id)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"render" => fn opts ->
|
||||
StatusView.render("show.json", opts)
|
||||
end
|
||||
},
|
||||
inputs: %{
|
||||
"Public root" => Map.put(opts, :activity, public_activity),
|
||||
"Private root" => Map.put(opts, :activity, private_activity)
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
|
||||
fetch_opts = opts_for_long_thread(user)
|
||||
|
||||
public_context =
|
||||
ActivityPub.fetch_activities_for_context(
|
||||
public.data["context"],
|
||||
Map.put(fetch_opts, :exclude_id, public.id)
|
||||
)
|
||||
|
||||
private_context =
|
||||
ActivityPub.fetch_activities_for_context(
|
||||
private.data["context"],
|
||||
Map.put(fetch_opts, :exclude_id, private.id)
|
||||
)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"render" => fn opts ->
|
||||
StatusView.render("context.json", opts)
|
||||
end
|
||||
},
|
||||
inputs: %{
|
||||
"Public context" => %{user: user, activity: public_activity, activities: public_context},
|
||||
"Private context" => %{
|
||||
user: user,
|
||||
activity: private_activity,
|
||||
activities: private_context
|
||||
}
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
|
||||
defp fetch_timelines_with_reply_filtering(user) do
|
||||
public_params = opts_for_public_timeline(user)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Public timeline without reply filtering" => fn ->
|
||||
ActivityPub.fetch_public_activities(public_params)
|
||||
end,
|
||||
"Public timeline with reply filtering - following" => fn ->
|
||||
public_params
|
||||
|> Map.put(:reply_visibility, "following")
|
||||
|> Map.put(:reply_filtering_user, user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
end,
|
||||
"Public timeline with reply filtering - self" => fn ->
|
||||
public_params
|
||||
|> Map.put(:reply_visibility, "self")
|
||||
|> Map.put(:reply_filtering_user, user)
|
||||
|> ActivityPub.fetch_public_activities()
|
||||
end
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
|
||||
private_params = opts_for_home_timeline(user)
|
||||
|
||||
recipients = [user.ap_id | User.following(user)]
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Home timeline without reply filtering" => fn ->
|
||||
ActivityPub.fetch_activities(recipients, private_params)
|
||||
end,
|
||||
"Home timeline with reply filtering - following" => fn ->
|
||||
private_params =
|
||||
private_params
|
||||
|> Map.put(:reply_filtering_user, user)
|
||||
|> Map.put(:reply_visibility, "following")
|
||||
|
||||
ActivityPub.fetch_activities(recipients, private_params)
|
||||
end,
|
||||
"Home timeline with reply filtering - self" => fn ->
|
||||
private_params =
|
||||
private_params
|
||||
|> Map.put(:reply_filtering_user, user)
|
||||
|> Map.put(:reply_visibility, "self")
|
||||
|
||||
ActivityPub.fetch_activities(recipients, private_params)
|
||||
end
|
||||
},
|
||||
formatters: formatters()
|
||||
)
|
||||
end
|
||||
end
|
14
benchmarks/load_testing/helper.ex
Normal file
14
benchmarks/load_testing/helper.ex
Normal file
@ -0,0 +1,14 @@
|
||||
defmodule Pleroma.LoadTesting.Helper do
|
||||
alias Ecto.Adapters.SQL
|
||||
alias Pleroma.Repo
|
||||
|
||||
def to_sec(microseconds), do: microseconds / 1_000_000
|
||||
|
||||
def clean_tables do
|
||||
IO.puts("Deleting old data...\n")
|
||||
SQL.query!(Repo, "TRUNCATE users CASCADE;")
|
||||
SQL.query!(Repo, "TRUNCATE activities CASCADE;")
|
||||
SQL.query!(Repo, "TRUNCATE objects CASCADE;")
|
||||
SQL.query!(Repo, "TRUNCATE oban_jobs CASCADE;")
|
||||
end
|
||||
end
|
189
benchmarks/load_testing/users.ex
Normal file
189
benchmarks/load_testing/users.ex
Normal file
@ -0,0 +1,189 @@
|
||||
defmodule Pleroma.LoadTesting.Users do
|
||||
@moduledoc """
|
||||
Module for generating users with friends.
|
||||
"""
|
||||
import Ecto.Query
|
||||
import Pleroma.LoadTesting.Helper, only: [to_sec: 1]
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
alias Pleroma.User.Query
|
||||
|
||||
@defaults [
|
||||
users: 20_000,
|
||||
friends: 100
|
||||
]
|
||||
|
||||
@max_concurrency 10
|
||||
|
||||
@spec generate(keyword()) :: User.t()
|
||||
def generate(opts \\ []) do
|
||||
opts = Keyword.merge(@defaults, opts)
|
||||
|
||||
generate_users(opts[:users])
|
||||
|
||||
main_user =
|
||||
Repo.one(from(u in User, where: u.local == true, order_by: fragment("RANDOM()"), limit: 1))
|
||||
|
||||
make_friends(main_user, opts[:friends])
|
||||
|
||||
User.get_by_id(main_user.id)
|
||||
end
|
||||
|
||||
def generate_users(max) do
|
||||
IO.puts("Starting generating #{max} users...")
|
||||
|
||||
{time, users} =
|
||||
:timer.tc(fn ->
|
||||
Task.async_stream(
|
||||
1..max,
|
||||
&generate_user(&1),
|
||||
max_concurrency: @max_concurrency,
|
||||
timeout: 30_000
|
||||
)
|
||||
|> Enum.to_list()
|
||||
end)
|
||||
|
||||
IO.puts("Generating users took #{to_sec(time)} sec.\n")
|
||||
users
|
||||
end
|
||||
|
||||
defp generate_user(i) do
|
||||
remote = Enum.random([true, false])
|
||||
|
||||
%User{
|
||||
name: "Test テスト User #{i}",
|
||||
email: "user#{i}@example.com",
|
||||
nickname: "nick#{i}",
|
||||
password_hash: Pleroma.Password.Pbkdf2.hash_pwd_salt("test"),
|
||||
bio: "Tester Number #{i}",
|
||||
local: !remote
|
||||
}
|
||||
|> user_urls()
|
||||
|> Repo.insert!()
|
||||
end
|
||||
|
||||
defp user_urls(%{local: true} = user) do
|
||||
urls = %{
|
||||
ap_id: User.ap_id(user),
|
||||
follower_address: User.ap_followers(user),
|
||||
following_address: User.ap_following(user)
|
||||
}
|
||||
|
||||
Map.merge(user, urls)
|
||||
end
|
||||
|
||||
defp user_urls(%{local: false} = user) do
|
||||
base_domain = Enum.random(["domain1.com", "domain2.com", "domain3.com"])
|
||||
|
||||
ap_id = "https://#{base_domain}/users/#{user.nickname}"
|
||||
|
||||
urls = %{
|
||||
ap_id: ap_id,
|
||||
follower_address: ap_id <> "/followers",
|
||||
following_address: ap_id <> "/following"
|
||||
}
|
||||
|
||||
Map.merge(user, urls)
|
||||
end
|
||||
|
||||
def make_friends(main_user, max) when is_integer(max) do
|
||||
IO.puts("Starting making friends for #{max} users...")
|
||||
|
||||
{time, _} =
|
||||
:timer.tc(fn ->
|
||||
number_of_users =
|
||||
(max / 2)
|
||||
|> Kernel.trunc()
|
||||
|
||||
main_user
|
||||
|> get_users(%{limit: number_of_users, local: :local})
|
||||
|> run_stream(main_user)
|
||||
|
||||
main_user
|
||||
|> get_users(%{limit: number_of_users, local: :external})
|
||||
|> run_stream(main_user)
|
||||
end)
|
||||
|
||||
IO.puts("Making friends took #{to_sec(time)} sec.\n")
|
||||
end
|
||||
|
||||
def make_friends(%User{} = main_user, %User{} = user) do
|
||||
{:ok, _, _} = User.follow(main_user, user)
|
||||
{:ok, _, _} = User.follow(user, main_user)
|
||||
end
|
||||
|
||||
@spec get_users(User.t(), keyword()) :: [User.t()]
|
||||
def get_users(user, opts) do
|
||||
criteria = %{limit: opts[:limit]}
|
||||
|
||||
criteria =
|
||||
if opts[:local] do
|
||||
Map.put(criteria, opts[:local], true)
|
||||
else
|
||||
criteria
|
||||
end
|
||||
|
||||
criteria =
|
||||
if opts[:friends?] do
|
||||
Map.put(criteria, :friends, user)
|
||||
else
|
||||
criteria
|
||||
end
|
||||
|
||||
query =
|
||||
criteria
|
||||
|> Query.build()
|
||||
|> random_without_user(user)
|
||||
|
||||
query =
|
||||
if opts[:friends?] == false do
|
||||
friends_ids =
|
||||
%{friends: user}
|
||||
|> Query.build()
|
||||
|> Repo.all()
|
||||
|> Enum.map(& &1.id)
|
||||
|
||||
from(u in query, where: u.id not in ^friends_ids)
|
||||
else
|
||||
query
|
||||
end
|
||||
|
||||
Repo.all(query)
|
||||
end
|
||||
|
||||
defp random_without_user(query, user) do
|
||||
from(u in query,
|
||||
where: u.id != ^user.id,
|
||||
order_by: fragment("RANDOM()")
|
||||
)
|
||||
end
|
||||
|
||||
defp run_stream(users, main_user) do
|
||||
Task.async_stream(users, &make_friends(main_user, &1),
|
||||
max_concurrency: @max_concurrency,
|
||||
timeout: 30_000
|
||||
)
|
||||
|> Stream.run()
|
||||
end
|
||||
|
||||
@spec prepare_users(User.t(), keyword()) :: map()
|
||||
def prepare_users(user, opts) do
|
||||
friends_limit = opts[:friends_used]
|
||||
non_friends_limit = opts[:non_friends_used]
|
||||
|
||||
%{
|
||||
user: user,
|
||||
friends_local: fetch_users(user, friends_limit, :local, true),
|
||||
friends_remote: fetch_users(user, friends_limit, :external, true),
|
||||
non_friends_local: fetch_users(user, non_friends_limit, :local, false),
|
||||
non_friends_remote: fetch_users(user, non_friends_limit, :external, false)
|
||||
}
|
||||
end
|
||||
|
||||
defp fetch_users(user, limit, local, friends?) do
|
||||
user
|
||||
|> get_users(limit: limit, local: local, friends?: friends?)
|
||||
|> Enum.shuffle()
|
||||
end
|
||||
end
|
125
benchmarks/mix/tasks/pleroma/benchmark.ex
Normal file
125
benchmarks/mix/tasks/pleroma/benchmark.ex
Normal file
@ -0,0 +1,125 @@
|
||||
# Pleroma: A lightweight social networking server
|
||||
# Copyright © 2017-2022 Pleroma Authors <https://pleroma.social/>
|
||||
# SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
defmodule Mix.Tasks.Pleroma.Benchmark do
|
||||
@shortdoc "Benchmarks"
|
||||
@moduledoc """
|
||||
Benchmark tasks available:
|
||||
|
||||
adapters
|
||||
render_timeline
|
||||
search
|
||||
tag
|
||||
|
||||
MIX_ENV=benchmark mix pleroma.benchmark adapters
|
||||
"""
|
||||
|
||||
use Mix.Task
|
||||
import Mix.Pleroma
|
||||
|
||||
def run(["search"]) do
|
||||
start_pleroma()
|
||||
|
||||
Benchee.run(%{
|
||||
"search" => fn ->
|
||||
Pleroma.Activity.search(nil, "cofe")
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
def run(["tag"]) do
|
||||
start_pleroma()
|
||||
|
||||
Benchee.run(%{
|
||||
"tag" => fn ->
|
||||
%{"type" => "Create", "tag" => "cofe"}
|
||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
def run(["render_timeline", nickname | _] = args) do
|
||||
start_pleroma()
|
||||
user = Pleroma.User.get_by_nickname(nickname)
|
||||
|
||||
activities =
|
||||
%{}
|
||||
|> Map.put("type", ["Create", "Announce"])
|
||||
|> Map.put("blocking_user", user)
|
||||
|> Map.put("muting_user", user)
|
||||
|> Map.put("user", user)
|
||||
|> Map.put("limit", 4096)
|
||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||
|> Enum.reverse()
|
||||
|
||||
inputs = %{
|
||||
"1 activity" => Enum.take_random(activities, 1),
|
||||
"10 activities" => Enum.take_random(activities, 10),
|
||||
"20 activities" => Enum.take_random(activities, 20),
|
||||
"40 activities" => Enum.take_random(activities, 40),
|
||||
"80 activities" => Enum.take_random(activities, 80)
|
||||
}
|
||||
|
||||
inputs =
|
||||
if Enum.at(args, 2) == "extended" do
|
||||
Map.merge(inputs, %{
|
||||
"200 activities" => Enum.take_random(activities, 200),
|
||||
"500 activities" => Enum.take_random(activities, 500),
|
||||
"2000 activities" => Enum.take_random(activities, 2000),
|
||||
"4096 activities" => Enum.take_random(activities, 4096)
|
||||
})
|
||||
else
|
||||
inputs
|
||||
end
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Standard rendering" => fn activities ->
|
||||
Pleroma.Web.MastodonAPI.StatusView.render("index.json", %{
|
||||
activities: activities,
|
||||
for: user,
|
||||
as: :activity
|
||||
})
|
||||
end
|
||||
},
|
||||
inputs: inputs
|
||||
)
|
||||
end
|
||||
|
||||
def run(["adapters"]) do
|
||||
start_pleroma()
|
||||
|
||||
:ok =
|
||||
Pleroma.Gun.Conn.open(
|
||||
"https://httpbin.org/stream-bytes/1500",
|
||||
:gun_connections
|
||||
)
|
||||
|
||||
Process.sleep(1_500)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Without conn and without pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [],
|
||||
pool: :no_pool,
|
||||
receive_conn: false
|
||||
)
|
||||
end,
|
||||
"Without conn and with pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [], receive_conn: false)
|
||||
end,
|
||||
"With reused conn and without pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} =
|
||||
Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500", [], pool: :no_pool)
|
||||
end,
|
||||
"With reused conn and with pool" => fn ->
|
||||
{:ok, %Tesla.Env{}} = Pleroma.HTTP.get("https://httpbin.org/stream-bytes/1500")
|
||||
end
|
||||
},
|
||||
parallel: 10
|
||||
)
|
||||
end
|
||||
end
|
114
benchmarks/mix/tasks/pleroma/benchmarks/tags.ex
Normal file
114
benchmarks/mix/tasks/pleroma/benchmarks/tags.ex
Normal file
@ -0,0 +1,114 @@
|
||||
defmodule Mix.Tasks.Pleroma.Benchmarks.Tags do
|
||||
use Mix.Task
|
||||
|
||||
import Pleroma.LoadTesting.Helper, only: [clean_tables: 0]
|
||||
import Ecto.Query
|
||||
|
||||
alias Pleroma.Repo
|
||||
|
||||
def run(_args) do
|
||||
Mix.Pleroma.start_pleroma()
|
||||
activities_count = Repo.aggregate(from(a in Pleroma.Activity), :count, :id)
|
||||
|
||||
if activities_count == 0 do
|
||||
IO.puts("Did not find any activities, cleaning and generating")
|
||||
clean_tables()
|
||||
Pleroma.LoadTesting.Users.generate_users(10)
|
||||
Pleroma.LoadTesting.Activities.generate_tagged_activities()
|
||||
else
|
||||
IO.puts("Found #{activities_count} activities, won't generate new ones")
|
||||
end
|
||||
|
||||
tags = Enum.map(0..20, fn i -> {"For #tag_#{i}", "tag_#{i}"} end)
|
||||
|
||||
Enum.each(tags, fn {_, tag} ->
|
||||
query =
|
||||
from(o in Pleroma.Object,
|
||||
where: fragment("(?)->'tag' \\? (?)", o.data, ^tag)
|
||||
)
|
||||
|
||||
count = Repo.aggregate(query, :count, :id)
|
||||
IO.puts("Database contains #{count} posts tagged with #{tag}")
|
||||
end)
|
||||
|
||||
user = Repo.all(Pleroma.User) |> List.first()
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Hashtag fetching, any" => fn tags ->
|
||||
hashtag_fetching(
|
||||
%{
|
||||
"any" => tags
|
||||
},
|
||||
user,
|
||||
false
|
||||
)
|
||||
end,
|
||||
# Will always return zero results because no overlapping hashtags are generated.
|
||||
"Hashtag fetching, all" => fn tags ->
|
||||
hashtag_fetching(
|
||||
%{
|
||||
"all" => tags
|
||||
},
|
||||
user,
|
||||
false
|
||||
)
|
||||
end
|
||||
},
|
||||
inputs:
|
||||
tags
|
||||
|> Enum.map(fn {_, v} -> v end)
|
||||
|> Enum.chunk_every(2)
|
||||
|> Enum.map(fn tags -> {"For #{inspect(tags)}", tags} end),
|
||||
time: 5
|
||||
)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"Hashtag fetching" => fn tag ->
|
||||
hashtag_fetching(
|
||||
%{
|
||||
"tag" => tag
|
||||
},
|
||||
user,
|
||||
false
|
||||
)
|
||||
end
|
||||
},
|
||||
inputs: tags,
|
||||
time: 5
|
||||
)
|
||||
end
|
||||
|
||||
defp hashtag_fetching(params, user, local_only) do
|
||||
tags =
|
||||
[params["tag"], params["any"]]
|
||||
|> List.flatten()
|
||||
|> Enum.uniq()
|
||||
|> Enum.filter(& &1)
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
tag_all =
|
||||
params
|
||||
|> Map.get("all", [])
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
tag_reject =
|
||||
params
|
||||
|> Map.get("none", [])
|
||||
|> Enum.map(&String.downcase(&1))
|
||||
|
||||
_activities =
|
||||
%{
|
||||
type: "Create",
|
||||
local_only: local_only,
|
||||
blocking_user: user,
|
||||
muting_user: user,
|
||||
user: user,
|
||||
tag: tags,
|
||||
tag_all: tag_all,
|
||||
tag_reject: tag_reject,
|
||||
}
|
||||
|> Pleroma.Web.ActivityPub.ActivityPub.fetch_public_activities()
|
||||
end
|
||||
end
|
70
benchmarks/mix/tasks/pleroma/benchmarks/timelines.ex
Normal file
70
benchmarks/mix/tasks/pleroma/benchmarks/timelines.ex
Normal file
@ -0,0 +1,70 @@
|
||||
defmodule Mix.Tasks.Pleroma.Benchmarks.Timelines do
|
||||
use Mix.Task
|
||||
|
||||
import Pleroma.LoadTesting.Helper, only: [clean_tables: 0]
|
||||
|
||||
alias Pleroma.Web.CommonAPI
|
||||
alias Plug.Conn
|
||||
|
||||
def run(_args) do
|
||||
Mix.Pleroma.start_pleroma()
|
||||
|
||||
# Cleaning tables
|
||||
clean_tables()
|
||||
|
||||
[{:ok, user} | users] = Pleroma.LoadTesting.Users.generate_users(1000)
|
||||
|
||||
# Let the user make 100 posts
|
||||
|
||||
1..100
|
||||
|> Enum.each(fn i -> CommonAPI.post(user, %{status: to_string(i)}) end)
|
||||
|
||||
# Let 10 random users post
|
||||
posts =
|
||||
users
|
||||
|> Enum.take_random(10)
|
||||
|> Enum.map(fn {:ok, random_user} ->
|
||||
{:ok, activity} = CommonAPI.post(random_user, %{status: "."})
|
||||
activity
|
||||
end)
|
||||
|
||||
# let our user repeat them
|
||||
posts
|
||||
|> Enum.each(fn activity ->
|
||||
CommonAPI.repeat(activity.id, user)
|
||||
end)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"user timeline, no followers" => fn reading_user ->
|
||||
conn =
|
||||
Phoenix.ConnTest.build_conn()
|
||||
|> Conn.assign(:user, reading_user)
|
||||
|> Conn.assign(:skip_link_headers, true)
|
||||
|
||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||
end
|
||||
},
|
||||
inputs: %{"user" => user, "no user" => nil},
|
||||
time: 60
|
||||
)
|
||||
|
||||
users
|
||||
|> Enum.each(fn {:ok, follower} -> Pleroma.User.follow(follower, user) end)
|
||||
|
||||
Benchee.run(
|
||||
%{
|
||||
"user timeline, all following" => fn reading_user ->
|
||||
conn =
|
||||
Phoenix.ConnTest.build_conn()
|
||||
|> Conn.assign(:user, reading_user)
|
||||
|> Conn.assign(:skip_link_headers, true)
|
||||
|
||||
Pleroma.Web.MastodonAPI.AccountController.statuses(conn, %{id: user.id})
|
||||
end
|
||||
},
|
||||
inputs: %{"user" => user, "no user" => nil},
|
||||
time: 60
|
||||
)
|
||||
end
|
||||
end
|
67
benchmarks/mix/tasks/pleroma/load_testing.ex
Normal file
67
benchmarks/mix/tasks/pleroma/load_testing.ex
Normal file
@ -0,0 +1,67 @@
|
||||
defmodule Mix.Tasks.Pleroma.LoadTesting do
|
||||
use Mix.Task
|
||||
import Ecto.Query
|
||||
import Pleroma.LoadTesting.Helper, only: [clean_tables: 0]
|
||||
|
||||
alias Pleroma.Repo
|
||||
alias Pleroma.User
|
||||
|
||||
@shortdoc "Factory for generation data"
|
||||
@moduledoc """
|
||||
Generates data like:
|
||||
- local/remote users
|
||||
- local/remote activities with differrent visibility:
|
||||
- simple activiities
|
||||
- with emoji
|
||||
- with mentions
|
||||
- hellthreads
|
||||
- with attachments
|
||||
- with tags
|
||||
- likes
|
||||
- reblogs
|
||||
- simple threads
|
||||
- long threads
|
||||
|
||||
## Generate data
|
||||
MIX_ENV=benchmark mix pleroma.load_testing --users 20000 --friends 1000 --iterations 170 --friends_used 20 --non_friends_used 20
|
||||
MIX_ENV=benchmark mix pleroma.load_testing -u 20000 -f 1000 -i 170 -fu 20 -nfu 20
|
||||
|
||||
Options:
|
||||
- `--users NUMBER` - number of users to generate. Defaults to: 20000. Alias: `-u`
|
||||
- `--friends NUMBER` - number of friends for main user. Defaults to: 1000. Alias: `-f`
|
||||
- `--iterations NUMBER` - number of iterations to generate activities. For each iteration in database is inserted about 120+ activities with different visibility, actors and types.Defaults to: 170. Alias: `-i`
|
||||
- `--friends_used NUMBER` - number of main user friends used in activity generation. Defaults to: 20. Alias: `-fu`
|
||||
- `--non_friends_used NUMBER` - number of non friends used in activity generation. Defaults to: 20. Alias: `-nfu`
|
||||
"""
|
||||
|
||||
@aliases [u: :users, f: :friends, i: :iterations, fu: :friends_used, nfu: :non_friends_used]
|
||||
@switches [
|
||||
users: :integer,
|
||||
friends: :integer,
|
||||
iterations: :integer,
|
||||
friends_used: :integer,
|
||||
non_friends_used: :integer
|
||||
]
|
||||
|
||||
def run(args) do
|
||||
Logger.configure(level: :error)
|
||||
Mix.Pleroma.start_pleroma()
|
||||
clean_tables()
|
||||
{opts, _} = OptionParser.parse!(args, strict: @switches, aliases: @aliases)
|
||||
|
||||
user = Pleroma.LoadTesting.Users.generate(opts)
|
||||
Pleroma.LoadTesting.Activities.generate(user, opts)
|
||||
|
||||
IO.puts("Users in DB: #{Repo.aggregate(from(u in User), :count, :id)}")
|
||||
|
||||
IO.puts("Activities in DB: #{Repo.aggregate(from(a in Pleroma.Activity), :count, :id)}")
|
||||
|
||||
IO.puts("Objects in DB: #{Repo.aggregate(from(o in Pleroma.Object), :count, :id)}")
|
||||
|
||||
IO.puts(
|
||||
"Notifications in DB: #{Repo.aggregate(from(n in Pleroma.Notification), :count, :id)}"
|
||||
)
|
||||
|
||||
Pleroma.LoadTesting.Fetcher.run_benchmarks(user)
|
||||
end
|
||||
end
|
0
changelog.d/2.6.0-mergeback.skip
Normal file
0
changelog.d/2.6.0-mergeback.skip
Normal file
1
changelog.d/3895.add
Normal file
1
changelog.d/3895.add
Normal file
@ -0,0 +1 @@
|
||||
Uploaded media content-type is scrubbed before serving the files to limit the security impact of some attachments
|
1
changelog.d/3896.add
Normal file
1
changelog.d/3896.add
Normal file
@ -0,0 +1 @@
|
||||
Validate Host header for Uploads and return a 302 if the base_url has changed
|
1
changelog.d/3900.change
Normal file
1
changelog.d/3900.change
Normal file
@ -0,0 +1 @@
|
||||
Update to Phoenix 1.7
|
1
changelog.d/akkoma-xml-remote-entities.security
Normal file
1
changelog.d/akkoma-xml-remote-entities.security
Normal file
@ -0,0 +1 @@
|
||||
Fix XML External Entity (XXE) loading vulnerability allowing to fetch arbitary files from the server's filesystem
|
1
changelog.d/authorize-interaction.add
Normal file
1
changelog.d/authorize-interaction.add
Normal file
@ -0,0 +1 @@
|
||||
Support /authorize-interaction route used by Mastodon
|
0
changelog.d/bare_uri_test.skip
Normal file
0
changelog.d/bare_uri_test.skip
Normal file
0
changelog.d/benchee.skip
Normal file
0
changelog.d/benchee.skip
Normal file
1
changelog.d/check-attachment-attribution.security
Normal file
1
changelog.d/check-attachment-attribution.security
Normal file
@ -0,0 +1 @@
|
||||
CommonAPI: Prevent users from accessing media of other users by creating a status with reused attachment ID
|
1
changelog.d/digest_emails.fix
Normal file
1
changelog.d/digest_emails.fix
Normal file
@ -0,0 +1 @@
|
||||
Fix the processing of email digest jobs.
|
1
changelog.d/docs-max-elixir-erlang.change
Normal file
1
changelog.d/docs-max-elixir-erlang.change
Normal file
@ -0,0 +1 @@
|
||||
- Document maximum supported version of Erlang & Elixir
|
1
changelog.d/emoji-pack-sanitization.security
Normal file
1
changelog.d/emoji-pack-sanitization.security
Normal file
@ -0,0 +1 @@
|
||||
Emoji pack loader sanitizes pack names
|
1
changelog.d/federation_status-access.change
Normal file
1
changelog.d/federation_status-access.change
Normal file
@ -0,0 +1 @@
|
||||
- Make `/api/v1/pleroma/federation_status` publicly available
|
1
changelog.d/healthcheck-disabled-error.fix
Normal file
1
changelog.d/healthcheck-disabled-error.fix
Normal file
@ -0,0 +1 @@
|
||||
TwitterAPI: Return proper error when healthcheck is disabled
|
1
changelog.d/meilisearch.add
Normal file
1
changelog.d/meilisearch.add
Normal file
@ -0,0 +1 @@
|
||||
Add meilisearch, make search engines pluggable
|
1
changelog.d/nginx-media-redirect-fix.fix
Normal file
1
changelog.d/nginx-media-redirect-fix.fix
Normal file
@ -0,0 +1 @@
|
||||
Fixing 302 redirect issue with media
|
1
changelog.d/otp_perms.security
Normal file
1
changelog.d/otp_perms.security
Normal file
@ -0,0 +1 @@
|
||||
- Reduced permissions of config files and directories, distros requiring greater permissions like group-read need to pre-create the directories
|
0
changelog.d/quotes-count.skip
Normal file
0
changelog.d/quotes-count.skip
Normal file
1
changelog.d/scrobble-url.add
Normal file
1
changelog.d/scrobble-url.add
Normal file
@ -0,0 +1 @@
|
||||
Adds the capability to add a URL to a scrobble (optional field)
|
1
changelog.d/system-cflags.fix
Normal file
1
changelog.d/system-cflags.fix
Normal file
@ -0,0 +1 @@
|
||||
- Fix eblurhash and elixir-captcha not using system cflags
|
8
ci/Dockerfile
Normal file
8
ci/Dockerfile
Normal file
@ -0,0 +1,8 @@
|
||||
FROM elixir:1.12.3
|
||||
|
||||
# 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
ci/build_and_push.sh
Executable file
1
ci/build_and_push.sh
Executable file
@ -0,0 +1 @@
|
||||
docker build -t gitlab.com/soapbox-pub/rebased/ci:latest --push .
|
88
config/benchmark.exs
Normal file
88
config/benchmark.exs
Normal file
@ -0,0 +1,88 @@
|
||||
import Config
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [port: 4001],
|
||||
url: [port: 4001]
|
||||
|
||||
# Disable captha for tests
|
||||
config :pleroma, Pleroma.Captcha,
|
||||
# It should not be enabled for automatic tests
|
||||
enabled: false,
|
||||
# A fake captcha service for tests
|
||||
method: Pleroma.Captcha.Mock
|
||||
|
||||
# Print only warnings and errors during test
|
||||
config :logger, level: :warn
|
||||
|
||||
config :pleroma, :auth, oauth_consumer_strategies: []
|
||||
|
||||
config :pleroma, Pleroma.Upload, filters: [], link_name: false
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
||||
|
||||
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
|
||||
|
||||
config :pleroma, :instance,
|
||||
email: "admin@example.com",
|
||||
notify_email: "noreply@example.com",
|
||||
skip_thread_containment: false,
|
||||
federating: false,
|
||||
external_user_synchronization: false
|
||||
|
||||
config :pleroma, :activitypub, sign_object_fetches: false
|
||||
|
||||
# Configure your database
|
||||
config :pleroma, Pleroma.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "pleroma_benchmark",
|
||||
hostname: System.get_env("DB_HOST") || "localhost",
|
||||
port: System.get_env("DB_PORT") || "5432",
|
||||
pool_size: 10
|
||||
|
||||
# Reduce hash rounds for testing
|
||||
config :pleroma, :password, iterations: 1
|
||||
|
||||
config :tesla, adapter: Tesla.Mock
|
||||
|
||||
config :pleroma, :rich_media,
|
||||
enabled: false,
|
||||
ignore_hosts: [],
|
||||
ignore_tld: ["local", "localdomain", "lan"]
|
||||
|
||||
config :web_push_encryption, :vapid_details,
|
||||
subject: "mailto:administrator@example.com",
|
||||
public_key:
|
||||
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
||||
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
||||
|
||||
config :pleroma, Pleroma.ScheduledActivity,
|
||||
daily_user_limit: 2,
|
||||
total_user_limit: 3,
|
||||
enabled: false
|
||||
|
||||
config :pleroma, :rate_limit,
|
||||
search: [{1000, 30}, {1000, 30}],
|
||||
app_account_creation: {10_000, 5},
|
||||
password_reset: {1000, 30}
|
||||
|
||||
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||
|
||||
config :pleroma, :http, send_user_agent: false
|
||||
|
||||
rum_enabled = System.get_env("RUM_ENABLED") == "true"
|
||||
config :pleroma, :database, rum_enabled: rum_enabled
|
||||
IO.puts("RUM enabled: #{rum_enabled}")
|
||||
|
||||
config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
|
||||
|
||||
if File.exists?("./config/benchmark.secret.exs") do
|
||||
import_config "benchmark.secret.exs"
|
||||
else
|
||||
IO.puts(
|
||||
"You may want to create benchmark.secret.exs to declare custom database connection parameters."
|
||||
)
|
||||
end
|
958
config/config.exs
Normal file
958
config/config.exs
Normal file
@ -0,0 +1,958 @@
|
||||
# .i;;;;i.
|
||||
# iYcviii;vXY:
|
||||
# .YXi .i1c.
|
||||
# .YC. . in7.
|
||||
# .vc. ...... ;1c.
|
||||
# i7, .. .;1;
|
||||
# i7, .. ... .Y1i
|
||||
# ,7v .6MMM@; .YX,
|
||||
# .7;. ..IMMMMMM1 :t7.
|
||||
# .;Y. ;$MMMMMM9. :tc.
|
||||
# vY. .. .nMMM@MMU. ;1v.
|
||||
# i7i ... .#MM@M@C. .....:71i
|
||||
# it: .... $MMM@9;.,i;;;i,;tti
|
||||
# :t7. ..... 0MMMWv.,iii:::,,;St.
|
||||
# .nC. ..... IMMMQ..,::::::,.,czX.
|
||||
# .ct: ....... .ZMMMI..,:::::::,,:76Y.
|
||||
# c2: ......,i..Y$M@t..:::::::,,..inZY
|
||||
# vov ......:ii..c$MBc..,,,,,,,,,,..iI9i
|
||||
# i9Y ......iii:..7@MA,..,,,,,,,,,....;AA:
|
||||
# iIS. ......:ii::..;@MI....,............;Ez.
|
||||
# .I9. ......:i::::...8M1..................C0z.
|
||||
# .z9; ......:i::::,.. .i:...................zWX.
|
||||
# vbv ......,i::::,,. ................. :AQY
|
||||
# c6Y. .,...,::::,,..:t0@@QY. ................ :8bi
|
||||
# :6S. ..,,...,:::,,,..EMMMMMMI. ............... .;bZ,
|
||||
# :6o, .,,,,..:::,,,..i#MMMMMM#v................. YW2.
|
||||
# .n8i ..,,,,,,,::,,,,.. tMMMMM@C:.................. .1Wn
|
||||
# 7Uc. .:::,,,,,::,,,,.. i1t;,..................... .UEi
|
||||
# 7C...::::::::::::,,,,.. .................... vSi.
|
||||
# ;1;...,,::::::,......... .................. Yz:
|
||||
# v97,......... .voC.
|
||||
# izAotX7777777777777777777777777777777777777777Y7n92:
|
||||
# .;CoIIIIIUAA666666699999ZZZZZZZZZZZZZZZZZZZZ6ov.
|
||||
#
|
||||
# !!! ATTENTION !!!
|
||||
# DO NOT EDIT THIS FILE! THIS FILE CONTAINS THE DEFAULT VALUES FOR THE CON-
|
||||
# FIGURATION! EDIT YOUR SECRET FILE (either prod.secret.exs, dev.secret.exs).
|
||||
#
|
||||
# This file is responsible for configuring your application
|
||||
# and its dependencies with the aid of the Config module.
|
||||
#
|
||||
# This configuration file is loaded before any dependency and
|
||||
# is restricted to this project.
|
||||
import Config
|
||||
|
||||
# General application configuration
|
||||
config :pleroma, ecto_repos: [Pleroma.Repo]
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
telemetry_event: [Pleroma.Repo.Instrumenter],
|
||||
migration_lock: nil
|
||||
|
||||
config :pleroma, Pleroma.Captcha,
|
||||
enabled: true,
|
||||
seconds_valid: 300,
|
||||
method: Pleroma.Captcha.Native
|
||||
|
||||
config :pleroma, Pleroma.Captcha.Kocaptcha, endpoint: "https://captcha.kotobank.ch"
|
||||
|
||||
# Upload configuration
|
||||
config :pleroma, Pleroma.Upload,
|
||||
uploader: Pleroma.Uploaders.Local,
|
||||
filters: [Pleroma.Upload.Filter.Dedupe],
|
||||
link_name: false,
|
||||
proxy_remote: false,
|
||||
filename_display_max_length: 30,
|
||||
default_description: nil,
|
||||
base_url: nil
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.S3,
|
||||
bucket: nil,
|
||||
bucket_namespace: nil,
|
||||
truncated_namespace: nil,
|
||||
streaming_enabled: true
|
||||
|
||||
config :ex_aws, :s3,
|
||||
# host: "s3.wasabisys.com", # required if not Amazon AWS
|
||||
access_key_id: nil,
|
||||
secret_access_key: nil,
|
||||
# region: "us-east-1", # may be required for Amazon AWS
|
||||
scheme: "https://"
|
||||
|
||||
config :pleroma, :emoji,
|
||||
shortcode_globs: ["/emoji/custom/**/*.png"],
|
||||
pack_extensions: [".png", ".gif"],
|
||||
groups: [
|
||||
Custom: ["/emoji/*.png", "/emoji/**/*.png"]
|
||||
],
|
||||
default_manifest: "https://git.pleroma.social/pleroma/emoji-index/raw/master/index.json",
|
||||
shared_pack_cache_seconds_per_file: 60
|
||||
|
||||
config :pleroma, :uri_schemes,
|
||||
valid_schemes: [
|
||||
"https",
|
||||
"http",
|
||||
"dat",
|
||||
"dweb",
|
||||
"gopher",
|
||||
"hyper",
|
||||
"ipfs",
|
||||
"ipns",
|
||||
"irc",
|
||||
"ircs",
|
||||
"magnet",
|
||||
"mailto",
|
||||
"mumble",
|
||||
"ssb",
|
||||
"xmpp"
|
||||
]
|
||||
|
||||
# Configures the endpoint
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
http: [
|
||||
ip: {127, 0, 0, 1},
|
||||
dispatch: [
|
||||
{:_,
|
||||
[
|
||||
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
||||
{:_, Plug.Cowboy.Handler, {Pleroma.Web.Endpoint, []}}
|
||||
]}
|
||||
]
|
||||
],
|
||||
protocol: "https",
|
||||
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
||||
live_view: [signing_salt: "U5ELgdEwTD3n1+D5s0rY0AMg8/y1STxZ3Zvsl3bWh+oBcGrYdil0rXqPMRd3Glcq"],
|
||||
signing_salt: "CqaoopA2",
|
||||
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
|
||||
pubsub_server: Pleroma.PubSub,
|
||||
secure_cookie_flag: true,
|
||||
extra_cookie_attrs: [
|
||||
"SameSite=Lax"
|
||||
]
|
||||
|
||||
# Configures Elixir's Logger
|
||||
config :logger, :console,
|
||||
level: :debug,
|
||||
format: "\n$time $metadata[$level] $message\n",
|
||||
metadata: [:request_id]
|
||||
|
||||
config :logger, :ex_syslogger,
|
||||
level: :debug,
|
||||
ident: "pleroma",
|
||||
format: "$metadata[$level] $message",
|
||||
metadata: [:request_id]
|
||||
|
||||
config :mime, :types, %{
|
||||
"application/xml" => ["xml"],
|
||||
"application/xrd+xml" => ["xrd+xml"],
|
||||
"application/jrd+json" => ["jrd+json"],
|
||||
"application/activity+json" => ["activity+json"],
|
||||
"application/ld+json" => ["activity+json"]
|
||||
}
|
||||
|
||||
config :tesla, adapter: Tesla.Adapter.Hackney
|
||||
|
||||
# Configures http settings, upstream proxy etc.
|
||||
config :pleroma, :http,
|
||||
proxy_url: nil,
|
||||
send_user_agent: true,
|
||||
user_agent: :default,
|
||||
adapter: []
|
||||
|
||||
config :pleroma, :instance,
|
||||
name: "Pleroma",
|
||||
email: "example@example.com",
|
||||
notify_email: "noreply@example.com",
|
||||
description: "Pleroma: An efficient and flexible fediverse server",
|
||||
short_description: "",
|
||||
background_image: "/images/city.jpg",
|
||||
instance_thumbnail: "/instance/thumbnail.png",
|
||||
favicon: "/favicon.png",
|
||||
limit: 5_000,
|
||||
description_limit: 5_000,
|
||||
remote_limit: 100_000,
|
||||
upload_limit: 16_000_000,
|
||||
avatar_upload_limit: 2_000_000,
|
||||
background_upload_limit: 4_000_000,
|
||||
banner_upload_limit: 4_000_000,
|
||||
poll_limits: %{
|
||||
max_options: 20,
|
||||
max_option_chars: 200,
|
||||
min_expiration: 0,
|
||||
max_expiration: 365 * 24 * 60 * 60
|
||||
},
|
||||
registrations_open: true,
|
||||
invites_enabled: false,
|
||||
account_activation_required: false,
|
||||
account_approval_required: true,
|
||||
federating: true,
|
||||
federation_incoming_replies_max_depth: 100,
|
||||
federation_reachability_timeout_days: 7,
|
||||
federation_publisher_modules: [
|
||||
Pleroma.Web.ActivityPub.Publisher
|
||||
],
|
||||
allow_relay: true,
|
||||
public: true,
|
||||
quarantined_instances: [],
|
||||
static_dir: "instance/static/",
|
||||
allowed_post_formats: [
|
||||
"text/plain",
|
||||
"text/html",
|
||||
"text/markdown",
|
||||
"text/bbcode"
|
||||
],
|
||||
autofollowed_nicknames: [],
|
||||
autofollowing_nicknames: [],
|
||||
max_pinned_statuses: 1,
|
||||
attachment_links: false,
|
||||
max_report_comment_size: 1000,
|
||||
report_strip_status: true,
|
||||
safe_dm_mentions: false,
|
||||
healthcheck: false,
|
||||
remote_post_retention_days: 90,
|
||||
skip_thread_containment: true,
|
||||
limit_to_local_content: :unauthenticated,
|
||||
user_bio_length: 5000,
|
||||
user_name_length: 100,
|
||||
user_location_length: 50,
|
||||
max_account_fields: 10,
|
||||
max_remote_account_fields: 20,
|
||||
account_field_name_length: 512,
|
||||
account_field_value_length: 2048,
|
||||
registration_reason_length: 500,
|
||||
external_user_synchronization: true,
|
||||
extended_nickname_format: true,
|
||||
cleanup_attachments: false,
|
||||
multi_factor_authentication: [
|
||||
totp: [
|
||||
# digits 6 or 8
|
||||
digits: 6,
|
||||
period: 30
|
||||
],
|
||||
backup_codes: [
|
||||
number: 5,
|
||||
length: 16
|
||||
]
|
||||
],
|
||||
show_reactions: true,
|
||||
password_reset_token_validity: 60 * 60 * 24,
|
||||
profile_directory: true,
|
||||
admin_privileges: [
|
||||
:users_read,
|
||||
:users_manage_invites,
|
||||
:users_manage_activation_state,
|
||||
:users_manage_tags,
|
||||
:users_manage_credentials,
|
||||
:users_delete,
|
||||
:messages_read,
|
||||
:messages_delete,
|
||||
:instances_delete,
|
||||
:reports_manage_reports,
|
||||
:moderation_log_read,
|
||||
:announcements_manage_announcements,
|
||||
:emoji_manage_emoji,
|
||||
:statistics_read
|
||||
],
|
||||
moderator_privileges: [
|
||||
:users_read,
|
||||
:users_manage_invites,
|
||||
:users_manage_activation_state,
|
||||
:users_manage_tags,
|
||||
:users_manage_credentials,
|
||||
:users_delete,
|
||||
:messages_read,
|
||||
:messages_delete,
|
||||
:instances_delete,
|
||||
:reports_manage_reports,
|
||||
:moderation_log_read,
|
||||
:announcements_manage_announcements,
|
||||
:emoji_manage_emoji,
|
||||
:statistics_read
|
||||
],
|
||||
max_endorsed_users: 20,
|
||||
birthday_required: false,
|
||||
birthday_min_age: 0,
|
||||
max_media_attachments: 1_000,
|
||||
migration_cooldown_period: 30
|
||||
|
||||
config :pleroma, :welcome,
|
||||
direct_message: [
|
||||
enabled: false,
|
||||
sender_nickname: nil,
|
||||
message: nil
|
||||
],
|
||||
chat_message: [
|
||||
enabled: false,
|
||||
sender_nickname: nil,
|
||||
message: nil
|
||||
],
|
||||
email: [
|
||||
enabled: false,
|
||||
sender: nil,
|
||||
subject: "Welcome to <%= instance_name %>",
|
||||
html: "Welcome to <%= instance_name %>",
|
||||
text: "Welcome to <%= instance_name %>"
|
||||
]
|
||||
|
||||
config :pleroma, :feed,
|
||||
post_title: %{
|
||||
max_length: 100,
|
||||
omission: "..."
|
||||
}
|
||||
|
||||
config :pleroma, :markup,
|
||||
allow_inline_images: false,
|
||||
allow_headings: false,
|
||||
allow_tables: false,
|
||||
allow_fonts: false,
|
||||
scrub_policy: [
|
||||
Pleroma.HTML.Scrubber.Default,
|
||||
Pleroma.HTML.Transform.MediaProxy
|
||||
]
|
||||
|
||||
config :pleroma, :frontend_configurations,
|
||||
pleroma_fe: %{
|
||||
alwaysShowSubjectInput: true,
|
||||
background: "/images/city.jpg",
|
||||
collapseMessageWithSubject: false,
|
||||
disableChat: false,
|
||||
greentext: false,
|
||||
hideFilteredStatuses: false,
|
||||
hideMutedPosts: false,
|
||||
hidePostStats: false,
|
||||
hideSitename: false,
|
||||
hideUserStats: false,
|
||||
loginMethod: "password",
|
||||
logo: "/static/logo.svg",
|
||||
logoMargin: ".1em",
|
||||
logoMask: true,
|
||||
minimalScopesMode: false,
|
||||
noAttachmentLinks: false,
|
||||
nsfwCensorImage: "",
|
||||
postContentType: "text/plain",
|
||||
redirectRootLogin: "/main/friends",
|
||||
redirectRootNoLogin: "/main/all",
|
||||
scopeCopy: true,
|
||||
sidebarRight: false,
|
||||
showFeaturesPanel: true,
|
||||
showInstanceSpecificPanel: false,
|
||||
subjectLineBehavior: "email",
|
||||
theme: "pleroma-dark",
|
||||
webPushNotifications: false
|
||||
}
|
||||
|
||||
config :pleroma, :assets,
|
||||
mascots: [
|
||||
pleroma_fox_tan: %{
|
||||
url: "/images/pleroma-fox-tan-smol.png",
|
||||
mime_type: "image/png"
|
||||
},
|
||||
pleroma_fox_tan_shy: %{
|
||||
url: "/images/pleroma-fox-tan-shy.png",
|
||||
mime_type: "image/png"
|
||||
}
|
||||
],
|
||||
default_mascot: :pleroma_fox_tan
|
||||
|
||||
config :pleroma, :manifest,
|
||||
icons: [
|
||||
%{
|
||||
src: "/static/logo.svg",
|
||||
type: "image/svg+xml"
|
||||
}
|
||||
],
|
||||
theme_color: "#282c37",
|
||||
background_color: "#191b22"
|
||||
|
||||
config :pleroma, :activitypub,
|
||||
unfollow_blocked: true,
|
||||
outgoing_blocks: true,
|
||||
blockers_visible: true,
|
||||
follow_handshake_timeout: 500,
|
||||
note_replies_output_limit: 5,
|
||||
sign_object_fetches: true,
|
||||
authorized_fetch_mode: false,
|
||||
fetch_actor_origin: nil
|
||||
|
||||
config :pleroma, :streamer,
|
||||
workers: 3,
|
||||
overflow_workers: 2
|
||||
|
||||
config :pleroma, :user, deny_follow_blocked: true
|
||||
|
||||
config :pleroma, :mrf_normalize_markup, scrub_policy: Pleroma.HTML.Scrubber.Default
|
||||
|
||||
config :pleroma, :mrf_rejectnonpublic,
|
||||
allow_followersonly: false,
|
||||
allow_direct: false
|
||||
|
||||
config :pleroma, :mrf_hellthread,
|
||||
delist_threshold: 10,
|
||||
reject_threshold: 20
|
||||
|
||||
config :pleroma, :mrf_simple,
|
||||
media_removal: [],
|
||||
media_nsfw: [],
|
||||
federated_timeline_removal: [],
|
||||
report_removal: [],
|
||||
reject: [],
|
||||
followers_only: [],
|
||||
accept: [],
|
||||
avatar_removal: [],
|
||||
banner_removal: [],
|
||||
reject_deletes: []
|
||||
|
||||
config :pleroma, :mrf_keyword,
|
||||
reject: [],
|
||||
federated_timeline_removal: [],
|
||||
replace: []
|
||||
|
||||
config :pleroma, :mrf_emoji,
|
||||
remove_url: [],
|
||||
remove_shortcode: [],
|
||||
federated_timeline_removal_url: [],
|
||||
federated_timeline_removal_shortcode: []
|
||||
|
||||
config :pleroma, :mrf_hashtag,
|
||||
sensitive: ["nsfw"],
|
||||
reject: [],
|
||||
federated_timeline_removal: []
|
||||
|
||||
config :pleroma, :mrf_subchain, match_actor: %{}
|
||||
|
||||
config :pleroma, :mrf_activity_expiration, days: 365
|
||||
|
||||
config :pleroma, :mrf_vocabulary,
|
||||
accept: [],
|
||||
reject: []
|
||||
|
||||
# threshold of 7 days
|
||||
config :pleroma, :mrf_object_age,
|
||||
threshold: 604_800,
|
||||
actions: [:delist, :strip_followers]
|
||||
|
||||
config :pleroma, :mrf_nsfw_api,
|
||||
url: "http://127.0.0.1:5000/",
|
||||
threshold: 0.7,
|
||||
mark_sensitive: true,
|
||||
unlist: false,
|
||||
reject: false
|
||||
|
||||
config :pleroma, :mrf_follow_bot, follower_nickname: nil
|
||||
|
||||
config :pleroma, :mrf_inline_quote, template: "<bdi>RT:</bdi> {url}"
|
||||
|
||||
config :pleroma, :mrf_remote_report,
|
||||
reject_all: false,
|
||||
reject_anonymous: true,
|
||||
reject_empty_message: true
|
||||
|
||||
config :pleroma, :mrf_anti_duplication,
|
||||
ttl: 60_000,
|
||||
min_length: 50
|
||||
|
||||
config :pleroma, :rich_media,
|
||||
enabled: true,
|
||||
ignore_hosts: [],
|
||||
ignore_tld: ["local", "localdomain", "lan"],
|
||||
parsers: [
|
||||
Pleroma.Web.RichMedia.Parsers.OEmbed,
|
||||
Pleroma.Web.RichMedia.Parsers.TwitterCard
|
||||
],
|
||||
oembed_providers_enabled: true,
|
||||
failure_backoff: 60_000,
|
||||
ttl_setters: [Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl]
|
||||
|
||||
config :pleroma, :media_proxy,
|
||||
enabled: false,
|
||||
invalidation: [
|
||||
enabled: false,
|
||||
provider: Pleroma.Web.MediaProxy.Invalidation.Script
|
||||
],
|
||||
proxy_opts: [
|
||||
redirect_on_failure: false,
|
||||
max_body_length: 25 * 1_048_576,
|
||||
# Note: max_read_duration defaults to Pleroma.ReverseProxy.max_read_duration_default/1
|
||||
max_read_duration: 30_000,
|
||||
http: [
|
||||
follow_redirect: true,
|
||||
pool: :media
|
||||
]
|
||||
],
|
||||
whitelist: []
|
||||
|
||||
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Http,
|
||||
method: :purge,
|
||||
headers: [],
|
||||
options: []
|
||||
|
||||
config :pleroma, Pleroma.Web.MediaProxy.Invalidation.Script,
|
||||
script_path: nil,
|
||||
url_format: nil
|
||||
|
||||
# Note: media preview proxy depends on media proxy to be enabled
|
||||
config :pleroma, :media_preview_proxy,
|
||||
enabled: false,
|
||||
thumbnail_max_width: 600,
|
||||
thumbnail_max_height: 600,
|
||||
image_quality: 85,
|
||||
min_content_length: 100 * 1024
|
||||
|
||||
config :phoenix, :format_encoders, json: Jason, "activity+json": Jason, ics: ICalendar
|
||||
|
||||
config :phoenix, :json_library, Jason
|
||||
|
||||
config :phoenix, :filter_parameters, ["password", "confirm"]
|
||||
|
||||
config :pleroma, :gopher,
|
||||
enabled: false,
|
||||
ip: {0, 0, 0, 0},
|
||||
port: 9999
|
||||
|
||||
config :pleroma, Pleroma.Web.Metadata,
|
||||
providers: [
|
||||
Pleroma.Web.Metadata.Providers.OpenGraph,
|
||||
Pleroma.Web.Metadata.Providers.TwitterCard
|
||||
],
|
||||
unfurl_nsfw: false
|
||||
|
||||
config :pleroma, Pleroma.Web.Preload,
|
||||
providers: [
|
||||
Pleroma.Web.Preload.Providers.Instance
|
||||
]
|
||||
|
||||
config :pleroma, :http_security,
|
||||
enabled: true,
|
||||
sts: false,
|
||||
sts_max_age: 31_536_000,
|
||||
ct_max_age: 2_592_000,
|
||||
referrer_policy: "same-origin"
|
||||
|
||||
config :cors_plug,
|
||||
max_age: 86_400,
|
||||
methods: ["POST", "PUT", "DELETE", "GET", "PATCH", "OPTIONS"],
|
||||
expose: [
|
||||
"Link",
|
||||
"X-RateLimit-Reset",
|
||||
"X-RateLimit-Limit",
|
||||
"X-RateLimit-Remaining",
|
||||
"X-Request-Id",
|
||||
"Idempotency-Key"
|
||||
],
|
||||
credentials: true,
|
||||
headers: ["Authorization", "Content-Type", "Idempotency-Key"]
|
||||
|
||||
config :pleroma, Pleroma.User,
|
||||
restricted_nicknames: [
|
||||
".well-known",
|
||||
"~",
|
||||
"about",
|
||||
"activities",
|
||||
"api",
|
||||
"auth",
|
||||
"check_password",
|
||||
"dev",
|
||||
"friend-requests",
|
||||
"inbox",
|
||||
"internal",
|
||||
"main",
|
||||
"media",
|
||||
"nodeinfo",
|
||||
"notice",
|
||||
"oauth",
|
||||
"objects",
|
||||
"ostatus_subscribe",
|
||||
"pleroma",
|
||||
"proxy",
|
||||
"push",
|
||||
"registration",
|
||||
"relay",
|
||||
"settings",
|
||||
"status",
|
||||
"tag",
|
||||
"user-search",
|
||||
"user_exists",
|
||||
"users",
|
||||
"web",
|
||||
"verify_credentials",
|
||||
"update_credentials",
|
||||
"relationships",
|
||||
"search",
|
||||
"confirmation_resend",
|
||||
"mfa"
|
||||
],
|
||||
email_blacklist: []
|
||||
|
||||
config :pleroma, Oban,
|
||||
repo: Pleroma.Repo,
|
||||
log: false,
|
||||
queues: [
|
||||
activity_expiration: 10,
|
||||
token_expiration: 5,
|
||||
filter_expiration: 1,
|
||||
backup: 1,
|
||||
federator_incoming: 50,
|
||||
federator_outgoing: 50,
|
||||
ingestion_queue: 50,
|
||||
web_push: 50,
|
||||
mailer: 10,
|
||||
transmogrifier: 20,
|
||||
scheduled_activities: 10,
|
||||
poll_notifications: 10,
|
||||
notifications: 20,
|
||||
background: 5,
|
||||
remote_fetcher: 2,
|
||||
attachments_cleanup: 1,
|
||||
new_users_digest: 1,
|
||||
mute_expire: 5,
|
||||
search_indexing: 10
|
||||
],
|
||||
plugins: [Oban.Plugins.Pruner],
|
||||
crontab: [
|
||||
{"0 0 * * 0", Pleroma.Workers.Cron.DigestEmailsWorker},
|
||||
{"0 0 * * *", Pleroma.Workers.Cron.NewUsersDigestWorker}
|
||||
]
|
||||
|
||||
config :pleroma, :workers,
|
||||
retries: [
|
||||
federator_incoming: 5,
|
||||
federator_outgoing: 5,
|
||||
search_indexing: 2
|
||||
]
|
||||
|
||||
config :pleroma, Pleroma.Formatter,
|
||||
class: false,
|
||||
rel: "ugc",
|
||||
new_window: false,
|
||||
truncate: false,
|
||||
strip_prefix: false,
|
||||
extra: true,
|
||||
validate_tld: :no_scheme
|
||||
|
||||
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"),
|
||||
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"
|
||||
|
||||
oauth_consumer_strategies =
|
||||
System.get_env("OAUTH_CONSUMER_STRATEGIES")
|
||||
|> to_string()
|
||||
|> String.split()
|
||||
|> Enum.map(&hd(String.split(&1, ":")))
|
||||
|
||||
ueberauth_providers =
|
||||
for strategy <- oauth_consumer_strategies do
|
||||
strategy_module_name = "Elixir.Ueberauth.Strategy.#{String.capitalize(strategy)}"
|
||||
strategy_module = String.to_atom(strategy_module_name)
|
||||
|
||||
params =
|
||||
case strategy do
|
||||
"keycloak" -> [uid_field: :email, default_scope: "openid profile"]
|
||||
_ -> [callback_params: ["state"]]
|
||||
end
|
||||
|
||||
{String.to_atom(strategy), {strategy_module, params}}
|
||||
end
|
||||
|
||||
config :ueberauth,
|
||||
Ueberauth,
|
||||
base_path: "/oauth",
|
||||
providers: ueberauth_providers
|
||||
|
||||
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
|
||||
|
||||
config :pleroma, :auth, basic_auth: false
|
||||
|
||||
config :pleroma, :auth, mongoose_im: false
|
||||
|
||||
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail, enabled: false
|
||||
|
||||
config :pleroma, Pleroma.Emails.UserEmail,
|
||||
logo: nil,
|
||||
styling: %{
|
||||
link_color: "#d8a070",
|
||||
background_color: "#2C3645",
|
||||
content_background_color: "#1B2635",
|
||||
header_color: "#d8a070",
|
||||
text_color: "#b9b9ba",
|
||||
text_muted_color: "#b9b9ba"
|
||||
}
|
||||
|
||||
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: false
|
||||
|
||||
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter,
|
||||
enabled: false,
|
||||
auth: false,
|
||||
ip_whitelist: [],
|
||||
path: "/api/pleroma/app_metrics",
|
||||
format: :text
|
||||
|
||||
config :pleroma, Pleroma.ScheduledActivity,
|
||||
daily_user_limit: 25,
|
||||
total_user_limit: 300,
|
||||
enabled: true
|
||||
|
||||
config :pleroma, :email_notifications,
|
||||
digest: %{
|
||||
active: false,
|
||||
interval: 7,
|
||||
inactivity_threshold: 7
|
||||
}
|
||||
|
||||
config :pleroma, :oauth2,
|
||||
token_expires_in: 3600 * 24 * 365 * 100,
|
||||
issue_new_refresh_token: true,
|
||||
clean_expired_tokens: false
|
||||
|
||||
config :pleroma, :database, rum_enabled: false
|
||||
|
||||
config :pleroma, :features, improved_hashtag_timeline: :auto
|
||||
|
||||
config :pleroma, :populate_hashtags_table, fault_rate_allowance: 0.01
|
||||
|
||||
config :pleroma, :delete_context_objects, fault_rate_allowance: 0.01
|
||||
|
||||
config :pleroma, :env, Mix.env()
|
||||
|
||||
config :http_signatures,
|
||||
adapter: Pleroma.Signature
|
||||
|
||||
config :pleroma, :rate_limit,
|
||||
authentication: {60_000, 15},
|
||||
timeline: {500, 3},
|
||||
search: [{1000, 10}, {1000, 30}],
|
||||
app_account_creation: {1_800_000, 25},
|
||||
relations_actions: {10_000, 10},
|
||||
relation_id_action: {60_000, 2},
|
||||
statuses_actions: {10_000, 15},
|
||||
status_id_action: {60_000, 3},
|
||||
events_actions: {10_000, 15},
|
||||
password_reset: {1_800_000, 5},
|
||||
account_confirmation_resend: {8_640_000, 5},
|
||||
ap_routes: {60_000, 15}
|
||||
|
||||
config :pleroma, Pleroma.Workers.PurgeExpiredActivity, enabled: true, min_lifetime: 600
|
||||
|
||||
config :pleroma, Pleroma.Web.Plugs.RemoteIp,
|
||||
enabled: true,
|
||||
headers: ["x-forwarded-for"],
|
||||
proxies: [],
|
||||
reserved: [
|
||||
"127.0.0.0/8",
|
||||
"::1/128",
|
||||
"fc00::/7",
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
"192.168.0.0/16"
|
||||
]
|
||||
|
||||
config :pleroma, :static_fe, enabled: false
|
||||
|
||||
# Example of frontend configuration
|
||||
# This example will make us serve the primary frontend from the
|
||||
# frontends directory within your `:pleroma, :instance, static_dir`.
|
||||
# e.g., instance/static/frontends/pleroma/develop/
|
||||
#
|
||||
# With no frontend configuration, the bundled files from the `static` directory will
|
||||
# be used.
|
||||
#
|
||||
# config :pleroma, :frontends,
|
||||
# primary: %{"name" => "pleroma-fe", "ref" => "develop"},
|
||||
# admin: %{"name" => "admin-fe", "ref" => "stable"},
|
||||
# available: %{...}
|
||||
|
||||
config :pleroma, :frontends,
|
||||
available: %{
|
||||
"kenoma" => %{
|
||||
"name" => "kenoma",
|
||||
"git" => "https://git.pleroma.social/lambadalambda/kenoma",
|
||||
"build_url" =>
|
||||
"https://git.pleroma.social/lambadalambda/kenoma/-/jobs/artifacts/${ref}/download?job=build",
|
||||
"ref" => "master"
|
||||
},
|
||||
"pleroma-fe" => %{
|
||||
"name" => "pleroma-fe",
|
||||
"git" => "https://git.pleroma.social/pleroma/pleroma-fe",
|
||||
"build_url" =>
|
||||
"https://git.pleroma.social/pleroma/pleroma-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||
"ref" => "develop"
|
||||
},
|
||||
"fedi-fe" => %{
|
||||
"name" => "fedi-fe",
|
||||
"git" => "https://git.pleroma.social/pleroma/fedi-fe",
|
||||
"build_url" =>
|
||||
"https://git.pleroma.social/pleroma/fedi-fe/-/jobs/artifacts/${ref}/download?job=build_release",
|
||||
"ref" => "master",
|
||||
"custom-http-headers" => [
|
||||
{"service-worker-allowed", "/"}
|
||||
]
|
||||
},
|
||||
"admin-fe" => %{
|
||||
"name" => "admin-fe",
|
||||
"git" => "https://git.pleroma.social/pleroma/admin-fe",
|
||||
"build_url" =>
|
||||
"https://git.pleroma.social/pleroma/admin-fe/-/jobs/artifacts/${ref}/download?job=build",
|
||||
"ref" => "develop"
|
||||
},
|
||||
"soapbox" => %{
|
||||
"name" => "soapbox",
|
||||
"git" => "https://gitlab.com/soapbox-pub/soapbox",
|
||||
"build_url" =>
|
||||
"https://gitlab.com/soapbox-pub/soapbox/-/jobs/artifacts/${ref}/download?job=build",
|
||||
"ref" => "main"
|
||||
},
|
||||
"glitch-lily" => %{
|
||||
"name" => "glitch-lily",
|
||||
"git" => "https://lily-is.land/infra/glitch-lily",
|
||||
"build_url" =>
|
||||
"https://lily-is.land/infra/glitch-lily/-/jobs/artifacts/${ref}/download?job=build",
|
||||
"ref" => "servant",
|
||||
"build_dir" => "public"
|
||||
}
|
||||
}
|
||||
|
||||
config :pleroma, :web_cache_ttl,
|
||||
activity_pub: nil,
|
||||
activity_pub_question: 30_000
|
||||
|
||||
config :pleroma, :modules, runtime_dir: "instance/modules"
|
||||
|
||||
config :pleroma, configurable_from_database: false
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
parameters: [gin_fuzzy_search_limit: "500"],
|
||||
prepare: :unnamed
|
||||
|
||||
config :pleroma, :connections_pool,
|
||||
reclaim_multiplier: 0.1,
|
||||
connection_acquisition_wait: 250,
|
||||
connection_acquisition_retries: 5,
|
||||
max_connections: 250,
|
||||
max_idle_time: 30_000,
|
||||
retry: 0,
|
||||
connect_timeout: 5_000
|
||||
|
||||
config :pleroma, :pools,
|
||||
federation: [
|
||||
size: 50,
|
||||
max_waiting: 10,
|
||||
recv_timeout: 10_000
|
||||
],
|
||||
media: [
|
||||
size: 50,
|
||||
max_waiting: 20,
|
||||
recv_timeout: 15_000
|
||||
],
|
||||
upload: [
|
||||
size: 25,
|
||||
max_waiting: 5,
|
||||
recv_timeout: 15_000
|
||||
],
|
||||
default: [
|
||||
size: 10,
|
||||
max_waiting: 2,
|
||||
recv_timeout: 5_000
|
||||
]
|
||||
|
||||
config :pleroma, :hackney_pools,
|
||||
federation: [
|
||||
max_connections: 50,
|
||||
timeout: 150_000
|
||||
],
|
||||
media: [
|
||||
max_connections: 50,
|
||||
timeout: 150_000
|
||||
],
|
||||
upload: [
|
||||
max_connections: 25,
|
||||
timeout: 300_000
|
||||
]
|
||||
|
||||
config :pleroma, :majic_pool, size: 2
|
||||
|
||||
private_instance? = :if_instance_is_private
|
||||
|
||||
config :pleroma, :restrict_unauthenticated,
|
||||
timelines: %{local: private_instance?, federated: private_instance?},
|
||||
profiles: %{local: private_instance?, remote: private_instance?},
|
||||
activities: %{local: private_instance?, remote: private_instance?}
|
||||
|
||||
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: false
|
||||
|
||||
config :pleroma, :mrf,
|
||||
policies: [
|
||||
Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.TagPolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy
|
||||
],
|
||||
transparency: true,
|
||||
transparency_exclusions: []
|
||||
|
||||
config :tzdata, :http_client, Pleroma.HTTP.Tzdata
|
||||
|
||||
config :ex_aws, http_client: Pleroma.HTTP.ExAws
|
||||
|
||||
config :web_push_encryption, http_client: Pleroma.HTTP.WebPush
|
||||
|
||||
config :pleroma, :instances_favicons, enabled: false
|
||||
|
||||
config :floki, :html_parser, Floki.HTMLParser.FastHtml
|
||||
|
||||
config :pleroma, Pleroma.Web.Auth.Authenticator, Pleroma.Web.Auth.PleromaAuthenticator
|
||||
|
||||
config :pleroma, Pleroma.User.Backup,
|
||||
purge_after_days: 30,
|
||||
limit_days: 7,
|
||||
dir: nil,
|
||||
process_wait_time: 30_000,
|
||||
process_chunk_size: 100
|
||||
|
||||
config :pleroma, ConcurrentLimiter, [
|
||||
{Pleroma.Web.RichMedia.Helpers, [max_running: 5, max_waiting: 5]},
|
||||
{Pleroma.Web.ActivityPub.MRF.MediaProxyWarmingPolicy, [max_running: 5, max_waiting: 5]},
|
||||
{Pleroma.Search, [max_running: 30, max_waiting: 50]},
|
||||
{Pleroma.Webhook.Notify, [max_running: 5, max_waiting: 200]}
|
||||
]
|
||||
|
||||
config :pleroma, Pleroma.Web.WebFinger, domain: nil, update_nickname_on_user_fetch: false
|
||||
|
||||
config :pleroma, Pleroma.Language.Translation, allow_unauthenticated: false, allow_remote: true
|
||||
|
||||
config :geospatial, Geospatial.Service, service: Geospatial.Providers.Nominatim
|
||||
|
||||
config :geospatial, Geospatial.Providers.GoogleMaps,
|
||||
api_key: nil,
|
||||
fetch_place_details: true
|
||||
|
||||
config :geospatial, Geospatial.Providers.Nominatim,
|
||||
endpoint: "https://nominatim.openstreetmap.org",
|
||||
api_key: nil
|
||||
|
||||
config :geospatial, Geospatial.Providers.Pelias,
|
||||
endpoint: "https://api.geocode.earth",
|
||||
api_key: nil
|
||||
|
||||
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,
|
||||
url: "http://127.0.0.1:7700/",
|
||||
private_key: nil,
|
||||
initial_indexing_chunk_size: 100_000
|
||||
|
||||
# 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"
|
3744
config/description.exs
Normal file
3744
config/description.exs
Normal file
File diff suppressed because it is too large
Load Diff
78
config/dev.exs
Normal file
78
config/dev.exs
Normal file
@ -0,0 +1,78 @@
|
||||
import Config
|
||||
|
||||
# For development, we disable any cache and enable
|
||||
# debugging and code reloading.
|
||||
#
|
||||
# The watchers configuration can be used to run external
|
||||
# watchers to your application. For example, we use it
|
||||
# with brunch.io to recompile .js and .css sources.
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [
|
||||
port: 4000,
|
||||
protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192]
|
||||
],
|
||||
protocol: "http",
|
||||
debug_errors: true,
|
||||
code_reloader: true,
|
||||
check_origin: false,
|
||||
watchers: [],
|
||||
secure_cookie_flag: false
|
||||
|
||||
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
# In order to use HTTPS in development, a self-signed
|
||||
# certificate can be generated by running the following
|
||||
# command from your terminal:
|
||||
#
|
||||
# openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -keyout priv/server.key -out priv/server.pem
|
||||
#
|
||||
# The `http:` config above can be replaced with:
|
||||
#
|
||||
# https: [port: 4000, keyfile: "priv/server.key", certfile: "priv/server.pem"],
|
||||
#
|
||||
# If desired, both `http:` and `https:` keys can be
|
||||
# configured to run both http and https servers on
|
||||
# different ports.
|
||||
|
||||
# Do not include metadata nor timestamps in development logs
|
||||
config :logger, :console, format: "[$level] $message\n"
|
||||
|
||||
# Set a higher stacktrace during development. Avoid configuring such
|
||||
# in production as building large stacktraces may be expensive.
|
||||
config :phoenix, :stacktrace_depth, 20
|
||||
|
||||
# Configure your database
|
||||
config :pleroma, Pleroma.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "pleroma_dev",
|
||||
hostname: "localhost",
|
||||
pool_size: 10
|
||||
|
||||
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
|
||||
|
||||
# Reduce recompilation time
|
||||
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
config :pleroma, Pleroma.PromEx,
|
||||
disabled: false,
|
||||
manual_metrics_start_delay: :no_delay,
|
||||
drop_metrics_groups: [],
|
||||
grafana: :disabled,
|
||||
metrics_server: :disabled
|
||||
|
||||
if File.exists?("./config/dev.secret.exs") do
|
||||
import_config "dev.secret.exs"
|
||||
else
|
||||
IO.puts(
|
||||
:stderr,
|
||||
"!!! RUNNING IN LOCALHOST DEV MODE! !!!\nFEDERATION WON'T WORK UNTIL YOU CONFIGURE A dev.secret.exs"
|
||||
)
|
||||
end
|
||||
|
||||
if File.exists?("./config/dev.exported_from_db.secret.exs"),
|
||||
do: import_config("dev.exported_from_db.secret.exs")
|
80
config/docker.exs
Normal file
80
config/docker.exs
Normal file
@ -0,0 +1,80 @@
|
||||
import Config
|
||||
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
url: [host: System.get_env("DOMAIN", "localhost"), scheme: "https", port: 443],
|
||||
http: [ip: {0, 0, 0, 0}, port: System.get_env("PORT", "5000")]
|
||||
|
||||
config :pleroma, :instance,
|
||||
name: System.get_env("INSTANCE_NAME", "Soapbox"),
|
||||
email: System.get_env("ADMIN_EMAIL"),
|
||||
notify_email: System.get_env("NOTIFY_EMAIL"),
|
||||
limit: 5000,
|
||||
registrations_open: false,
|
||||
healthcheck: true
|
||||
|
||||
# Prefer `DATABASE_URL` if set, otherwise use granular env.
|
||||
case System.get_env("DATABASE_URL") do
|
||||
database_url when is_binary(database_url) ->
|
||||
config :pleroma, Pleroma.Repo, url: database_url
|
||||
|
||||
_ ->
|
||||
config :pleroma, Pleroma.Repo,
|
||||
username: System.get_env("DB_USER", "postgres"),
|
||||
password: System.get_env("DB_PASS", "postgres"),
|
||||
database: System.get_env("DB_NAME", "postgres"),
|
||||
hostname: System.get_env("DB_HOST", "db"),
|
||||
port: System.get_env("DB_PORT", "5432")
|
||||
end
|
||||
|
||||
# Configure web push notifications
|
||||
config :web_push_encryption, :vapid_details, subject: "mailto:#{System.get_env("NOTIFY_EMAIL")}"
|
||||
|
||||
config :pleroma, :database, rum_enabled: false
|
||||
config :pleroma, :instance, static_dir: "/var/lib/pleroma/static"
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "/var/lib/pleroma/uploads"
|
||||
|
||||
config :pleroma, Pleroma.Language.LanguageDetector,
|
||||
provider: Pleroma.Language.LanguageDetector.Fasttext
|
||||
|
||||
config :pleroma, Pleroma.Language.LanguageDetector.Fasttext,
|
||||
model: "/usr/share/fasttext/lid.176.ftz"
|
||||
|
||||
# We can't store the secrets in this file, since this is baked into the docker image
|
||||
if not File.exists?("/var/lib/pleroma/secret.exs") do
|
||||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
||||
signing_salt = :crypto.strong_rand_bytes(8) |> Base.encode64() |> binary_part(0, 8)
|
||||
{web_push_public_key, web_push_private_key} = :crypto.generate_key(:ecdh, :prime256v1)
|
||||
|
||||
secret_file =
|
||||
EEx.eval_string(
|
||||
"""
|
||||
import Config
|
||||
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
secret_key_base: "<%= secret %>",
|
||||
signing_salt: "<%= signing_salt %>"
|
||||
|
||||
config :web_push_encryption, :vapid_details,
|
||||
public_key: "<%= web_push_public_key %>",
|
||||
private_key: "<%= web_push_private_key %>"
|
||||
""",
|
||||
secret: secret,
|
||||
signing_salt: signing_salt,
|
||||
web_push_public_key: Base.url_encode64(web_push_public_key, padding: false),
|
||||
web_push_private_key: Base.url_encode64(web_push_private_key, padding: false)
|
||||
)
|
||||
|
||||
File.write("/var/lib/pleroma/secret.exs", secret_file)
|
||||
end
|
||||
|
||||
import_config("/var/lib/pleroma/secret.exs")
|
||||
|
||||
# For additional user config
|
||||
if File.exists?("/var/lib/pleroma/config.exs"),
|
||||
do: import_config("/var/lib/pleroma/config.exs"),
|
||||
else:
|
||||
File.write("/var/lib/pleroma/config.exs", """
|
||||
import Config
|
||||
|
||||
# For additional configuration outside of environmental variables
|
||||
""")
|
25
config/dokku.exs
Normal file
25
config/dokku.exs
Normal file
@ -0,0 +1,25 @@
|
||||
import Config
|
||||
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [
|
||||
port: String.to_integer(System.get_env("PORT") || "4000"),
|
||||
protocol_options: [max_request_line_length: 8192, max_header_value_length: 8192]
|
||||
],
|
||||
protocol: "http",
|
||||
secure_cookie_flag: false,
|
||||
url: [host: System.get_env("APP_HOST"), scheme: "https", port: 443],
|
||||
secret_key_base: "+S+ULgf7+N37c/lc9K66SMphnjQIRGklTu0BRr2vLm2ZzvK0Z6OH/PE77wlUNtvP"
|
||||
|
||||
database_url =
|
||||
System.get_env("DATABASE_URL") ||
|
||||
raise """
|
||||
environment variable DATABASE_URL is missing.
|
||||
For example: ecto://USER:PASS@HOST/DATABASE
|
||||
"""
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
# ssl: true,
|
||||
url: database_url,
|
||||
pool_size: String.to_integer(System.get_env("POOL_SIZE") || "10")
|
||||
|
||||
config :pleroma, :instance, name: "#{System.get_env("APP_NAME")} CI Instance"
|
3
config/emoji.txt
Normal file
3
config/emoji.txt
Normal file
@ -0,0 +1,3 @@
|
||||
firefox, /emoji/Firefox.gif, Gif,Fun
|
||||
blank, /emoji/blank.png, Fun
|
||||
dinosaur, /emoji/dino walking.gif, Gif
|
97
config/prod.exs
Normal file
97
config/prod.exs
Normal file
@ -0,0 +1,97 @@
|
||||
import Config
|
||||
|
||||
# For production, we often load configuration from external
|
||||
# sources, such as your system environment. For this reason,
|
||||
# you won't find the :http configuration below, but set inside
|
||||
# Pleroma.Web.Endpoint.load_from_system_env/1 dynamically.
|
||||
# Any dynamic configuration should be moved to such function.
|
||||
#
|
||||
# Don't forget to configure the url host to something meaningful,
|
||||
# Phoenix uses this information when generating URLs.
|
||||
#
|
||||
# Finally, we also include the path to a cache manifest
|
||||
# containing the digested version of static files. This
|
||||
# manifest is generated by the mix phoenix.digest task
|
||||
# which you typically run after static files are built.
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [port: 4000],
|
||||
protocol: "http"
|
||||
|
||||
config :phoenix, serve_endpoints: true
|
||||
|
||||
# Do not print debug messages in production
|
||||
config :logger, :console, level: :info
|
||||
config :logger, :ex_syslogger, level: :info
|
||||
|
||||
# PromEx set up
|
||||
config :pleroma, Pleroma.Web.Plugs.MetricsPredicate,
|
||||
auth_token: System.get_env("PROMETHEUS_AUTH_TOKEN", "supersecret")
|
||||
|
||||
config :pleroma, Pleroma.PromEx,
|
||||
prometheus_data_source_id:
|
||||
System.get_env(
|
||||
"PROMETHEUS_DATASOURCE_ID",
|
||||
"Prometheus"
|
||||
),
|
||||
grafana: [
|
||||
host: System.get_env("GRAFANA_HOST", "http://localhost:3000"),
|
||||
auth_token: System.get_env("GRAFANA_AUTH_TOKEN", "LOLNO"),
|
||||
upload_dashboards_on_start: true,
|
||||
folder_name: "Pleroma - PromEx",
|
||||
annotate_app_lifecycle: true
|
||||
]
|
||||
|
||||
# ## SSL Support
|
||||
#
|
||||
# To get SSL working, you will need to add the `https` key
|
||||
# to the previous section and set your `:url` port to 443:
|
||||
#
|
||||
# config :pleroma, Pleroma.Web.Endpoint,
|
||||
# ...
|
||||
# url: [host: "example.com", port: 443],
|
||||
# https: [:inet6,
|
||||
# port: 443,
|
||||
# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
|
||||
# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
|
||||
#
|
||||
# Where those two env variables return an absolute path to
|
||||
# the key and cert in disk or a relative path inside priv,
|
||||
# for example "priv/ssl/server.key".
|
||||
#
|
||||
# We also recommend setting `force_ssl`, ensuring no data is
|
||||
# ever sent via http, always redirecting to https:
|
||||
#
|
||||
# config :pleroma, Pleroma.Web.Endpoint,
|
||||
# force_ssl: [hsts: true]
|
||||
#
|
||||
# Check `Plug.SSL` for all available options in `force_ssl`.
|
||||
|
||||
# ## Using releases
|
||||
#
|
||||
# If you are doing OTP releases, you need to instruct Phoenix
|
||||
# to start the server for all endpoints:
|
||||
#
|
||||
# config :phoenix, :serve_endpoints, true
|
||||
#
|
||||
# Alternatively, you can configure exactly which server to
|
||||
# start per endpoint:
|
||||
#
|
||||
# config :pleroma, Pleroma.Web.Endpoint, server: true
|
||||
#
|
||||
|
||||
# Finally import the config/prod.secret.exs
|
||||
# which should be versioned separately.
|
||||
cond do
|
||||
File.exists?("./config/prod.secret.exs") ->
|
||||
import_config "prod.secret.exs"
|
||||
|
||||
System.get_env("CI") == "true" ->
|
||||
nil
|
||||
|
||||
true ->
|
||||
"`config/prod.secret.exs` not found. You may want to create one by running `mix pleroma.instance gen`"
|
||||
|> IO.warn([])
|
||||
end
|
||||
|
||||
if File.exists?("./config/prod.exported_from_db.secret.exs"),
|
||||
do: import_config("prod.exported_from_db.secret.exs")
|
58
config/soapbox.exs
Normal file
58
config/soapbox.exs
Normal file
@ -0,0 +1,58 @@
|
||||
# Soapbox 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: [
|
||||
Pleroma.Upload.Filter.AnalyzeMetadata,
|
||||
Pleroma.Upload.Filter.Dedupe,
|
||||
Pleroma.Upload.Filter.Exiftool.StripLocation
|
||||
]
|
||||
|
||||
# Default MRF policies
|
||||
config :pleroma, :mrf,
|
||||
policies: [
|
||||
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.HellthreadPolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.ObjectAgePolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.TagPolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.InlineQuotePolicy
|
||||
]
|
||||
|
||||
# Increase the pool size and timeout
|
||||
config :pleroma, :dangerzone, override_repo_pool_size: true
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
pool_size: 40,
|
||||
timeout: 30_000
|
||||
|
||||
# Allow privileged staff
|
||||
config :pleroma, :instance, privileged_staff: true
|
||||
|
||||
# Enable instance favicons
|
||||
config :pleroma, :instances_favicons, enabled: true
|
||||
|
||||
# Hellthread limits
|
||||
config :pleroma, :mrf_hellthread,
|
||||
delist_threshold: 15,
|
||||
reject_threshold: 100
|
||||
|
||||
# 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"
|
||||
|
||||
# Background migration performance
|
||||
config :pleroma, :delete_context_objects, sleep_interval_ms: 3_000
|
||||
|
||||
# Pretend to be WhatsApp because some sites don't return link previews otherwise
|
||||
config :pleroma, :rich_media, user_agent: "WhatsApp/2"
|
164
config/test.exs
Normal file
164
config/test.exs
Normal file
@ -0,0 +1,164 @@
|
||||
import Config
|
||||
|
||||
# We don't run a server during test. If one is required,
|
||||
# you can enable the server option below.
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
http: [port: 4001],
|
||||
url: [port: 4001],
|
||||
server: true
|
||||
|
||||
# Disable captha for tests
|
||||
config :pleroma, Pleroma.Captcha,
|
||||
# It should not be enabled for automatic tests
|
||||
enabled: false,
|
||||
# A fake captcha service for tests
|
||||
method: Pleroma.Captcha.Mock
|
||||
|
||||
# Print only warnings and errors during test
|
||||
config :logger, :console,
|
||||
level: :warning,
|
||||
format: "\n[$level] $message\n"
|
||||
|
||||
config :pleroma, :auth, oauth_consumer_strategies: []
|
||||
|
||||
config :pleroma, Pleroma.Upload,
|
||||
filters: [],
|
||||
link_name: false,
|
||||
default_description: :filename
|
||||
|
||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
||||
|
||||
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test, enabled: true
|
||||
|
||||
config :pleroma, :instance,
|
||||
name: "Pleroma",
|
||||
description: "Pleroma: An efficient and flexible fediverse server",
|
||||
instance_thumbnail: "/instance/thumbnail.jpeg",
|
||||
email: "admin@example.com",
|
||||
notify_email: "noreply@example.com",
|
||||
skip_thread_containment: false,
|
||||
federating: false,
|
||||
account_approval_required: false,
|
||||
external_user_synchronization: false,
|
||||
static_dir: "test/instance_static/"
|
||||
|
||||
config :pleroma, :activitypub, sign_object_fetches: false, follow_handshake_timeout: 0
|
||||
|
||||
# Configure your database
|
||||
config :pleroma, Pleroma.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "postgres",
|
||||
password: "postgres",
|
||||
database: "pleroma_test",
|
||||
hostname: System.get_env("DB_HOST") || "localhost",
|
||||
port: System.get_env("DB_PORT") || "5432",
|
||||
pool: Ecto.Adapters.SQL.Sandbox,
|
||||
pool_size: 50
|
||||
|
||||
config :pleroma, :dangerzone, override_repo_pool_size: true
|
||||
|
||||
# Reduce hash rounds for testing
|
||||
config :pleroma, :password, iterations: 1
|
||||
|
||||
config :tesla, adapter: Tesla.Mock
|
||||
|
||||
config :tesla, Geospatial.HTTP, adapter: Tesla.Mock
|
||||
|
||||
config :pleroma, :rich_media,
|
||||
enabled: false,
|
||||
ignore_hosts: [],
|
||||
ignore_tld: ["local", "localdomain", "lan"]
|
||||
|
||||
config :pleroma, :instance,
|
||||
multi_factor_authentication: [
|
||||
totp: [
|
||||
# digits 6 or 8
|
||||
digits: 6,
|
||||
period: 30
|
||||
],
|
||||
backup_codes: [
|
||||
number: 2,
|
||||
length: 6
|
||||
]
|
||||
]
|
||||
|
||||
config :web_push_encryption, :vapid_details,
|
||||
subject: "mailto:administrator@example.com",
|
||||
public_key:
|
||||
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
||||
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
||||
|
||||
config :pleroma, Oban, testing: :manual
|
||||
|
||||
config :pleroma, Pleroma.ScheduledActivity,
|
||||
daily_user_limit: 2,
|
||||
total_user_limit: 3,
|
||||
enabled: false
|
||||
|
||||
config :pleroma, :rate_limit, %{}
|
||||
|
||||
config :pleroma, :http_security, report_uri: "https://endpoint.com"
|
||||
|
||||
config :pleroma, :http, send_user_agent: false
|
||||
|
||||
rum_enabled = System.get_env("RUM_ENABLED") == "true"
|
||||
config :pleroma, :database, rum_enabled: rum_enabled
|
||||
IO.puts("RUM enabled: #{rum_enabled}")
|
||||
|
||||
config :joken, default_signer: "yU8uHKq+yyAkZ11Hx//jcdacWc8yQ1bxAAGrplzB0Zwwjkp35v0RK9SO8WTPr6QZ"
|
||||
|
||||
config :pleroma, Pleroma.ReverseProxy.Client, Pleroma.ReverseProxy.ClientMock
|
||||
|
||||
config :pleroma, :modules, runtime_dir: "test/fixtures/modules"
|
||||
|
||||
config :pleroma, Pleroma.Gun, Pleroma.GunMock
|
||||
|
||||
config :pleroma, Pleroma.Emails.NewUsersDigestEmail, enabled: true
|
||||
|
||||
config :pleroma, Pleroma.Web.Plugs.RemoteIp, enabled: false
|
||||
|
||||
config :pleroma, Pleroma.Web.ApiSpec.CastAndValidate, strict: true
|
||||
|
||||
config :tzdata, :autoupdate, :disabled
|
||||
|
||||
config :pleroma, :mrf, policies: []
|
||||
|
||||
config :pleroma, :instances_favicons, enabled: false
|
||||
|
||||
config :pleroma, :pipeline,
|
||||
object_validator: Pleroma.Web.ActivityPub.ObjectValidatorMock,
|
||||
mrf: Pleroma.Web.ActivityPub.MRFMock,
|
||||
activity_pub: Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||
side_effects: Pleroma.Web.ActivityPub.SideEffectsMock,
|
||||
federator: Pleroma.Web.FederatorMock,
|
||||
config: Pleroma.ConfigMock
|
||||
|
||||
config :pleroma, :cachex, provider: Pleroma.CachexMock
|
||||
|
||||
config :pleroma, Pleroma.Web.WebFinger, update_nickname_on_user_fetch: false
|
||||
|
||||
config :pleroma, :side_effects,
|
||||
ap_streamer: Pleroma.Web.ActivityPub.ActivityPubMock,
|
||||
logger: Pleroma.LoggerMock
|
||||
|
||||
config :pleroma, Pleroma.Search, module: Pleroma.Search.DatabaseSearch
|
||||
|
||||
config :pleroma, Pleroma.Search.Meilisearch, url: "http://127.0.0.1:7700/", private_key: nil
|
||||
|
||||
# Reduce recompilation time
|
||||
# https://dashbit.co/blog/speeding-up-re-compilation-of-elixir-projects
|
||||
config :phoenix, :plug_init_mode, :runtime
|
||||
|
||||
# Allow inline images in tests (for now).
|
||||
# FIXME: rework/remove tests that depend on this.
|
||||
config :pleroma, :markup, allow_inline_images: true
|
||||
|
||||
config :pleroma, :config_impl, Pleroma.UnstubbedConfigMock
|
||||
|
||||
if File.exists?("./config/test.secret.exs") do
|
||||
import_config "test.secret.exs"
|
||||
else
|
||||
IO.puts(
|
||||
"You may want to create test.secret.exs to declare custom database connection parameters."
|
||||
)
|
||||
end
|
7
coveralls.json
Normal file
7
coveralls.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"skip_files": [
|
||||
"test/support",
|
||||
"lib/mix/tasks/pleroma/benchmark.ex",
|
||||
"lib/credo/check/consistency/file_location.ex"
|
||||
]
|
||||
}
|
16
docker-entrypoint.sh
Executable file
16
docker-entrypoint.sh
Executable file
@ -0,0 +1,16 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
DATABASE_URL=${DATABASE_URL:-"postgres://${DB_USER:-postgres}:${DB_PASS:-postgres}@${DB_HOST:-db}:5432/${DB_NAME:-postgres}"}
|
||||
|
||||
echo "-- Waiting for database..."
|
||||
while ! pg_isready -d $DATABASE_URL -t 1; do
|
||||
sleep 1s
|
||||
done
|
||||
|
||||
echo "-- Running migrations..."
|
||||
$HOME/bin/pleroma_ctl migrate
|
||||
|
||||
echo "-- Starting!"
|
||||
exec $HOME/bin/pleroma start
|
4
docs/README.md
Normal file
4
docs/README.md
Normal file
@ -0,0 +1,4 @@
|
||||
# Rebased
|
||||
|
||||
**Rebased** is a Fediverse backend written in Elixir.
|
||||
It's compatible with the Mastodon API and is the recommended backend for Soapbox.
|
16
docs/SUMMARY.md
Normal file
16
docs/SUMMARY.md
Normal file
@ -0,0 +1,16 @@
|
||||
# Summary
|
||||
|
||||
* [Introduction](README.md)
|
||||
|
||||
* Administration
|
||||
* [Backing up](administration/backup.md)
|
||||
* [Updating](administration/updating.md)
|
||||
|
||||
## Configuration
|
||||
|
||||
* [Cheatsheet](configuration/cheatsheet.md)
|
||||
* [Hardening](configuration/hardening.md)
|
||||
|
||||
* Guides
|
||||
* [Setting up the media proxy](configuration/howto_proxy.md)
|
||||
* [Saving config in the database](configuration/howto_database_config.md)
|
157
docs/administration/CLI_tasks/config.md
Normal file
157
docs/administration/CLI_tasks/config.md
Normal file
@ -0,0 +1,157 @@
|
||||
# Transfering the config to/from the database
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Transfer config from file to DB.
|
||||
|
||||
!!! note
|
||||
You need to add the following to your config before executing this command:
|
||||
|
||||
```elixir
|
||||
config :pleroma, configurable_from_database: true
|
||||
```
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config migrate_to_db
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config migrate_to_db
|
||||
```
|
||||
|
||||
## Transfer config from DB to `config/env.exported_from_db.secret.exs`
|
||||
|
||||
!!! note
|
||||
In-Database configuration will still be applied after executing this command unless you set the following in your config:
|
||||
|
||||
```elixir
|
||||
config :pleroma, configurable_from_database: false
|
||||
```
|
||||
|
||||
Options:
|
||||
|
||||
- `<path>` - where to save migrated config. E.g. `--path=/tmp`. If file saved into non standart folder, you must manually copy file into directory where Pleroma can read it. For OTP install path will be `PLEROMA_CONFIG_PATH` or `/etc/pleroma`. For installation from source - `config` directory in the pleroma folder.
|
||||
- `<env>` - environment, for which is migrated config. By default is `prod`.
|
||||
- To delete transferred settings from database optional flag `-d` can be used
|
||||
|
||||
=== "OTP"
|
||||
```sh
|
||||
./bin/pleroma_ctl config migrate_from_db [--env=<env>] [-d] [--path=<path>]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
```sh
|
||||
mix pleroma.config migrate_from_db [--env=<env>] [-d] [--path=<path>]
|
||||
```
|
||||
|
||||
## Dump all of the config settings defined in the database
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config dump
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config dump
|
||||
```
|
||||
|
||||
## List individual configuration groups in the database
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config groups
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config groups
|
||||
```
|
||||
|
||||
## Dump the saved configuration values for a specific group or key
|
||||
|
||||
e.g., this shows all the settings under `config :pleroma`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config dump pleroma
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config dump pleroma
|
||||
```
|
||||
|
||||
To get values under a specific key:
|
||||
|
||||
e.g., this shows all the settings under `config :pleroma, :instance`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config dump pleroma instance
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config dump pleroma instance
|
||||
```
|
||||
|
||||
## Delete the saved configuration values for a specific group or key
|
||||
|
||||
e.g., this deletes all the settings under `config :tesla`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config delete [--force] tesla
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config delete [--force] tesla
|
||||
```
|
||||
|
||||
To delete values under a specific key:
|
||||
|
||||
e.g., this deletes all the settings under `config :phoenix, :stacktrace_depth`
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config delete [--force] phoenix stacktrace_depth
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config delete [--force] phoenix stacktrace_depth
|
||||
```
|
||||
|
||||
## Remove all settings from the database
|
||||
|
||||
This forcibly removes all saved values in the database.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl config [--force] reset
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.config [--force] reset
|
||||
```
|
161
docs/administration/CLI_tasks/database.md
Normal file
161
docs/administration/CLI_tasks/database.md
Normal file
@ -0,0 +1,161 @@
|
||||
# Database maintenance tasks
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
!!! danger
|
||||
These mix tasks can take a long time to complete. Many of them were written to address specific database issues that happened because of bugs in migrations or other specific scenarios. Do not run these tasks "just in case" if everything is fine your instance.
|
||||
|
||||
## Replace embedded objects with their references
|
||||
|
||||
Replaces embedded objects with references to them in the `objects` table. Only needs to be ran once if the instance was created before Pleroma 1.0.5. The reason why this is not a migration is because it could significantly increase the database size after being ran, however after this `VACUUM FULL` will be able to reclaim about 20% (really depends on what is in the database, your mileage may vary) of the db size before the migration.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database remove_embedded_objects [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database remove_embedded_objects [option ...]
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
- `--vacuum` - run `VACUUM FULL` after the embedded objects are replaced with their references
|
||||
|
||||
## Prune old remote posts from the database
|
||||
|
||||
This will prune remote posts older than 90 days (configurable with [`config :pleroma, :instance, remote_post_retention_days`](../../configuration/cheatsheet.md#instance)) from the database, they will be refetched from source when accessed.
|
||||
|
||||
!!! danger
|
||||
The disk space will only be reclaimed after `VACUUM FULL`. You may run out of disk space during the execution of the task or vacuuming if you don't have about 1/3rds of the database size free.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database prune_objects [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database prune_objects [option ...]
|
||||
```
|
||||
|
||||
### Options
|
||||
- `--vacuum` - run `VACUUM FULL` after the objects are pruned
|
||||
|
||||
## Create a conversation for all existing DMs
|
||||
|
||||
Can be safely re-run
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database bump_all_conversations
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database bump_all_conversations
|
||||
```
|
||||
|
||||
## Remove duplicated items from following and update followers count for all users
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database update_users_following_followers_counts
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database update_users_following_followers_counts
|
||||
```
|
||||
|
||||
## Fix the pre-existing "likes" collections for all objects
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database fix_likes_collections
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database fix_likes_collections
|
||||
```
|
||||
|
||||
## Vacuum the database
|
||||
|
||||
### Analyze
|
||||
|
||||
Running an `analyze` vacuum job can improve performance by updating statistics used by the query planner. **It is safe to cancel this.**
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database vacuum analyze
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database vacuum analyze
|
||||
```
|
||||
|
||||
### Full
|
||||
|
||||
Running a `full` vacuum job rebuilds your entire database by reading all of the data and rewriting it into smaller
|
||||
and more compact files with an optimized layout. This process will take a long time and use additional disk space as
|
||||
it builds the files side-by-side the existing database files. It can make your database faster and use less disk space,
|
||||
but should only be run if necessary. **It is safe to cancel this.**
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database vacuum full
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database vacuum full
|
||||
```
|
||||
|
||||
## Add expiration to all local statuses
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database ensure_expiration
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database ensure_expiration
|
||||
```
|
||||
|
||||
## Change Text Search Configuration
|
||||
|
||||
Change `default_text_search_config` for database and (if necessary) text_search_config used in index, then rebuild index (it may take time).
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database set_text_search_config english
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database set_text_search_config english
|
||||
```
|
||||
|
||||
See [PostgreSQL documentation](https://www.postgresql.org/docs/current/textsearch-configuration.html) and `docs/configuration/howto_search_cjk.md` for more detail.
|
33
docs/administration/CLI_tasks/digest.md
Normal file
33
docs/administration/CLI_tasks/digest.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Managing digest emails
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Send digest email since given date (user registration date by default) ignoring user activity status.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl digest test <nickname> [since_date]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.digest test <nickname> [since_date]
|
||||
```
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl digest test donaldtheduck 2019-05-20
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.digest test donaldtheduck 2019-05-20
|
||||
```
|
||||
|
45
docs/administration/CLI_tasks/email.md
Normal file
45
docs/administration/CLI_tasks/email.md
Normal file
@ -0,0 +1,45 @@
|
||||
# EMail administration tasks
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Send test email (instance email by default)
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl email test [--to <destination email address>]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.email test [--to <destination email address>]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl email test --to root@example.org
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.email test --to root@example.org
|
||||
```
|
||||
|
||||
## Send confirmation emails to all unconfirmed user accounts
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl email resend_confirmation_emails
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.email resend_confirmation_emails
|
||||
```
|
61
docs/administration/CLI_tasks/emoji.md
Normal file
61
docs/administration/CLI_tasks/emoji.md
Normal file
@ -0,0 +1,61 @@
|
||||
# Managing emoji packs
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Lists emoji packs and metadata specified in the manifest
|
||||
|
||||
=== "OTP"
|
||||
```sh
|
||||
./bin/pleroma_ctl emoji ls-packs [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
```sh
|
||||
mix pleroma.emoji ls-packs [option ...]
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
- `-m, --manifest PATH/URL` - path to a custom manifest, it can either be an URL starting with `http`, in that case the manifest will be fetched from that address, or a local path
|
||||
|
||||
## Fetch, verify and install the specified packs from the manifest into `STATIC-DIR/emoji/PACK-NAME`
|
||||
|
||||
=== "OTP"
|
||||
```sh
|
||||
./bin/pleroma_ctl emoji get-packs [option ...] <pack ...>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
```sh
|
||||
mix pleroma.emoji get-packs [option ...] <pack ...>
|
||||
```
|
||||
|
||||
### Options
|
||||
- `-m, --manifest PATH/URL` - same as [`ls-packs`](#ls-packs)
|
||||
|
||||
## Create a new manifest entry and a file list from the specified remote pack file
|
||||
|
||||
=== "OTP"
|
||||
```sh
|
||||
./bin/pleroma_ctl emoji gen-pack PACK-URL
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
```sh
|
||||
mix pleroma.emoji gen-pack PACK-URL
|
||||
```
|
||||
|
||||
Currently, only .zip archives are recognized as remote pack files and packs are therefore assumed to be zip archives. This command is intended to run interactively and will first ask you some basic questions about the pack, then download the remote file and generate an SHA256 checksum for it, then generate an emoji file list for you.
|
||||
|
||||
The manifest entry will either be written to a newly created `pack_name.json` file (pack name is asked in questions) or appended to the existing one, *replacing* the old pack with the same name if it was in the file previously.
|
||||
|
||||
The file list will be written to the file specified previously, *replacing* that file. You _should_ check that the file list doesn't contain anything you don't need in the pack, that is, anything that is not an emoji (the whole pack is downloaded, but only emoji files are extracted).
|
||||
|
||||
## Reload emoji packs
|
||||
|
||||
=== "OTP"
|
||||
```sh
|
||||
./bin/pleroma_ctl emoji reload
|
||||
```
|
||||
|
||||
This command only works with OTP releases.
|
113
docs/administration/CLI_tasks/frontend.md
Normal file
113
docs/administration/CLI_tasks/frontend.md
Normal file
@ -0,0 +1,113 @@
|
||||
# Managing frontends
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>] [--primary] [--admin]
|
||||
./bin/pleroma_ctl frontend enable <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>] [--primary] [--admin]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.frontend install <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>] [--primary] [--admin]
|
||||
mix pleroma.frontend enable <frontend> [--ref <ref>] [--file <file>] [--build-url <build-url>] [--path <path>] [--build-dir <build-dir>] [--primary] [--admin]
|
||||
```
|
||||
|
||||
Frontend can be installed either from local zip file, or automatically downloaded from the web.
|
||||
|
||||
You can give all the options directly on the command line, but missing information will be filled out by looking at the data configured under `frontends.available` in the config files.
|
||||
|
||||
Currently, known `<frontend>` values are:
|
||||
|
||||
- [admin-fe](https://git.pleroma.social/pleroma/admin-fe)
|
||||
- [kenoma](http://git.pleroma.social/lambadalambda/kenoma)
|
||||
- [pleroma-fe](http://git.pleroma.social/pleroma/pleroma-fe)
|
||||
- [fedi-fe](https://git.pleroma.social/pleroma/fedi-fe)
|
||||
- [soapbox](https://gitlab.com/soapbox-pub/soapbox)
|
||||
|
||||
You can still install frontends that are not configured, see below.
|
||||
|
||||
## Example installations for a known frontend
|
||||
|
||||
For a frontend configured under the `available` key, it's enough to install it by name.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl frontend install pleroma
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.frontend install pleroma
|
||||
```
|
||||
|
||||
This will download the latest build for the pre-configured `ref` and install it. It can then be configured as the one of the served frontends in the config file (see `primary` or `admin`).
|
||||
|
||||
You can override any of the details. To install a pleroma build from a different URL, you could do this:
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.frontend install pleroma --ref 2hu_edition --build-url https://example.org/raymoo.zip
|
||||
```
|
||||
|
||||
Similarly, you can also install from a local zip file.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.frontend install pleroma --ref mybuild --file ~/Downloads/doomfe.zip
|
||||
```
|
||||
|
||||
The resulting frontend will always be installed into a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
|
||||
|
||||
Careful: This folder will be completely replaced on installation.
|
||||
|
||||
## Example installation for an unknown frontend
|
||||
|
||||
The installation process is the same, but you will have to give all the needed options on the command line. For example:
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.frontend install gensokyo --ref master --build-url https://gensokyo.2hu/builds/marisa.zip
|
||||
```
|
||||
|
||||
If you don't have a zip file but just want to install a frontend from a local path, you can simply copy the files over a folder of this template: `${instance_static}/frontends/${name}/${ref}`.
|
||||
|
||||
## Enabling a frontend
|
||||
|
||||
Once installed, a frontend can be enabled with the `enable` command:
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl frontend enable gensokyo --primary
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.frontend enable gensokyo --primary
|
||||
```
|
@ -0,0 +1,5 @@
|
||||
Every command should be ran as the `pleroma` user from it's home directory. For example if you are superuser, you would have to wrap the command in `su pleroma -s $SHELL -lc "$COMMAND"`.
|
||||
|
||||
??? note "From source note about `MIX_ENV`"
|
||||
|
||||
The `mix` command should be prefixed with the name of environment your Pleroma server is running in, usually it's `MIX_ENV=prod`
|
45
docs/administration/CLI_tasks/instance.md
Normal file
45
docs/administration/CLI_tasks/instance.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Managing instance configuration
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Generate a new configuration file
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl instance gen [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.instance gen [option ...]
|
||||
```
|
||||
|
||||
|
||||
If any of the options are left unspecified, you will be prompted interactively.
|
||||
|
||||
### Options
|
||||
- `-f`, `--force` - overwrite any output files
|
||||
- `-o <path>`, `--output <path>` - the output file for the generated configuration
|
||||
- `--output-psql <path>` - the output file for the generated PostgreSQL setup
|
||||
- `--domain <domain>` - the domain of your instance
|
||||
- `--instance-name <instance_name>` - the name of your instance
|
||||
- `--admin-email <email>` - the email address of the instance admin
|
||||
- `--notify-email <email>` - email address for notifications
|
||||
- `--dbhost <hostname>` - the hostname of the PostgreSQL database to use
|
||||
- `--dbname <database_name>` - the name of the database to use
|
||||
- `--dbuser <username>` - the user (aka role) to use for the database connection
|
||||
- `--dbpass <password>` - the password to use for the database connection
|
||||
- `--rum <Y|N>` - Whether to enable RUM indexes
|
||||
- `--indexable <Y|N>` - Allow/disallow indexing site by search engines
|
||||
- `--db-configurable <Y|N>` - Allow/disallow configuring instance from admin part
|
||||
- `--uploads-dir <path>` - the directory uploads go in when using a local uploader
|
||||
- `--static-dir <path>` - the directory custom public files should be read from (custom emojis, frontend bundle overrides, robots.txt, etc.)
|
||||
- `--listen-ip <ip>` - the ip the app should listen to, defaults to 127.0.0.1
|
||||
- `--listen-port <port>` - the port the app should listen to, defaults to 4000
|
||||
- `--strip-uploads-location <Y|N>` - use ExifTool to strip uploads of sensitive location data
|
||||
- `--read-uploads-description <Y|N>` - use ExifTool to read image descriptions from uploads
|
||||
- `--anonymize-uploads <Y|N>` - randomize uploaded filenames
|
||||
- `--dedupe-uploads <Y|N>` - store files based on their hash to reduce data storage requirements if duplicates are uploaded with different filenames
|
||||
- `--skip-release-env` - skip generation the release environment file
|
||||
- `--release-env-file` - release environment file path
|
20
docs/administration/CLI_tasks/oauth_app.md
Normal file
20
docs/administration/CLI_tasks/oauth_app.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Creating trusted OAuth App
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Create trusted OAuth App.
|
||||
|
||||
Optional params:
|
||||
* `-s SCOPES` - scopes for app, e.g. `read,write,follow,push`.
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl app create -n APP_NAME -r REDIRECT_URI
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.app create -n APP_NAME -r REDIRECT_URI
|
||||
```
|
45
docs/administration/CLI_tasks/relay.md
Normal file
45
docs/administration/CLI_tasks/relay.md
Normal file
@ -0,0 +1,45 @@
|
||||
# Managing relays
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Follow a relay
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl relay follow <relay_url>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.relay follow <relay_url>
|
||||
```
|
||||
|
||||
## Unfollow a remote relay
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl relay unfollow <relay_url>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.relay unfollow <relay_url>
|
||||
```
|
||||
|
||||
## List relay subscriptions
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl relay list
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.relay list
|
||||
```
|
21
docs/administration/CLI_tasks/robots_txt.md
Normal file
21
docs/administration/CLI_tasks/robots_txt.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Managing robots.txt
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Generate a new robots.txt file and add it to the static directory
|
||||
|
||||
The `robots.txt` that ships by default is permissive. It allows well-behaved search engines to index all of your instance's URIs.
|
||||
|
||||
If you want to generate a restrictive `robots.txt`, you can run the following mix task. The generated `robots.txt` will be written in your instance [static directory](../../../configuration/static_dir/).
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl robots_txt disallow_all
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.robots_txt disallow_all
|
||||
```
|
21
docs/administration/CLI_tasks/uploads.md
Normal file
21
docs/administration/CLI_tasks/uploads.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Managing uploads
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Migrate uploads from local to remote storage
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl uploads migrate_local <target_uploader> [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.uploads migrate_local <target_uploader> [option ...]
|
||||
```
|
||||
|
||||
### Options
|
||||
- `--delete` - delete local uploads after migrating them to the target uploader
|
||||
|
||||
A list of available uploaders can be seen in [Configuration Cheat Sheet](../../configuration/cheatsheet.md#pleromaupload)
|
302
docs/administration/CLI_tasks/user.md
Normal file
302
docs/administration/CLI_tasks/user.md
Normal file
@ -0,0 +1,302 @@
|
||||
# Managing users
|
||||
|
||||
{! backend/administration/CLI_tasks/general_cli_task_info.include !}
|
||||
|
||||
## Create a user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user new <nickname> <email> [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user new <nickname> <email> [option ...]
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
- `--name <name>` - the user's display name
|
||||
- `--bio <bio>` - the user's bio
|
||||
- `--password <password>` - the user's password
|
||||
- `--moderator`/`--no-moderator` - whether the user should be a moderator
|
||||
- `--admin`/`--no-admin` - whether the user should be an admin
|
||||
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
|
||||
|
||||
## List local users
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user list
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user list
|
||||
```
|
||||
|
||||
|
||||
## Generate an invite link
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user invite [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user invite [option ...]
|
||||
```
|
||||
|
||||
|
||||
### Options
|
||||
- `--expires-at DATE` - last day on which token is active (e.g. "2019-04-05")
|
||||
- `--max-use NUMBER` - maximum numbers of token uses
|
||||
|
||||
## List generated invites
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user invites
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user invites
|
||||
```
|
||||
|
||||
|
||||
## Revoke invite
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user revoke_invite <token>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user revoke_invite <token>
|
||||
```
|
||||
|
||||
|
||||
## Delete a user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user rm <nickname>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user rm <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Delete user's posts and interactions
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user delete_activities <nickname>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user delete_activities <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Sign user out from all applications (delete user's OAuth tokens and authorizations)
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user sign_out <nickname>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user sign_out <nickname>
|
||||
```
|
||||
|
||||
## Activate a user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user activate NICKNAME
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user activate NICKNAME
|
||||
```
|
||||
|
||||
## Deactivate a user and unsubscribes local users from the user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user deactivate NICKNAME
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user deactivate NICKNAME
|
||||
```
|
||||
|
||||
|
||||
## Deactivate all accounts from an instance and unsubscribe local users on it
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user deactivate_all_from_instance <instance>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user deactivate_all_from_instance <instance>
|
||||
```
|
||||
|
||||
|
||||
## Create a password reset link for user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user reset_password <nickname>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user reset_password <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Disable Multi Factor Authentication (MFA/2FA) for a user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user reset_mfa <nickname>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user reset_mfa <nickname>
|
||||
```
|
||||
|
||||
|
||||
## Set the value of the given user's settings
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user set <nickname> [option ...]
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user set <nickname> [option ...]
|
||||
```
|
||||
|
||||
### Options
|
||||
- `--admin`/`--no-admin` - whether the user should be an admin
|
||||
- `--confirmed`/`--no-confirmed` - whether the user account is confirmed
|
||||
- `--locked`/`--no-locked` - whether the user should be locked
|
||||
- `--moderator`/`--no-moderator` - whether the user should be a moderator
|
||||
|
||||
## Add tags to a user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user tag <nickname> <tags>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user tag <nickname> <tags>
|
||||
```
|
||||
|
||||
|
||||
## Delete tags from a user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user untag <nickname> <tags>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user untag <nickname> <tags>
|
||||
```
|
||||
|
||||
|
||||
## Toggle confirmation status of the user
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user confirm <nickname>
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user confirm <nickname>
|
||||
```
|
||||
|
||||
## Set confirmation status for all regular active users
|
||||
*Admins and moderators are excluded*
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user confirm_all
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user confirm_all
|
||||
```
|
||||
|
||||
## Revoke confirmation status for all regular active users
|
||||
*Admins and moderators are excluded*
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl user unconfirm_all
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.user unconfirm_all
|
||||
```
|
41
docs/administration/backup.md
Normal file
41
docs/administration/backup.md
Normal file
@ -0,0 +1,41 @@
|
||||
# Backup/Restore/Move/Remove your instance
|
||||
|
||||
## Backup
|
||||
|
||||
1. Stop the Pleroma service.
|
||||
2. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
||||
3. Run `sudo -Hu postgres pg_dump -d <pleroma_db> --format=custom -f </path/to/backup_location/pleroma.pgdump>` (make sure the postgres user has write access to the destination file)
|
||||
4. Copy `pleroma.pgdump`, `config/prod.secret.exs`, `config/setup_db.psql` (if still available) and the `uploads` folder to your backup destination. If you have other modifications, copy those changes too.
|
||||
5. Restart the Pleroma service.
|
||||
|
||||
## Restore/Move
|
||||
|
||||
1. Optionally reinstall Pleroma (either on the same server or on another server if you want to move servers).
|
||||
2. Stop the Pleroma service.
|
||||
3. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
||||
4. Copy the above mentioned files back to their original position.
|
||||
5. Drop the existing database and user if restoring in-place. `sudo -Hu postgres psql -c 'DROP DATABASE <pleroma_db>;';` `sudo -Hu postgres psql -c 'DROP USER <pleroma_db>;'`
|
||||
6. Restore the database schema and pleroma postgres role the with the original `setup_db.psql` if you have it: `sudo -Hu postgres psql -f config/setup_db.psql`.
|
||||
|
||||
Alternatively, run the `mix pleroma.instance gen` task again. You can ignore most of the questions, but make the database user, name, and password the same as found in your backup of `config/prod.secret.exs`. Then run the restoration of the pleroma role and schema with of the generated `config/setup_db.psql` as instructed above. You may delete the `config/generated_config.exs` file as it is not needed.
|
||||
|
||||
7. Now restore the Pleroma instance's data into the empty database schema: `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
|
||||
8. If you installed a newer Pleroma version, you should run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
|
||||
9. Restart the Pleroma service.
|
||||
10. Run `sudo -Hu postgres vacuumdb --all --analyze-in-stages`. This will quickly generate the statistics so that postgres can properly plan queries.
|
||||
11. If setting up on a new server configure Nginx by using the `installation/pleroma.nginx` config sample or reference the Pleroma installation guide for your OS which contains the Nginx configuration instructions.
|
||||
|
||||
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
|
||||
|
||||
## Remove
|
||||
|
||||
1. Optionally you can remove the users of your instance. This will trigger delete requests for their accounts and posts. Note that this is 'best effort' and doesn't mean that all traces of your instance will be gone from the fediverse.
|
||||
* You can do this from the admin-FE where you can select all local users and delete the accounts using the *Moderate multiple users* dropdown.
|
||||
* You can also list local users and delete them individualy using the CLI tasks for [Managing users](./CLI_tasks/user.md).
|
||||
2. Stop the Pleroma service `systemctl stop pleroma`
|
||||
3. Disable pleroma from systemd `systemctl disable pleroma`
|
||||
4. Remove the files and folders you created during installation (see installation guide). This includes the pleroma, nginx and systemd files and folders.
|
||||
5. Reload nginx now that the configuration is removed `systemctl reload nginx`
|
||||
6. Remove the database and database user `sudo -Hu postgres psql -c 'DROP DATABASE <pleroma_db>;';` `sudo -Hu postgres psql -c 'DROP USER <pleroma_db>;'`
|
||||
7. Remove the system user `userdel pleroma`
|
||||
8. Remove the dependencies that you don't need anymore (see installation guide). Make sure you don't remove packages that are still needed for other software that you have running!
|
27
docs/administration/updating.md
Normal file
27
docs/administration/updating.md
Normal file
@ -0,0 +1,27 @@
|
||||
# Updating your instance
|
||||
|
||||
You should **always check the [release notes/changelog](https://git.pleroma.social/pleroma/pleroma/-/releases)** in case there are config deprecations, special update steps, etc.
|
||||
|
||||
Besides that, doing the following is generally enough:
|
||||
|
||||
## For OTP installations
|
||||
|
||||
```sh
|
||||
# Download the new release
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl update"
|
||||
|
||||
# Migrate the database, you are advised to stop the instance before doing that
|
||||
su pleroma -s $SHELL -lc "./bin/pleroma_ctl migrate"
|
||||
```
|
||||
|
||||
## For from source installations (using git)
|
||||
|
||||
1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
||||
2. Run `git checkout <tagged release>` [^1]. e.g. `git checkout v2.4.5` This pulls the [tagged release](https://git.pleroma.social/pleroma/pleroma/-/releases) from upstream.
|
||||
3. Run `mix deps.get` [^1]. This pulls in any new dependencies.
|
||||
4. Stop the Pleroma service.
|
||||
5. Run `mix ecto.migrate` [^1] [^2]. This task performs database migrations, if there were any.
|
||||
6. Start the Pleroma service.
|
||||
|
||||
[^1]: Depending on which install guide you followed (for example on Debian/Ubuntu), you want to run `git` and `mix` tasks as `pleroma` user by adding `sudo -Hu pleroma` before the command.
|
||||
[^2]: Prefix with `MIX_ENV=prod` to run it using the production config file.
|
125
docs/clients.md
Normal file
125
docs/clients.md
Normal file
@ -0,0 +1,125 @@
|
||||
# Pleroma Clients
|
||||
Note: Additional clients may be working but theses are officially supporting Pleroma.
|
||||
Feel free to contact us to be added to this list!
|
||||
|
||||
## Desktop
|
||||
### Social
|
||||
- Source Code: <https://gitlab.gnome.org/World/Social>
|
||||
- Contact: [@brainblasted@social.libre.fi](https://social.libre.fi/users/brainblasted)
|
||||
- Platforms: Linux (GNOME)
|
||||
- Note(2019-01-28): Not at a pre-alpha stage yet
|
||||
- Features: MastoAPI
|
||||
|
||||
### Whalebird
|
||||
- Homepage: <https://whalebird.social/>
|
||||
- Source Code: <https://github.com/h3poteto/whalebird-desktop>
|
||||
- Contact: [@whalebird@pleroma.io](https://pleroma.io/users/whalebird)
|
||||
- Platforms: Windows, Mac, Linux
|
||||
- Features: MastoAPI, Streaming Ready
|
||||
|
||||
### Fedistar
|
||||
- Homepage: <https://fedistar.net>
|
||||
- Source Code: <https://github.com/h3poteto/fedistar>
|
||||
- Contact: [@fedistar@pleroma.io](https://pleroma.io/users/fedistar)
|
||||
- Platforms: Windows, Mac, Linux
|
||||
- Features: MastoAPI, Streaming Ready
|
||||
|
||||
## Handheld
|
||||
### AndStatus
|
||||
- Homepage: <http://andstatus.org/>
|
||||
- Source Code: <https://github.com/andstatus/andstatus/>
|
||||
- Platforms: Android
|
||||
- Features: MastoAPI, ActivityPub (Client-to-Server)
|
||||
|
||||
### Amaroq
|
||||
- Homepage: <https://itunes.apple.com/us/app/amaroq-for-mastodon/id1214116200>
|
||||
- Source Code: <https://github.com/ReticentJohn/Amaroq>
|
||||
- Contact: [@eurasierboy@mastodon.social](https://mastodon.social/users/eurasierboy)
|
||||
- Platforms: iOS
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Fedilab
|
||||
- Homepage: <https://fedilab.app/>
|
||||
- Source Code: <https://framagit.org/tom79/fedilab/>
|
||||
- Contact: [@fedilab@framapiaf.org](https://framapiaf.org/users/fedilab)
|
||||
- Platforms: Android
|
||||
- Features: MastoAPI, Streaming Ready, Moderation, Text Formatting
|
||||
|
||||
### Kyclos
|
||||
- Source Code: <https://git.pleroma.social/pleroma/harbour-kyclos>
|
||||
- Platforms: SailfishOS
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Husky
|
||||
- Source code: <https://git.mentality.rip/FWGS/Husky>
|
||||
- Contact: [@Husky@enigmatic.observer](https://enigmatic.observer/users/Husky)
|
||||
- Platforms: Android
|
||||
- Features: MastoAPI, No Streaming, Emoji Reactions, Text Formatting, FE Stickers
|
||||
|
||||
### Fedi
|
||||
- Homepage: <https://www.fediapp.com/>
|
||||
- Source Code: Proprietary, but gratis
|
||||
- Platforms: iOS, Android
|
||||
- Features: MastoAPI, Pleroma-specific features like Reactions
|
||||
|
||||
### Tusky
|
||||
- Homepage: <https://tuskyapp.github.io/>
|
||||
- Source Code: <https://github.com/tuskyapp/Tusky>
|
||||
- Contact: [@ConnyDuck@mastodon.social](https://mastodon.social/users/ConnyDuck)
|
||||
- Platforms: Android
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Twidere
|
||||
- Homepage: <https://twidere.mariotaku.org/>
|
||||
- Source Code: <https://github.com/TwidereProject/Twidere-Android/>
|
||||
- Contact: <me@mariotaku.org>
|
||||
- Platform: Android
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Indigenous
|
||||
- Homepage: <https://indigenous.realize.be/>
|
||||
- Source Code: <https://github.com/swentel/indigenous-android/>
|
||||
- Contact: [@swentel@realize.be](https://realize.be)
|
||||
- Platforms: Android
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
## Alternative Web Interfaces
|
||||
### Brutaldon
|
||||
- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>
|
||||
- Source Code: <https://git.carcosa.net/jmcbray/brutaldon>
|
||||
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Halcyon
|
||||
- Source Code: <https://notabug.org/halcyon-suite/halcyon>
|
||||
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
|
||||
- Features: MastoAPI, Streaming Ready
|
||||
|
||||
### Pinafore
|
||||
- Homepage: <https://pinafore.social/>
|
||||
- Source Code: <https://github.com/nolanlawson/pinafore>
|
||||
- Contact: [@pinafore@mastodon.technology](https://mastodon.technology/users/pinafore)
|
||||
- Note: Pleroma support is a secondary goal
|
||||
- Features: MastoAPI, No Streaming
|
||||
|
||||
### Sengi
|
||||
- Homepage: <https://nicolasconstant.github.io/sengi/>
|
||||
- Source Code: <https://github.com/NicolasConstant/sengi>
|
||||
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
|
||||
- Features: MastoAPI
|
||||
|
||||
### DashFE
|
||||
- Source Code: <https://notabug.org/daisuke/DashboardFE>
|
||||
- Contact: [@dashfe@stereophonic.space](https://stereophonic.space/users/dashfe)
|
||||
|
||||
### BloatFE
|
||||
- Source Code: <https://git.freesoftwareextremist.com/bloat/>
|
||||
- Contact: [@r@freesoftwareextremist.com](https://freesoftwareextremist.com/users/r)
|
||||
- Features: Does not requires JavaScript
|
||||
- Features: MastoAPI
|
||||
|
||||
### Glitch-lily
|
||||
- Source Code: <https://lily.kazv.moe/infra/glitch-lily>
|
||||
- Contact: [@tusooa@kazv.moe](https://kazv.moe/users/tusooa)
|
||||
- Features: MastoAPI
|
||||
- Based on [glitch-soc](https://github.com/glitch-soc/mastodon) frontend
|
1
docs/configuration/auth.md
Normal file
1
docs/configuration/auth.md
Normal file
@ -0,0 +1 @@
|
||||
See `Authentication` section of [the configuration cheatsheet](../configuration/cheatsheet.md#authentication).
|
1198
docs/configuration/cheatsheet.md
Normal file
1198
docs/configuration/cheatsheet.md
Normal file
File diff suppressed because it is too large
Load Diff
69
docs/configuration/custom_emoji.md
Normal file
69
docs/configuration/custom_emoji.md
Normal file
@ -0,0 +1,69 @@
|
||||
# Custom Emoji
|
||||
|
||||
Before you add your own custom emoji, check if they are available in an existing pack.
|
||||
See `Mix.Tasks.Pleroma.Emoji` for information about emoji packs.
|
||||
|
||||
To add custom emoji:
|
||||
|
||||
* Create the `STATIC-DIR/emoji/` directory if it doesn't exist
|
||||
(`STATIC-DIR` is configurable, `instance/static/` by default)
|
||||
* Create a directory with whatever name you want (custom is a good name to show the purpose of it).
|
||||
This will create a local emoji pack.
|
||||
* Put your `.png` emoji files in that directory. In case of conflicts, you can create an `emoji.txt`
|
||||
file in that directory and specify a custom shortcode using the following format:
|
||||
`shortcode, file-path, tag1, tag2, etc`. One emoji per line. Note that if you do so,
|
||||
you'll have to list all other emojis in the pack too.
|
||||
* Either restart pleroma or connect to the iex session pleroma's running and
|
||||
run `Pleroma.Emoji.reload/0` in it.
|
||||
|
||||
Example:
|
||||
|
||||
image files (in `instance/static/emoji/custom`): `happy.png` and `sad.png`
|
||||
|
||||
content of `emoji.txt`:
|
||||
```
|
||||
happy, /emoji/custom/happy.png, Tag1,Tag2
|
||||
sad, /emoji/custom/sad.png, Tag1
|
||||
foo, /emoji/custom/foo.png
|
||||
```
|
||||
|
||||
The files should be PNG (APNG is okay with `.png` for `image/png` Content-type) and under 50kb for compatibility with mastodon.
|
||||
|
||||
Default file extentions and locations for emojis are set in `config.exs`. To use different locations or file-extentions, add the `shortcode_globs` to your secrets file (`prod.secret.exs` or `dev.secret.exs`) and edit it. Note that not all fediverse-software will show emojis with other file extentions:
|
||||
```elixir
|
||||
config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png", "/emoji/custom/**/*.gif"]
|
||||
```
|
||||
|
||||
## Emoji tags (groups)
|
||||
|
||||
Default tags are set in `config.exs`. To set your own tags, copy the structure to your secrets file (`prod.secret.exs` or `dev.secret.exs`) and edit it.
|
||||
```elixir
|
||||
config :pleroma, :emoji,
|
||||
shortcode_globs: ["/emoji/custom/**/*.png"],
|
||||
groups: [
|
||||
Finmoji: "/finmoji/128px/*-128.png",
|
||||
Custom: ["/emoji/*.png", "/emoji/custom/*.png"]
|
||||
]
|
||||
```
|
||||
|
||||
Order of the `groups` matters, so to override default tags just put your group on top of the list. E.g:
|
||||
```elixir
|
||||
config :pleroma, :emoji,
|
||||
shortcode_globs: ["/emoji/custom/**/*.png"],
|
||||
groups: [
|
||||
"Finmoji special": "/finmoji/128px/a_trusted_friend-128.png", # special file
|
||||
"Cirno": "/emoji/custom/cirno*.png", # png files in /emoji/custom/ which start with `cirno`
|
||||
"Special group": "/emoji/custom/special_folder/*.png", # png files in /emoji/custom/special_folder/
|
||||
"Another group": "/emoji/custom/special_folder/*/.png", # png files in /emoji/custom/special_folder/ subfolders
|
||||
Finmoji: "/finmoji/128px/*-128.png",
|
||||
Custom: ["/emoji/*.png", "/emoji/custom/*.png"]
|
||||
]
|
||||
```
|
||||
|
||||
Priority of tags assigns in emoji.txt and custom.txt:
|
||||
|
||||
`tag in file > special group setting in config.exs > default setting in config.exs`
|
||||
|
||||
Priority for globs:
|
||||
|
||||
`special group setting in config.exs > default setting in config.exs`
|
117
docs/configuration/hardening.md
Normal file
117
docs/configuration/hardening.md
Normal file
@ -0,0 +1,117 @@
|
||||
# Hardening your instance
|
||||
Here are some suggestions which improve the security of parts of your Pleroma instance.
|
||||
|
||||
## Configuration file
|
||||
|
||||
These changes should go into `prod.secret.exs` or `dev.secret.exs`, depending on your `MIX_ENV` value.
|
||||
|
||||
### `http`
|
||||
|
||||
> Recommended value: `[ip: {127, 0, 0, 1}]`
|
||||
|
||||
This sets the Pleroma application server to only listen to the localhost interface. This way, you can only reach your server over the Internet by going through the reverse proxy. By default, Pleroma listens on all interfaces.
|
||||
|
||||
### `secure_cookie_flag`
|
||||
|
||||
> Recommended value: `true`
|
||||
|
||||
This sets the `secure` flag on Pleroma’s session cookie. This makes sure, that the cookie is only accepted over encrypted HTTPs connections. This implicitly renames the cookie from `pleroma_key` to `__Host-pleroma-key` which enforces some restrictions. (see [cookie prefixes](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#Cookie_prefixes))
|
||||
|
||||
### `:http_security`
|
||||
|
||||
> Recommended value: `true`
|
||||
|
||||
This will send additional HTTP security headers to the clients, including:
|
||||
|
||||
* `X-XSS-Protection: "1; mode=block"`
|
||||
* `X-Permitted-Cross-Domain-Policies: "none"`
|
||||
* `X-Frame-Options: "DENY"`
|
||||
* `X-Content-Type-Options: "nosniff"`
|
||||
* `X-Download-Options: "noopen"`
|
||||
|
||||
A content security policy (CSP) will also be set:
|
||||
|
||||
```csp
|
||||
content-security-policy:
|
||||
default-src 'none';
|
||||
base-uri 'self';
|
||||
frame-ancestors 'none';
|
||||
img-src 'self' data: blob: https:;
|
||||
media-src 'self' https:;
|
||||
style-src 'self' 'unsafe-inline';
|
||||
font-src 'self';
|
||||
script-src 'self';
|
||||
connect-src 'self' wss://example.tld;
|
||||
manifest-src 'self';
|
||||
upgrade-insecure-requests;
|
||||
```
|
||||
|
||||
#### `sts`
|
||||
|
||||
> Recommended value: `true`
|
||||
|
||||
An additional “Strict transport security” header will be sent with the configured `sts_max_age` parameter. This tells the browser, that the domain should only be accessed over a secure HTTPs connection.
|
||||
|
||||
#### `ct_max_age`
|
||||
|
||||
An additional “Expect-CT” header will be sent with the configured `ct_max_age` parameter. This enforces the use of TLS certificates that are published in the certificate transparency log. (see [Expect-CT](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect-CT))
|
||||
|
||||
#### `referrer_policy`
|
||||
|
||||
> Recommended value: `same-origin`
|
||||
|
||||
If you click on a link, your browser’s request to the other site will include from where it is coming from. The “Referrer policy” header tells the browser how and if it should send this information. (see [Referrer policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy))
|
||||
|
||||
### Uploaded media and media proxy
|
||||
|
||||
It is STRONGLY RECOMMENDED to serve both the locally-uploaded media and the media proxy from another domain than the domain that Pleroma runs on, if applicable.
|
||||
|
||||
```elixir
|
||||
config :pleroma, :media_proxy,
|
||||
base_url: "https://some.other.domain"
|
||||
|
||||
config :pleroma, Pleroma.Upload,
|
||||
base_url: "https://some.other.domain/media"
|
||||
```
|
||||
|
||||
See `installation/pleroma-mediaproxy.nginx` for examples on how to configure your media proxy.
|
||||
|
||||
## systemd
|
||||
|
||||
A systemd unit example is provided at `installation/pleroma.service`.
|
||||
|
||||
### PrivateTmp
|
||||
|
||||
> Recommended value: `true`
|
||||
|
||||
Use private `/tmp` and `/var/tmp` folders inside a new file system namespace, which are discarded after the process stops.
|
||||
|
||||
### ProtectHome
|
||||
|
||||
> Recommended value: `true`
|
||||
|
||||
The `/home`, `/root`, and `/run/user` folders can not be accessed by this service anymore. If your Pleroma user has its home folder in one of the restricted places, or use one of these folders as its working directory, you have to set this to `false`.
|
||||
|
||||
### ProtectSystem
|
||||
|
||||
> Recommended value: `full`
|
||||
|
||||
Mount `/usr`, `/boot`, and `/etc` as read-only for processes invoked by this service.
|
||||
|
||||
### PrivateDevices
|
||||
|
||||
> Recommended value: `true`
|
||||
|
||||
Sets up a new `/dev` mount for the process and only adds API pseudo devices like `/dev/null`, `/dev/zero` or `/dev/random` but not physical devices. This may not work on devices like the Raspberry Pi, where you need to set this to `false`.
|
||||
|
||||
### NoNewPrivileges
|
||||
|
||||
> Recommended value: `true`
|
||||
|
||||
Ensures that the service process and all its children can never gain new privileges through `execve()`.
|
||||
|
||||
### CapabilityBoundingSet
|
||||
|
||||
> Recommended value: `~CAP_SYS_ADMIN`
|
||||
|
||||
Drops the sysadmin capability from the daemon.
|
@ -0,0 +1,62 @@
|
||||
# How to use a different domain name for Pleroma and the users it serves
|
||||
|
||||
Pleroma users are primarily identified by a `user@example.org` handle, and you might want this identifier to be the same as your email or jabber account, for instance.
|
||||
However, in this case, you are almost certainly serving some web content on `https://example.org` already, and you might want to use another domain (say `pleroma.example.org`) for Pleroma itself.
|
||||
|
||||
Pleroma supports that, but it might be tricky to set up, and any error might prevent you from federating with other instances.
|
||||
|
||||
*If you are already running Pleroma on `example.org`, it is no longer possible to move it to `pleroma.example.org`.*
|
||||
|
||||
## Account identifiers
|
||||
|
||||
It is important to understand that for federation purposes, a user in Pleroma has two unique identifiers associated:
|
||||
|
||||
- A webfinger `acct:` URI, used for discovery and as a verifiable global name for the user across Pleroma instances. In our example, our account's acct: URI is `acct:user@example.org`
|
||||
- An author/actor URI, used in every other aspect of federation. This is the way in which users are identified in ActivityPub, the underlying protocol used for federation with other Pleroma instances.
|
||||
In our case, it is `https://pleroma.example.org/users/user`.
|
||||
|
||||
Both account identifiers are unique and required for Pleroma. An important risk if you set up your Pleroma instance incorrectly is to create two users (with different acct: URIs) with conflicting author/actor URIs.
|
||||
|
||||
## WebFinger
|
||||
|
||||
As said earlier, each Pleroma user has an `acct`: URI, which is used for discovery and authentication. When you add @user@example.org, a webfinger query is performed. This is done in two steps:
|
||||
|
||||
1. Querying `https://example.org/.well-known/host-meta` (where the domain of the URL matches the domain part of the `acct`: URI) to get information on how to perform the query.
|
||||
This file will indeed contain a URL template of the form `https://example.org/.well-known/webfinger?resource={uri}` that will be used in the second step.
|
||||
2. Fill the returned template with the `acct`: URI to be queried and perform the query: `https://example.org/.well-known/webfinger?resource=acct:user@example.org`
|
||||
|
||||
## Configuring your Pleroma instance
|
||||
|
||||
**_DO NOT ATTEMPT TO CONFIGURE YOUR INSTANCE THIS WAY IF YOU DID NOT UNDERSTAND THE ABOVE_**
|
||||
|
||||
### Configuring Pleroma
|
||||
|
||||
Pleroma has a two configuration settings to enable using different domains for your users and Pleroma itself. `host` in `Pleroma.Web.Endpoint` and `domain` in `Pleroma.Web.WebFinger`. When the latter is not set, it defaults to the value of `host`.
|
||||
|
||||
*Be extra careful when configuring your Pleroma instance, as changing `host` may cause remote instances to register different accounts with the same author/actor URI, which will result in federation issues!*
|
||||
|
||||
```elixir
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
url: [host: "pleroma.example.org"]
|
||||
|
||||
config :pleroma, Pleroma.Web.WebFinger, domain: "example.org"
|
||||
```
|
||||
|
||||
- `domain` - is the domain for which your Pleroma instance has authority, it's the domain used in `acct:` URI. In our example, `domain` would be set to `example.org`. This is used in WebFinger account ids, which are the canonical account identifier in some other fediverse software like Mastodon. **If you change `domain`, the accounts on your server will be shown as different accounts in those software**.
|
||||
- `host` - is the domain used for any URL generated for your instance, including the author/actor URL's. In our case, that would be `pleroma.example.org`. This is used in AP ids, which are the canonical account identifier in Pleroma and some other fediverse software. **You should not change this after you have set up the instance**.
|
||||
|
||||
### Configuring WebFinger domain
|
||||
|
||||
Now, you have Pleroma running at `https://pleroma.example.org` as well as a website at `https://example.org`. If you recall how webfinger queries work, the first step is to query `https://example.org/.well-known/host-meta`, which will contain an URL template.
|
||||
|
||||
Therefore, the easiest way to configure `example.org` is to redirect `/.well-known/host-meta` to `pleroma.example.org`.
|
||||
|
||||
With nginx, it would be as simple as adding:
|
||||
|
||||
```nginx
|
||||
location = /.well-known/host-meta {
|
||||
return 301 https://pleroma.example.org$request_uri;
|
||||
}
|
||||
```
|
||||
|
||||
in example.org's server block.
|
155
docs/configuration/howto_database_config.md
Normal file
155
docs/configuration/howto_database_config.md
Normal file
@ -0,0 +1,155 @@
|
||||
# How to activate Pleroma in-database configuration
|
||||
## Explanation
|
||||
|
||||
The configuration of Pleroma has traditionally been managed with a config file, e.g. `config/prod.secret.exs`. This method requires a restart of the application for any configuration changes to take effect. We have made it possible to control most settings in the AdminFE interface after running a migration script.
|
||||
|
||||
## Migration to database config
|
||||
|
||||
1. Run the mix task to migrate to the database.
|
||||
|
||||
**Source:**
|
||||
|
||||
```
|
||||
$ mix pleroma.config migrate_to_db
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
**OTP:**
|
||||
|
||||
*Note: OTP users need Pleroma to be running for `pleroma_ctl` commands to work*
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config migrate_to_db
|
||||
```
|
||||
|
||||
```
|
||||
Migrating settings from file: /home/pleroma/config/dev.secret.exs
|
||||
|
||||
Settings for key instance migrated.
|
||||
Settings for group :pleroma migrated.
|
||||
```
|
||||
|
||||
2. It is recommended to backup your config file now.
|
||||
|
||||
```
|
||||
cp config/dev.secret.exs config/dev.secret.exs.orig
|
||||
```
|
||||
|
||||
3. Edit your Pleroma config to enable database configuration:
|
||||
|
||||
```
|
||||
config :pleroma, configurable_from_database: true
|
||||
```
|
||||
|
||||
4. ⚠️ **THIS IS NOT REQUIRED** ⚠️
|
||||
|
||||
Now you can edit your config file and strip it down to the only settings which are not possible to control in the database. e.g., the Postgres (Repo) and webserver (Endpoint) settings cannot be controlled in the database because the application needs the settings to start up and access the database.
|
||||
|
||||
Any settings in the database will override those in the config file, but you may find it less confusing if the setting is only declared in one place.
|
||||
|
||||
A non-exhaustive list of settings that are only possible in the config file include the following:
|
||||
|
||||
* config :pleroma, Pleroma.Web.Endpoint
|
||||
* config :pleroma, Pleroma.Repo
|
||||
* config :pleroma, configurable\_from\_database
|
||||
* config :pleroma, :database, rum_enabled
|
||||
* config :pleroma, :connections_pool
|
||||
|
||||
Here is an example of a server config stripped down after migration:
|
||||
|
||||
```
|
||||
import Config
|
||||
|
||||
config :pleroma, Pleroma.Web.Endpoint,
|
||||
url: [host: "cool.pleroma.site", scheme: "https", port: 443]
|
||||
|
||||
config :pleroma, Pleroma.Repo,
|
||||
adapter: Ecto.Adapters.Postgres,
|
||||
username: "pleroma",
|
||||
password: "MySecretPassword",
|
||||
database: "pleroma_prod",
|
||||
hostname: "localhost"
|
||||
|
||||
config :pleroma, configurable_from_database: true
|
||||
```
|
||||
|
||||
5. Restart your instance and you can now access the Settings tab in AdminFE.
|
||||
|
||||
|
||||
## Reverting back from database config
|
||||
|
||||
1. Run the mix task to migrate back from the database. You'll receive some debugging output and a few messages informing you of what happened.
|
||||
|
||||
**Source:**
|
||||
|
||||
```
|
||||
$ mix pleroma.config migrate_from_db
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
**OTP:**
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config migrate_from_db
|
||||
```
|
||||
|
||||
```
|
||||
10:26:30.593 [debug] QUERY OK source="config" db=9.8ms decode=1.2ms queue=26.0ms idle=0.0ms
|
||||
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||
|
||||
10:26:30.659 [debug] QUERY OK source="config" db=1.1ms idle=80.7ms
|
||||
SELECT c0."id", c0."key", c0."group", c0."value", c0."inserted_at", c0."updated_at" FROM "config" AS c0 []
|
||||
Database configuration settings have been saved to config/dev.exported_from_db.secret.exs
|
||||
```
|
||||
|
||||
2. Remove `config :pleroma, configurable_from_database: true` from your config. The in-database configuration still exists, but it will not be used. Future migrations will erase the database config before importing your config file again.
|
||||
|
||||
3. Restart your instance.
|
||||
|
||||
## Debugging
|
||||
|
||||
### Clearing database config
|
||||
You can clear the database config with the following command:
|
||||
|
||||
**Source:**
|
||||
|
||||
```
|
||||
$ mix pleroma.config reset
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
**OTP:**
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config reset
|
||||
```
|
||||
|
||||
Additionally, every time you migrate the configuration to the database the config table is automatically truncated to ensure a clean migration.
|
||||
|
||||
### Manually removing a setting
|
||||
If you encounter a situation where the server cannot run properly because of an invalid setting in the database and this is preventing you from accessing AdminFE, you can manually remove the offending setting if you know which one it is.
|
||||
|
||||
e.g., here is an example showing a the removal of the `config :pleroma, :instance` settings:
|
||||
|
||||
**Source:**
|
||||
|
||||
```
|
||||
$ mix pleroma.config delete pleroma instance
|
||||
Are you sure you want to continue? [n] y
|
||||
config :pleroma, :instance deleted from the ConfigDB.
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
**OTP:**
|
||||
|
||||
```
|
||||
$ ./bin/pleroma_ctl config delete pleroma instance
|
||||
Are you sure you want to continue? [n] y
|
||||
config :pleroma, :instance deleted from the ConfigDB.
|
||||
```
|
||||
|
||||
Now the `config :pleroma, :instance` settings have been removed from the database.
|
136
docs/configuration/howto_ejabberd.md
Normal file
136
docs/configuration/howto_ejabberd.md
Normal file
@ -0,0 +1,136 @@
|
||||
# Configuring Ejabberd (XMPP Server) to use Pleroma for authentication
|
||||
|
||||
If you want to give your Pleroma users an XMPP (chat) account, you can configure [Ejabberd](https://github.com/processone/ejabberd) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
|
||||
|
||||
In general, you just have to follow the configuration described at [https://docs.ejabberd.im/admin/configuration/authentication/#external-script](https://docs.ejabberd.im/admin/configuration/authentication/#external-script). Please read this section carefully.
|
||||
|
||||
Copy the script below to suitable path on your system and set owner and permissions. Also do not forget adjusting `PLEROMA_HOST` and `PLEROMA_PORT`, if necessary.
|
||||
|
||||
```bash
|
||||
cp pleroma_ejabberd_auth.py /etc/ejabberd/pleroma_ejabberd_auth.py
|
||||
chown ejabberd /etc/ejabberd/pleroma_ejabberd_auth.py
|
||||
chmod 700 /etc/ejabberd/pleroma_ejabberd_auth.py
|
||||
```
|
||||
|
||||
Set external auth params in ejabberd.yaml file:
|
||||
|
||||
```bash
|
||||
auth_method: [external]
|
||||
extauth_program: "python3 /etc/ejabberd/pleroma_ejabberd_auth.py"
|
||||
extauth_instances: 3
|
||||
auth_use_cache: false
|
||||
```
|
||||
|
||||
Restart / reload your ejabberd service.
|
||||
|
||||
After restarting your Ejabberd server, your users should now be able to connect with their Pleroma credentials.
|
||||
|
||||
|
||||
```python
|
||||
import sys
|
||||
import struct
|
||||
import http.client
|
||||
from base64 import b64encode
|
||||
import logging
|
||||
|
||||
|
||||
PLEROMA_HOST = "127.0.0.1"
|
||||
PLEROMA_PORT = "4000"
|
||||
AUTH_ENDPOINT = "/api/v1/accounts/verify_credentials"
|
||||
USER_ENDPOINT = "/api/v1/accounts"
|
||||
LOGFILE = "/var/log/ejabberd/pleroma_auth.log"
|
||||
|
||||
logging.basicConfig(filename=LOGFILE, level=logging.INFO)
|
||||
|
||||
|
||||
# Pleroma functions
|
||||
def create_connection():
|
||||
return http.client.HTTPConnection(PLEROMA_HOST, PLEROMA_PORT)
|
||||
|
||||
|
||||
def verify_credentials(user: str, password: str) -> bool:
|
||||
user_pass_b64 = b64encode("{}:{}".format(
|
||||
user, password).encode('utf-8')).decode("ascii")
|
||||
params = {}
|
||||
headers = {
|
||||
"Authorization": "Basic {}".format(user_pass_b64)
|
||||
}
|
||||
|
||||
try:
|
||||
conn = create_connection()
|
||||
conn.request("GET", AUTH_ENDPOINT, params, headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
if response.status == 200:
|
||||
return True
|
||||
|
||||
return False
|
||||
except Exception as e:
|
||||
logging.info("Can not connect: %s", str(e))
|
||||
return False
|
||||
|
||||
|
||||
def does_user_exist(user: str) -> bool:
|
||||
conn = create_connection()
|
||||
conn.request("GET", "{}/{}".format(USER_ENDPOINT, user))
|
||||
|
||||
response = conn.getresponse()
|
||||
if response.status == 200:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def auth(username: str, server: str, password: str) -> bool:
|
||||
return verify_credentials(username, password)
|
||||
|
||||
|
||||
def isuser(username, server):
|
||||
return does_user_exist(username)
|
||||
|
||||
|
||||
def read():
|
||||
(pkt_size,) = struct.unpack('>H', bytes(sys.stdin.read(2), encoding='utf8'))
|
||||
pkt = sys.stdin.read(pkt_size)
|
||||
cmd = pkt.split(':')[0]
|
||||
if cmd == 'auth':
|
||||
username, server, password = pkt.split(':', 3)[1:]
|
||||
write(auth(username, server, password))
|
||||
elif cmd == 'isuser':
|
||||
username, server = pkt.split(':', 2)[1:]
|
||||
write(isuser(username, server))
|
||||
elif cmd == 'setpass':
|
||||
# u, s, p = pkt.split(':', 3)[1:]
|
||||
write(False)
|
||||
elif cmd == 'tryregister':
|
||||
# u, s, p = pkt.split(':', 3)[1:]
|
||||
write(False)
|
||||
elif cmd == 'removeuser':
|
||||
# u, s = pkt.split(':', 2)[1:]
|
||||
write(False)
|
||||
elif cmd == 'removeuser3':
|
||||
# u, s, p = pkt.split(':', 3)[1:]
|
||||
write(False)
|
||||
else:
|
||||
write(False)
|
||||
|
||||
|
||||
def write(result):
|
||||
if result:
|
||||
sys.stdout.write('\x00\x02\x00\x01')
|
||||
else:
|
||||
sys.stdout.write('\x00\x02\x00\x00')
|
||||
sys.stdout.flush()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.info("Starting pleroma ejabberd auth daemon...")
|
||||
while True:
|
||||
try:
|
||||
read()
|
||||
except Exception as e:
|
||||
logging.info(
|
||||
"Error while processing data from ejabberd %s", str(e))
|
||||
pass
|
||||
|
||||
```
|
34
docs/configuration/howto_mediaproxy.md
Normal file
34
docs/configuration/howto_mediaproxy.md
Normal file
@ -0,0 +1,34 @@
|
||||
# How to activate mediaproxy
|
||||
## Explanation
|
||||
|
||||
Without the `mediaproxy` function, Pleroma doesn't store any remote content like pictures, video etc. locally. So every time you open Pleroma, the content is loaded from the source server, from where the post is coming. This can result in slowly loading content or/and increased bandwidth usage on the source server.
|
||||
With the `mediaproxy` function you can use nginx to cache this content, so users can access it faster, because it's loaded from your server.
|
||||
|
||||
## Activate it
|
||||
|
||||
* Edit your nginx config and add the following location:
|
||||
```
|
||||
location /proxy {
|
||||
proxy_cache pleroma_media_cache;
|
||||
proxy_cache_lock on;
|
||||
proxy_pass http://localhost:4000;
|
||||
}
|
||||
```
|
||||
Also add the following on top of the configuration, outside of the `server` block:
|
||||
```
|
||||
proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
|
||||
```
|
||||
If you came here from one of the installation guides, take a look at the example configuration `/installation/pleroma.nginx`, where this part is already included.
|
||||
|
||||
* Append the following to your `prod.secret.exs` or `dev.secret.exs` (depends on which mode your instance is running):
|
||||
```
|
||||
config :pleroma, :media_proxy,
|
||||
enabled: true,
|
||||
proxy_opts: [
|
||||
redirect_on_failure: true
|
||||
]
|
||||
#base_url: "https://cache.pleroma.social"
|
||||
```
|
||||
If you want to use a subdomain to serve the files, uncomment `base_url`, change the url and add a comma after `true` in the previous line.
|
||||
|
||||
* Restart nginx and Pleroma
|
10
docs/configuration/howto_mongooseim.md
Normal file
10
docs/configuration/howto_mongooseim.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Configuring MongooseIM (XMPP Server) to use Pleroma for authentication
|
||||
|
||||
If you want to give your Pleroma users an XMPP (chat) account, you can configure [MongooseIM](https://github.com/esl/MongooseIM) to use your Pleroma server for user authentication, automatically giving every local user an XMPP account.
|
||||
|
||||
In general, you just have to follow the configuration described at [https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/](https://mongooseim.readthedocs.io/en/latest/authentication-backends/HTTP-authentication-module/) and do these changes to your mongooseim.cfg.
|
||||
|
||||
1. Set the auth_method to `{auth_method, http}`.
|
||||
2. Add the http auth pool like this: `{http, global, auth, [{workers, 50}], [{server, "https://yourpleromainstance.com"}]}`
|
||||
|
||||
Restart your MongooseIM server, your users should now be able to connect with their Pleroma credentials.
|
12
docs/configuration/howto_proxy.md
Normal file
12
docs/configuration/howto_proxy.md
Normal file
@ -0,0 +1,12 @@
|
||||
# How to configure upstream proxy for federation
|
||||
If you want to proxify all http requests (e.g. for TOR) that pleroma makes to an upstream proxy server, edit you config file (`dev.secret.exs` or `prod.secret.exs`) and add the following:
|
||||
|
||||
```
|
||||
config :pleroma, :http,
|
||||
proxy_url: "127.0.0.1:8123"
|
||||
```
|
||||
|
||||
The other way to do it, for example, with Tor you would most likely add something like this:
|
||||
```
|
||||
config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050}
|
||||
```
|
42
docs/configuration/howto_search_cjk.md
Normal file
42
docs/configuration/howto_search_cjk.md
Normal file
@ -0,0 +1,42 @@
|
||||
# How to enable text search for Chinese, Japanese and Korean
|
||||
|
||||
Pleroma's full text search feature is powered by PostgreSQL's native [text search](https://www.postgresql.org/docs/current/textsearch.html), it works well out of box for most of languages, but needs extra configurations for some asian languages like Chinese, Japanese and Korean (CJK).
|
||||
|
||||
|
||||
## Setup and test the new search config
|
||||
|
||||
In most cases, you would need an extension installed to support parsing CJK text. Here are a few extensions you may choose from, or you are more than welcome to share additional ones you found working for you with the rest of Pleroma community.
|
||||
|
||||
* [a generic n-gram parser](https://github.com/huangjimmy/pg_cjk_parser) supports Simplifed/Traditional Chinese, Japanese, and Korean
|
||||
* [a Korean parser](https://github.com/i0seph/textsearch_ko) based on mecab
|
||||
* [a Japanese parser](https://www.amris.co.jp/tsja/index.html) based on mecab
|
||||
* [zhparser](https://github.com/amutu/zhparser/) is a PostgreSQL extension base on the Simple Chinese Word Segmentation(SCWS)
|
||||
* [another Chinese parser](https://github.com/jaiminpan/pg_jieba) based on Jieba Chinese Word Segmentation
|
||||
|
||||
Once you have the new search config , make sure you test it with the `pleroma` user in PostgreSQL (change `YOUR.CONFIG` to your real configuration name)
|
||||
```
|
||||
SELECT ts_debug('YOUR.CONFIG', '安装和配置Nginx, ElixirとErlangをインストールします');
|
||||
```
|
||||
Check output of the query, and see if it matches your expectation.
|
||||
|
||||
|
||||
## Update text search config and index in database
|
||||
|
||||
=== "OTP"
|
||||
|
||||
```sh
|
||||
./bin/pleroma_ctl database set_text_search_config YOUR.CONFIG
|
||||
```
|
||||
|
||||
=== "From Source"
|
||||
|
||||
```sh
|
||||
mix pleroma.database set_text_search_config YOUR.CONFIG
|
||||
```
|
||||
|
||||
Note: index update may take a while, and it can be done while the instance is up and running, so you may restart db connection as soon as you see `Recreate index` in task output.
|
||||
|
||||
## Restart database connection
|
||||
Since some changes above will only apply with a new database connection, you will have to restart either Pleroma or PostgreSQL process, or use `pg_terminate_backend` SQL command without restarting either.
|
||||
|
||||
Now the search results of statuses should be much more friendly for your language of choice, the results for searching users and tags were not changed, as the default parsing/matching should work for most cases.
|
@ -0,0 +1,33 @@
|
||||
# How to set rich media cache ttl based on image ttl
|
||||
## Explanation
|
||||
|
||||
Richmedia are cached without the ttl but the rich media may have image which can expire, like aws signed url.
|
||||
In such cases the old image url (expired) is returned from the media cache.
|
||||
|
||||
So to avoid such situation we can define a module that will set ttl based on image.
|
||||
The module must adopt behaviour `Pleroma.Web.RichMedia.Parser.TTL`
|
||||
|
||||
### Example
|
||||
|
||||
```exs
|
||||
defmodule MyModule do
|
||||
@behaviour Pleroma.Web.RichMedia.Parser.TTL
|
||||
|
||||
@impl Pleroma.Web.RichMedia.Parser.TTL
|
||||
def ttl(data, url) do
|
||||
image_url = Map.get(data, :image)
|
||||
# do some parsing in the url and get the ttl of the image
|
||||
# return ttl is unix time
|
||||
parse_ttl_from_url(image_url)
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
And update the config
|
||||
|
||||
```exs
|
||||
config :pleroma, :rich_media,
|
||||
ttl_setters: [Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl, MyModule]
|
||||
```
|
||||
|
||||
> For reference there is a parser for AWS signed URL `Pleroma.Web.RichMedia.Parser.TTL.AwsSignedUrl`, it's enabled by default.
|
74
docs/configuration/howto_theming_your_instance.md
Normal file
74
docs/configuration/howto_theming_your_instance.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Theming your instance
|
||||
|
||||
To add a custom theme to your instance, you'll first need to get a custom theme, upload it to the server, make it available to the instance and eventually you can set it as default.
|
||||
|
||||
## Getting a custom theme
|
||||
|
||||
### Create your own theme
|
||||
|
||||
* You can create your own theme using the Pleroma FE by going to settings (gear on the top right) and choose the Theme tab. Here you have the options to create a personal theme.
|
||||
* To download your theme, you can do Save preset
|
||||
* If you want to upload a theme to customise it further, you can upload it using Load preset
|
||||
|
||||
This will only save the theme for you personally. To make it available to the whole instance, you'll need to upload it to the server.
|
||||
|
||||
### Get an existing theme
|
||||
|
||||
* You can download a theme from another instance by going to that instance, go to settings and make sure you have the theme selected that you want. Then you can do Save preset to download it.
|
||||
* You can also find and download custom themes at <https://plthemes.vulpes.one/>
|
||||
|
||||
## Adding the custom theme to the instance
|
||||
|
||||
### Upload the theme to the server
|
||||
|
||||
Themes can be found in the [static directory](static_dir.md). Create `STATIC-DIR/static/themes/` if needed and copy your theme there. Next you need to add an entry for your theme to `STATIC-DIR/static/styles.json`. If you use a from source installation, you'll first need to copy the file from `priv/static/static/styles.json`.
|
||||
|
||||
Example of `styles.json` where we add our own `my-awesome-theme.json`
|
||||
```json
|
||||
{
|
||||
"pleroma-dark": [ "Pleroma Dark", "#121a24", "#182230", "#b9b9ba", "#d8a070", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"pleroma-light": [ "Pleroma Light", "#f2f4f6", "#dbe0e8", "#304055", "#f86f0f", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"classic-dark": [ "Classic Dark", "#161c20", "#282e32", "#b9b9b9", "#baaa9c", "#d31014", "#0fa00f", "#0095ff", "#ffa500" ],
|
||||
"bird": [ "Bird", "#f8fafd", "#e6ecf0", "#14171a", "#0084b8", "#e0245e", "#17bf63", "#1b95e0", "#fab81e"],
|
||||
"ir-black": [ "Ir Black", "#000000", "#242422", "#b5b3aa", "#ff6c60", "#FF6C60", "#A8FF60", "#96CBFE", "#FFFFB6" ],
|
||||
"monokai": [ "Monokai", "#272822", "#383830", "#f8f8f2", "#f92672", "#F92672", "#a6e22e", "#66d9ef", "#f4bf75" ],
|
||||
|
||||
"redmond-xx": "/static/themes/redmond-xx.json",
|
||||
"redmond-xx-se": "/static/themes/redmond-xx-se.json",
|
||||
"redmond-xxi": "/static/themes/redmond-xxi.json",
|
||||
"breezy-dark": "/static/themes/breezy-dark.json",
|
||||
"breezy-light": "/static/themes/breezy-light.json",
|
||||
"mammal": "/static/themes/mammal.json",
|
||||
"my-awesome-theme": "/static/themes/my-awesome-theme.json"
|
||||
}
|
||||
```
|
||||
|
||||
Now you'll already be able to select the theme in Pleroma FE from the drop-down. You don't need to restart Pleroma because we only changed static served files. You may need to refresh the page in your browser. You'll notice however that the theme doesn't have a name, it's just an empty entry in the drop-down.
|
||||
|
||||
### Give the theme a name
|
||||
|
||||
When you open one of the themes that ship with Pleroma, you'll notice that the json has a `"name"` key. Add a key-value pair to your theme where the key name is `"name"` and the value the name you want to give your theme. After this you can refresh te page in your browser and the name should be visible in the drop-down.
|
||||
|
||||
Example of `my-awesome-theme.json` where we add the name "My Awesome Theme"
|
||||
```json
|
||||
{
|
||||
"_pleroma_theme_version": 2,
|
||||
"name": "My Awesome Theme",
|
||||
"theme": {}
|
||||
}
|
||||
```
|
||||
|
||||
### Set as default theme
|
||||
|
||||
Now we can set the new theme as default in the [Pleroma FE configuration](../../../frontend/CONFIGURATION).
|
||||
|
||||
Example of adding the new theme in the back-end config files
|
||||
```elixir
|
||||
config :pleroma, :frontend_configurations,
|
||||
pleroma_fe: %{
|
||||
theme: "my-awesome-theme"
|
||||
}
|
||||
```
|
||||
|
||||
If you added it in the back-end configuration file, you'll need to restart your instance for the changes to take effect. If you don't see the changes, it's probably because the browser has cached the previous theme. In that case you'll want to clear browser caches. Alternatively you can use a private/incognito window just to see the changes.
|
||||
|
196
docs/configuration/i2p.md
Normal file
196
docs/configuration/i2p.md
Normal file
@ -0,0 +1,196 @@
|
||||
# I2P Federation and Accessability
|
||||
|
||||
This guide is going to focus on the Pleroma federation aspect. The actual installation is neatly explained in the official documentation, and more likely to remain up-to-date.
|
||||
It might be added to this guide if there will be a need for that.
|
||||
|
||||
We're going to use I2PD for its lightweightness over the official client.
|
||||
Follow the documentation according to your distro: https://i2pd.readthedocs.io/en/latest/user-guide/install/#installing
|
||||
|
||||
How to run it: https://i2pd.readthedocs.io/en/latest/user-guide/run/
|
||||
|
||||
## I2P Federation
|
||||
|
||||
There are 2 ways to go about this.
|
||||
One using the config, and one using external software (fedproxy). The external software works better so far.
|
||||
|
||||
### Using the Config
|
||||
|
||||
**Warning:** So far, everytime I followed this way of federating using I2P, the rest of my federation stopped working. I'm leaving this here in case it will help with making it work.
|
||||
|
||||
Assuming you're running in prod, cd to your Pleroma folder and append the following to `config/prod.secret.exs`:
|
||||
```
|
||||
config :pleroma, :http, proxy_url: {:socks5, :localhost, 4447}
|
||||
```
|
||||
And then run the following:
|
||||
```
|
||||
su pleroma
|
||||
MIX_ENV=prod mix deps.get
|
||||
MIX_ENV=prod mix ecto.migrate
|
||||
exit
|
||||
```
|
||||
You can restart I2PD here and finish if you don't wish to make your instance viewable or accessible over I2P.
|
||||
```
|
||||
systemctl stop i2pd.service --no-block
|
||||
systemctl start i2pd.service
|
||||
```
|
||||
*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
|
||||
|
||||
You can change the socks proxy port in `/etc/i2pd/i2pd.conf`.
|
||||
|
||||
### Using Fedproxy
|
||||
|
||||
Fedproxy passes through clearnet requests direct to where they are going. It doesn't force anything over Tor.
|
||||
|
||||
To use [fedproxy](https://github.com/majestrate/fedproxy) you'll need to install Golang.
|
||||
```
|
||||
apt install golang
|
||||
```
|
||||
Use a different user than pleroma or root. Run the following to add the Gopath to your ~/.bashrc.
|
||||
```
|
||||
echo "export GOPATH=/home/ren/.go" >> ~/.bashrc
|
||||
```
|
||||
Restart that bash session (you can exit and log back in).
|
||||
Run the following to get fedproxy.
|
||||
```
|
||||
go get -u github.com/majestrate/fedproxy$
|
||||
cp $(GOPATH)/bin/fedproxy /usr/local/bin/fedproxy
|
||||
```
|
||||
And then the following to start it for I2P only.
|
||||
```
|
||||
fedproxy 127.0.0.1:2000 127.0.0.1:4447
|
||||
```
|
||||
If you want to also use it for Tor, add `127.0.0.1:9050` to that command.
|
||||
You'll also need to modify your Pleroma config.
|
||||
|
||||
Assuming you're running in prod, cd to your Pleroma folder and append the following to `config/prod.secret.exs`:
|
||||
```
|
||||
config :pleroma, :http, proxy_url: {:socks5, :localhost, 2000}
|
||||
```
|
||||
And then run the following:
|
||||
```
|
||||
su pleroma
|
||||
MIX_ENV=prod mix deps.get
|
||||
MIX_ENV=prod mix ecto.migrate
|
||||
exit
|
||||
```
|
||||
You can restart I2PD here and finish if you don't wish to make your instance viewable or accessible over I2P.
|
||||
|
||||
```
|
||||
systemctl stop i2pd.service --no-block
|
||||
systemctl start i2pd.service
|
||||
```
|
||||
*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
|
||||
|
||||
You can change the socks proxy port in `/etc/i2pd/i2pd.conf`.
|
||||
|
||||
## I2P Instance Access
|
||||
|
||||
Make your instance accessible using I2P.
|
||||
|
||||
Add the following to your I2PD config `/etc/i2pd/tunnels.conf`:
|
||||
```
|
||||
[pleroma]
|
||||
type = http
|
||||
host = 127.0.0.1
|
||||
port = 14447
|
||||
keys = pleroma.dat
|
||||
```
|
||||
Restart I2PD:
|
||||
```
|
||||
systemctl stop i2pd.service --no-block
|
||||
systemctl start i2pd.service
|
||||
```
|
||||
*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
|
||||
|
||||
Now you'll have to find your address.
|
||||
To do that you can download and use I2PD tools.[^1]
|
||||
Or you'll need to access your web-console on localhost:7070.
|
||||
If you don't have a GUI, you'll have to SSH tunnel into it like this:
|
||||
`ssh -L 7070:127.0.0.1:7070 user@ip -p port`.
|
||||
Now you can access it at localhost:7070.
|
||||
Go to I2P tunnels page. Look for Server tunnels and you will see an address that ends with `.b32.i2p` next to "pleroma".
|
||||
This is your site's address.
|
||||
|
||||
### I2P-only Instance
|
||||
|
||||
If creating an I2P-only instance, open `config/prod.secret.exs` and under "config :pleroma, Pleroma.Web.Endpoint," edit "https" and "port: 443" to the following:
|
||||
```
|
||||
url: [host: "i2paddress", scheme: "http", port: 80],
|
||||
```
|
||||
In addition to that, replace the existing nginx config's contents with the example below.
|
||||
|
||||
### Existing Instance (Clearnet Instance)
|
||||
|
||||
If not an I2P-only instance, add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
|
||||
|
||||
And for both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
|
||||
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
|
||||
```
|
||||
config :pleroma, :http_security,
|
||||
enabled: false
|
||||
```
|
||||
|
||||
Use this as the Nginx config:
|
||||
```
|
||||
proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
|
||||
# The above already exists in a clearnet instance's config.
|
||||
# If not, add it.
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:14447;
|
||||
server_name youri2paddress;
|
||||
|
||||
# Comment to enable logs
|
||||
access_log /dev/null;
|
||||
error_log /dev/null;
|
||||
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
|
||||
|
||||
client_max_body_size 16m;
|
||||
|
||||
location / {
|
||||
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Permitted-Cross-Domain-Policies none;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header Referrer-Policy same-origin;
|
||||
add_header X-Download-Options noopen;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
proxy_pass http://localhost:4000;
|
||||
|
||||
client_max_body_size 16m;
|
||||
}
|
||||
|
||||
location /proxy {
|
||||
proxy_cache pleroma_media_cache;
|
||||
proxy_cache_lock on;
|
||||
proxy_ignore_client_abort on;
|
||||
proxy_pass http://localhost:4000;
|
||||
}
|
||||
}
|
||||
```
|
||||
reload Nginx:
|
||||
```
|
||||
systemctl stop i2pd.service --no-block
|
||||
systemctl start i2pd.service
|
||||
```
|
||||
*Notice:* The stop command initiates a graceful shutdown process, i2pd stops after finishing to route transit tunnels (maximum 10 minutes).
|
||||
|
||||
You should now be able to both access your instance using I2P and federate with other I2P instances!
|
||||
|
||||
[^1]: [I2PD tools](https://github.com/purplei2p/i2pd-tools) to print information about a router info file or an I2P private key, generate an I2P private key, and generate vanity addresses.
|
||||
|
||||
### Possible Issues
|
||||
|
||||
Will be added when encountered.
|
158
docs/configuration/mrf.md
Normal file
158
docs/configuration/mrf.md
Normal file
@ -0,0 +1,158 @@
|
||||
# Message Rewrite Facility
|
||||
|
||||
The Message Rewrite Facility (MRF) is a subsystem that is implemented as a series of hooks that allows the administrator to rewrite or discard messages.
|
||||
|
||||
Possible uses include:
|
||||
|
||||
* marking incoming messages with media from a given account or instance as sensitive
|
||||
* rejecting messages from a specific instance
|
||||
* rejecting reports (flags) from a specific instance
|
||||
* removing/unlisting messages from the public timelines
|
||||
* removing media from messages
|
||||
* sending only public messages to a specific instance
|
||||
|
||||
The MRF provides user-configurable policies. The default policy is `NoOpPolicy`, which disables the MRF functionality. Pleroma also includes an easy to use policy called `SimplePolicy` which maps messages matching certain pre-defined criterion to actions built into the policy module.
|
||||
|
||||
It is possible to use multiple, active MRF policies at the same time.
|
||||
|
||||
## Quarantine Instances
|
||||
|
||||
You have the ability to prevent from private / followers-only messages from federating with specific instances. Which means they will only get the public or unlisted messages from your instance.
|
||||
|
||||
If, for example, you're using `MIX_ENV=prod` aka using production mode, you would open your configuration file located in `config/prod.secret.exs` and edit or add the option under your `:instance` config object. Then you would specify the instance within quotes.
|
||||
|
||||
```elixir
|
||||
config :pleroma, :instance,
|
||||
[...]
|
||||
quarantined_instances: ["instance.example", "other.example"]
|
||||
```
|
||||
|
||||
## Using `SimplePolicy`
|
||||
|
||||
`SimplePolicy` is capable of handling most common admin tasks.
|
||||
|
||||
To use `SimplePolicy`, you must enable it. Do so by adding the following to your `:instance` config object, so that it looks like this:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :mrf,
|
||||
[...]
|
||||
policies: Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||
```
|
||||
|
||||
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||
|
||||
* `reject`: Servers in this group will have their messages rejected.
|
||||
* `accept`: If not empty, only messages from these instances will be accepted (whitelist federation).
|
||||
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
||||
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
||||
* `avatar_removal`: Avatars from these servers will be stripped from incoming messages.
|
||||
* `banner_removal`: Banner images from these servers will be stripped from incoming messages.
|
||||
* `report_removal`: Servers in this group will have their reports (flags) rejected.
|
||||
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
||||
* `reject_deletes`: Deletion requests will be rejected from these servers.
|
||||
|
||||
Servers should be configured as lists.
|
||||
|
||||
### Example
|
||||
|
||||
This example will enable `SimplePolicy`, block media from `illegalporn.biz`, mark media as NSFW from `porn.biz` and `porn.business`, reject messages from `spam.com`, remove messages from `spam.university` from the federated timeline and block reports (flags) from `whiny.whiner`. We also give a reason why the moderation was done:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :mrf,
|
||||
policies: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
||||
|
||||
config :pleroma, :mrf_simple,
|
||||
media_removal: [{"illegalporn.biz", "Media can contain illegal contant"}],
|
||||
media_nsfw: [{"porn.biz", "unmarked nsfw media"}, {"porn.business", "A lot of unmarked nsfw media"}],
|
||||
reject: [{"spam.com", "They keep spamming our users"}],
|
||||
federated_timeline_removal: [{"spam.university", "Annoying low-quality posts who otherwise fill up TWKN"}],
|
||||
report_removal: [{"whiny.whiner", "Keep spamming us with irrelevant reports"}]
|
||||
```
|
||||
|
||||
### Use with Care
|
||||
|
||||
The effects of MRF policies can be very drastic. It is important to use this functionality carefully. Always try to talk to an admin before writing an MRF policy concerning their instance.
|
||||
|
||||
## Writing your own MRF Policy
|
||||
|
||||
As discussed above, the MRF system is a modular system that supports pluggable policies. This means that an admin may write a custom MRF policy in Elixir or any other language that runs on the Erlang VM, by specifying the module name in the `policies` config setting.
|
||||
|
||||
For example, here is a sample policy module which rewrites all messages to "new message content":
|
||||
|
||||
```elixir
|
||||
defmodule Pleroma.Web.ActivityPub.MRF.RewritePolicy do
|
||||
@moduledoc "MRF policy which rewrites all Notes to have 'new message content'."
|
||||
@behaviour Pleroma.Web.ActivityPub.MRF.Policy
|
||||
|
||||
# Catch messages which contain Note objects with actual data to filter.
|
||||
# Capture the object as `object`, the message content as `content` and the
|
||||
# message itself as `message`.
|
||||
@impl true
|
||||
def filter(
|
||||
%{"type" => "Create", "object" => %{"type" => "Note", "content" => content} = object} =
|
||||
message
|
||||
)
|
||||
when is_binary(content) do
|
||||
# Subject / CW is stored as summary instead of `name` like other AS2 objects
|
||||
# because of Mastodon doing it that way.
|
||||
summary = object["summary"]
|
||||
|
||||
# Message edits go here.
|
||||
content = "new message content"
|
||||
|
||||
# Assemble the mutated object.
|
||||
object =
|
||||
object
|
||||
|> Map.put("content", content)
|
||||
|> Map.put("summary", summary)
|
||||
|
||||
# Assemble the mutated message.
|
||||
message = Map.put(message, "object", object)
|
||||
{:ok, message}
|
||||
end
|
||||
|
||||
# Let all other messages through without modifying them.
|
||||
@impl true
|
||||
def filter(message), do: {:ok, message}
|
||||
|
||||
@impl true
|
||||
def describe do
|
||||
{:ok, %{mrf_sample: %{content: "new message content"}}}
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
If you save this file as `lib/pleroma/web/activity_pub/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
|
||||
|
||||
```elixir
|
||||
config :pleroma, :mrf,
|
||||
policies: [
|
||||
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
|
||||
Pleroma.Web.ActivityPub.MRF.RewritePolicy
|
||||
]
|
||||
```
|
||||
|
||||
Please note that the Pleroma developers consider custom MRF policy modules to fall under the purview of the AGPL. As such, you are obligated to release the sources to your custom MRF policy modules upon request.
|
||||
|
||||
### MRF policies descriptions
|
||||
|
||||
If MRF policy depends on config, it can be added into MRF tab to adminFE by adding `config_description/0` method, which returns a map with a specific structure. See existing MRF's like `lib/pleroma/web/activity_pub/mrf/activity_expiration_policy.ex` for examples. Note that more complex inputs, like tuples or maps, may need extra changes in the adminFE and just adding it to `config_description/0` may not be enough to get these inputs working from the adminFE.
|
||||
|
||||
Example:
|
||||
|
||||
```elixir
|
||||
%{
|
||||
key: :mrf_activity_expiration,
|
||||
related_policy: "Pleroma.Web.ActivityPub.MRF.ActivityExpirationPolicy",
|
||||
label: "MRF Activity Expiration Policy",
|
||||
description: "Adds automatic expiration to all local activities",
|
||||
children: [
|
||||
%{
|
||||
key: :days,
|
||||
type: :integer,
|
||||
description: "Default global expiration time for all local activities (in days)",
|
||||
suggestions: [90, 365]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
159
docs/configuration/onion_federation.md
Normal file
159
docs/configuration/onion_federation.md
Normal file
@ -0,0 +1,159 @@
|
||||
# Easy Onion Federation (Tor)
|
||||
Tor can free people from the necessity of a domain, in addition to helping protect their privacy. As Pleroma's goal is to empower the people and let as many as possible host an instance with as little resources as possible, the ability to host an instance with a small, cheap computer like a RaspberryPi along with Tor, would be a great way to achieve that.
|
||||
In addition, federating with such instances will also help furthering that goal.
|
||||
|
||||
This is a guide to show you how it can be easily done.
|
||||
|
||||
This guide assumes you already got Pleroma working, and that it's running on the default port 4000.
|
||||
Currently only has an Nginx example.
|
||||
|
||||
To install Tor on Debian / Ubuntu:
|
||||
```
|
||||
apt -yq install tor
|
||||
```
|
||||
If using an old server version (older than Debian Stretch or Ubuntu 18.04), install from backports or PPA.
|
||||
I recommend using a newer server version instead.
|
||||
|
||||
To have the newest, V3 onion addresses (which I recommend) in Debian, install Tor from backports.
|
||||
If you do not have backports, uncomment the stretch-backports links at the end of `/etc/apt/sources.list`.
|
||||
Then install:
|
||||
```
|
||||
apt update
|
||||
apt -t stretch-backports -yq install tor
|
||||
```
|
||||
**WARNING:** Onion instances not using a Tor version supporting V3 addresses will not be able to federate with you.
|
||||
|
||||
Create the hidden service for your Pleroma instance in `/etc/tor/torrc`:
|
||||
```
|
||||
HiddenServiceDir /var/lib/tor/pleroma_hidden_service/
|
||||
HiddenServicePort 80 127.0.0.1:8099
|
||||
HiddenServiceVersion 3 # Remove if Tor version is below 0.3 ( tor --version )
|
||||
```
|
||||
Restart Tor to generate an adress:
|
||||
```
|
||||
systemctl restart tor@default.service
|
||||
```
|
||||
Get the address:
|
||||
```
|
||||
cat /var/lib/tor/pleroma_hidden_service/hostname
|
||||
```
|
||||
|
||||
# Federation
|
||||
|
||||
Next, edit your Pleroma config.
|
||||
If running in prod, cd to your Pleroma directory, edit `config/prod.secret.exs`
|
||||
and append this line:
|
||||
```
|
||||
config :pleroma, :http, proxy_url: {:socks5, :localhost, 9050}
|
||||
```
|
||||
In your Pleroma directory, assuming you're running prod,
|
||||
run the following:
|
||||
```
|
||||
su pleroma
|
||||
MIX_ENV=prod mix deps.get
|
||||
MIX_ENV=prod mix ecto.migrate
|
||||
exit
|
||||
```
|
||||
restart Pleroma (if using systemd):
|
||||
```
|
||||
systemctl restart pleroma
|
||||
```
|
||||
|
||||
# Tor Instance Access
|
||||
|
||||
Make your instance accessible using Tor.
|
||||
|
||||
## Tor-only Instance
|
||||
If creating a Tor-only instance, open `config/prod.secret.exs` and under "config :pleroma, Pleroma.Web.Endpoint," edit "https" and "port: 443" to the following:
|
||||
```
|
||||
url: [host: "onionaddress", scheme: "http", port: 80],
|
||||
```
|
||||
In addition to that, replace the existing nginx config's contents with the example below.
|
||||
|
||||
## Existing Instance (Clearnet Instance)
|
||||
If not a Tor-only instance,
|
||||
add the nginx config below to your existing config at `/etc/nginx/sites-enabled/pleroma.nginx`.
|
||||
|
||||
---
|
||||
For both cases, disable CSP in Pleroma's config (STS is disabled by default) so you can define those yourself separately from the clearnet (if your instance is also on the clearnet).
|
||||
Copy the following into the `config/prod.secret.exs` in your Pleroma folder (/home/pleroma/pleroma/):
|
||||
```
|
||||
config :pleroma, :http_security,
|
||||
enabled: false
|
||||
```
|
||||
|
||||
Use this as the Nginx config:
|
||||
```
|
||||
proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cache:10m max_size=10g inactive=720m use_temp_path=off;
|
||||
# The above already exists in a clearnet instance's config.
|
||||
# If not, add it.
|
||||
|
||||
server {
|
||||
listen 127.0.0.1:8099;
|
||||
server_name youronionaddress;
|
||||
|
||||
# Comment to enable logs
|
||||
access_log /dev/null;
|
||||
error_log /dev/null;
|
||||
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_buffers 16 8k;
|
||||
gzip_http_version 1.1;
|
||||
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript application/activity+json application/atom+xml;
|
||||
|
||||
client_max_body_size 16m;
|
||||
|
||||
location / {
|
||||
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
add_header X-Permitted-Cross-Domain-Policies none;
|
||||
add_header X-Frame-Options DENY;
|
||||
add_header X-Content-Type-Options nosniff;
|
||||
add_header Referrer-Policy same-origin;
|
||||
add_header X-Download-Options noopen;
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
proxy_set_header Host $http_host;
|
||||
|
||||
proxy_pass http://localhost:4000;
|
||||
|
||||
client_max_body_size 16m;
|
||||
}
|
||||
|
||||
location /proxy {
|
||||
proxy_cache pleroma_media_cache;
|
||||
proxy_cache_lock on;
|
||||
proxy_ignore_client_abort on;
|
||||
proxy_pass http://localhost:4000;
|
||||
}
|
||||
}
|
||||
```
|
||||
reload Nginx:
|
||||
```
|
||||
systemctl reload nginx
|
||||
```
|
||||
|
||||
You should now be able to both access your instance using Tor and federate with other Tor instances!
|
||||
|
||||
---
|
||||
|
||||
### Possible Issues
|
||||
|
||||
* In Debian, make sure your hidden service folder `/var/lib/tor/pleroma_hidden_service/` and its contents, has debian-tor as both owner and group by using
|
||||
```
|
||||
ls -la /var/lib/tor/
|
||||
```
|
||||
If it's not, run:
|
||||
```
|
||||
chown -R debian-tor:debian-tor /var/lib/tor/pleroma_hidden_service/
|
||||
```
|
||||
* Make sure *only* the owner has *only* read and write permissions.
|
||||
If not, run:
|
||||
```
|
||||
chmod -R 600 /var/lib/tor/pleroma_hidden_service/
|
||||
```
|
||||
* If you have trouble logging in to the Mastodon Frontend when using Tor, use the Tor Browser Bundle.
|
66
docs/configuration/optimizing_beam.md
Normal file
66
docs/configuration/optimizing_beam.md
Normal file
@ -0,0 +1,66 @@
|
||||
# Optimizing the BEAM
|
||||
|
||||
Pleroma is built upon the Erlang/OTP VM known as BEAM. The BEAM VM is highly optimized for latency, but this has drawbacks in environments without dedicated hardware. One of the tricks used by the BEAM VM is [busy waiting](https://en.wikipedia.org/wiki/Busy_waiting). This allows the application to pretend to be busy working so the OS kernel does not pause the application process and switch to another process waiting for the CPU to execute its workload. It does this by spinning for a period of time which inflates the apparent CPU usage of the application so it is immediately ready to execute another task. This can be observed with utilities like **top(1)** which will show consistently high CPU usage for the process. Switching between procesess is a rather expensive operation and also clears CPU caches further affecting latency and performance. The goal of busy waiting is to avoid this penalty.
|
||||
|
||||
This strategy is very successful in making a performant and responsive application, but is not desirable on Virtual Machines or hardware with few CPU cores. Pleroma instances are often deployed on the same server as the required PostgreSQL database which can lead to situations where the Pleroma application is holding the CPU in a busy-wait loop and as a result the database cannot process requests in a timely manner. The fewer CPUs available, the more this problem is exacerbated. The latency is further amplified by the OS being installed on a Virtual Machine as the Hypervisor uses CPU time-slicing to pause the entire OS and switch between other tasks.
|
||||
|
||||
More adventurous admins can be creative with CPU affinity (e.g., *taskset* for Linux and *cpuset* on FreeBSD) to pin processes to specific CPUs and eliminate much of this contention. The most important advice is to run as few processes as possible on your server to achieve the best performance. Even idle background processes can occasionally create [software interrupts](https://en.wikipedia.org/wiki/Interrupt) and take attention away from the executing process creating latency spikes and invalidation of the CPU caches as they must be cleared when switching between processes for security.
|
||||
|
||||
Please only change these settings if you are experiencing issues or really know what you are doing. In general, there's no need to change these settings.
|
||||
|
||||
## VPS Provider Recommendations
|
||||
|
||||
### Good
|
||||
|
||||
* Hetzner Cloud
|
||||
|
||||
### Bad
|
||||
|
||||
* AWS (known to use burst scheduling)
|
||||
|
||||
|
||||
## Example configurations
|
||||
|
||||
Tuning the BEAM requires you provide a config file normally called [vm.args](http://erlang.org/doc/man/erl.html#emulator-flags). If you are using systemd to manage the service you can modify the unit file as such:
|
||||
|
||||
`ExecStart=/usr/bin/elixir --erl '-args_file /opt/pleroma/config/vm.args' -S /usr/bin/mix phx.server`
|
||||
|
||||
Check your OS documentation to adopt a similar strategy on other platforms.
|
||||
|
||||
### Virtual Machine and/or few CPU cores
|
||||
|
||||
Disable the busy-waiting. This should generally only be done if you're on a platform that does burst scheduling, like AWS.
|
||||
|
||||
**vm.args:**
|
||||
|
||||
```
|
||||
+sbwt none
|
||||
+sbwtdcpu none
|
||||
+sbwtdio none
|
||||
```
|
||||
|
||||
### Dedicated Hardware
|
||||
|
||||
Enable more busy waiting, increase the internal maximum limit of BEAM processes and ports. You can use this if you run on dedicated hardware, but it is not necessary.
|
||||
|
||||
**vm.args:**
|
||||
|
||||
```
|
||||
+P 16777216
|
||||
+Q 16777216
|
||||
+K true
|
||||
+A 128
|
||||
+sbt db
|
||||
+sbwt very_long
|
||||
+swt very_low
|
||||
+sub true
|
||||
+Mulmbcs 32767
|
||||
+Mumbcgs 1
|
||||
+Musmbcs 2047
|
||||
```
|
||||
|
||||
## Additional Reading
|
||||
|
||||
* [WhatsApp: Scaling to Millions of Simultaneous Connections](https://www.erlang-factory.com/upload/presentations/558/efsf2012-whatsapp-scaling.pdf)
|
||||
* [Preemptive Scheduling and Spinlocks](https://www.uio.no/studier/emner/matnat/ifi/nedlagte-emner/INF3150/h03/annet/slides/preemptive.pdf)
|
||||
* [The Curious Case of BEAM CPU Usage](https://stressgrid.com/blog/beam_cpu_usage/)
|
48
docs/configuration/postgresql.md
Normal file
48
docs/configuration/postgresql.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Optimizing PostgreSQL performance
|
||||
|
||||
Pleroma performance is largely dependent on performance of the underlying database. Better performance can be achieved by adjusting a few settings.
|
||||
|
||||
## PGTune
|
||||
|
||||
[PgTune](https://pgtune.leopard.in.ua) can be used to get recommended settings. Be sure to set "Number of Connections" to 20, otherwise it might produce settings hurtful to database performance. It is also recommended to not use "Network Storage" option.
|
||||
|
||||
## Disable generic query plans
|
||||
|
||||
When PostgreSQL receives a query, it decides on a strategy for searching the requested data, this is called a query plan. The query planner has two modes: generic and custom. Generic makes a plan for all queries of the same shape, ignoring the parameters, which is then cached and reused. Custom, on the contrary, generates a unique query plan based on query parameters.
|
||||
|
||||
By default PostgreSQL has an algorithm to decide which mode is more efficient for particular query, however this algorithm has been observed to be wrong on some of the queries Pleroma sends, leading to serious performance loss. Therefore, it is recommended to disable generic mode.
|
||||
|
||||
|
||||
Pleroma already avoids generic query plans by default, however the method it uses is not the most efficient because it needs to be compatible with all supported PostgreSQL versions. For PostgreSQL 12 and higher additional performance can be gained by adding the following to Pleroma configuration:
|
||||
```elixir
|
||||
config :pleroma, Pleroma.Repo,
|
||||
prepare: :named,
|
||||
parameters: [
|
||||
plan_cache_mode: "force_custom_plan"
|
||||
]
|
||||
```
|
||||
|
||||
A more detailed explaination of the issue can be found at <https://blog.soykaf.com/post/postgresql-elixir-troubles/>.
|
||||
|
||||
## Example configurations
|
||||
|
||||
Here are some configuration suggestions for PostgreSQL 10+.
|
||||
|
||||
### 1GB RAM, 1 CPU
|
||||
```
|
||||
shared_buffers = 256MB
|
||||
effective_cache_size = 768MB
|
||||
maintenance_work_mem = 64MB
|
||||
work_mem = 13107kB
|
||||
```
|
||||
|
||||
### 2GB RAM, 2 CPU
|
||||
```
|
||||
shared_buffers = 512MB
|
||||
effective_cache_size = 1536MB
|
||||
maintenance_work_mem = 128MB
|
||||
work_mem = 26214kB
|
||||
max_worker_processes = 2
|
||||
max_parallel_workers_per_gather = 1
|
||||
max_parallel_workers = 2
|
||||
```
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user