Merge branch 'develop' into feature/database-compaction
This commit is contained in:
commit
627e5a0a49
19
.credo.exs
19
.credo.exs
|
@ -19,7 +19,7 @@
|
||||||
#
|
#
|
||||||
# You can give explicit globs or simply directories.
|
# You can give explicit globs or simply directories.
|
||||||
# In the latter case `**/*.{ex,exs}` will be used.
|
# In the latter case `**/*.{ex,exs}` will be used.
|
||||||
included: ["lib/", "src/", "web/", "apps/"],
|
included: ["lib/", "src/", "web/", "apps/", "test/"],
|
||||||
excluded: [~r"/_build/", ~r"/deps/"]
|
excluded: [~r"/_build/", ~r"/deps/"]
|
||||||
},
|
},
|
||||||
#
|
#
|
||||||
|
@ -57,7 +57,7 @@
|
||||||
|
|
||||||
# For some checks, like AliasUsage, you can only customize the priority
|
# For some checks, like AliasUsage, you can only customize the priority
|
||||||
# Priority values are: `low, normal, high, higher`
|
# Priority values are: `low, normal, high, higher`
|
||||||
{Credo.Check.Design.AliasUsage, priority: :low},
|
{Credo.Check.Design.AliasUsage, priority: :low, if_called_more_often_than: 3},
|
||||||
|
|
||||||
# For others you can set parameters
|
# For others you can set parameters
|
||||||
|
|
||||||
|
@ -69,8 +69,8 @@
|
||||||
# You can also customize the exit_status of each check.
|
# You can also customize the exit_status of each check.
|
||||||
# If you don't want TODO comments to cause `mix credo` to fail, just
|
# If you don't want TODO comments to cause `mix credo` to fail, just
|
||||||
# set this value to 0 (zero).
|
# set this value to 0 (zero).
|
||||||
{Credo.Check.Design.TagTODO, exit_status: 2},
|
{Credo.Check.Design.TagTODO, exit_status: 0},
|
||||||
{Credo.Check.Design.TagFIXME},
|
{Credo.Check.Design.TagFIXME, exit_status: 0},
|
||||||
|
|
||||||
{Credo.Check.Readability.FunctionNames},
|
{Credo.Check.Readability.FunctionNames},
|
||||||
{Credo.Check.Readability.LargeNumbers},
|
{Credo.Check.Readability.LargeNumbers},
|
||||||
|
@ -81,7 +81,9 @@
|
||||||
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
|
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
|
||||||
{Credo.Check.Readability.ParenthesesInCondition},
|
{Credo.Check.Readability.ParenthesesInCondition},
|
||||||
{Credo.Check.Readability.PredicateFunctionNames},
|
{Credo.Check.Readability.PredicateFunctionNames},
|
||||||
{Credo.Check.Readability.PreferImplicitTry},
|
# 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.RedundantBlankLines},
|
{Credo.Check.Readability.RedundantBlankLines},
|
||||||
{Credo.Check.Readability.StringSigils},
|
{Credo.Check.Readability.StringSigils},
|
||||||
{Credo.Check.Readability.TrailingBlankLine},
|
{Credo.Check.Readability.TrailingBlankLine},
|
||||||
|
@ -104,7 +106,8 @@
|
||||||
{Credo.Check.Warning.BoolOperationOnSameValues},
|
{Credo.Check.Warning.BoolOperationOnSameValues},
|
||||||
{Credo.Check.Warning.IExPry},
|
{Credo.Check.Warning.IExPry},
|
||||||
{Credo.Check.Warning.IoInspect},
|
{Credo.Check.Warning.IoInspect},
|
||||||
{Credo.Check.Warning.LazyLogging},
|
# Got too much of them, not sure if relevant
|
||||||
|
{Credo.Check.Warning.LazyLogging, false},
|
||||||
{Credo.Check.Warning.OperationOnSameValues},
|
{Credo.Check.Warning.OperationOnSameValues},
|
||||||
{Credo.Check.Warning.OperationWithConstantResult},
|
{Credo.Check.Warning.OperationWithConstantResult},
|
||||||
{Credo.Check.Warning.UnusedEnumOperation},
|
{Credo.Check.Warning.UnusedEnumOperation},
|
||||||
|
@ -125,10 +128,6 @@
|
||||||
|
|
||||||
# Deprecated checks (these will be deleted after a grace period)
|
# Deprecated checks (these will be deleted after a grace period)
|
||||||
{Credo.Check.Readability.Specs, false},
|
{Credo.Check.Readability.Specs, false},
|
||||||
{Credo.Check.Warning.NameRedeclarationByAssignment, false},
|
|
||||||
{Credo.Check.Warning.NameRedeclarationByCase, false},
|
|
||||||
{Credo.Check.Warning.NameRedeclarationByDef, false},
|
|
||||||
{Credo.Check.Warning.NameRedeclarationByFn, false},
|
|
||||||
|
|
||||||
# Custom checks can be created using `mix credo.gen.check`.
|
# Custom checks can be created using `mix credo.gen.check`.
|
||||||
#
|
#
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
/.elixir_ls
|
/.elixir_ls
|
||||||
/test/fixtures/test_tmp.txt
|
/test/fixtures/test_tmp.txt
|
||||||
/test/fixtures/image_tmp.jpg
|
/test/fixtures/image_tmp.jpg
|
||||||
|
/test/tmp/
|
||||||
/doc
|
/doc
|
||||||
|
/instance
|
||||||
|
|
||||||
# Prevent committing custom emojis
|
# Prevent committing custom emojis
|
||||||
/priv/static/emoji/custom/*
|
/priv/static/emoji/custom/*
|
||||||
|
@ -23,6 +25,7 @@ erl_crash.dump
|
||||||
# secrets files as long as you replace their contents by environment
|
# secrets files as long as you replace their contents by environment
|
||||||
# variables.
|
# variables.
|
||||||
/config/*.secret.exs
|
/config/*.secret.exs
|
||||||
|
/config/generated_config.exs
|
||||||
|
|
||||||
# Database setup file, some may forget to delete it
|
# Database setup file, some may forget to delete it
|
||||||
/config/setup_db.psql
|
/config/setup_db.psql
|
||||||
|
@ -31,4 +34,7 @@ erl_crash.dump
|
||||||
.env
|
.env
|
||||||
|
|
||||||
# Editor config
|
# Editor config
|
||||||
/.vscode
|
/.vscode/
|
||||||
|
|
||||||
|
# Prevent committing docs files
|
||||||
|
/priv/static/doc/*
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
image: elixir:1.7.2
|
image: elixir:1.8.1
|
||||||
|
|
||||||
services:
|
|
||||||
- postgres:9.6.2
|
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
POSTGRES_DB: pleroma_test
|
POSTGRES_DB: pleroma_test
|
||||||
POSTGRES_USER: postgres
|
POSTGRES_USER: postgres
|
||||||
POSTGRES_PASSWORD: postgres
|
POSTGRES_PASSWORD: postgres
|
||||||
DB_HOST: postgres
|
DB_HOST: postgres
|
||||||
|
MIX_ENV: test
|
||||||
|
|
||||||
cache:
|
cache:
|
||||||
key: ${CI_COMMIT_REF_SLUG}
|
key: ${CI_COMMIT_REF_SLUG}
|
||||||
|
@ -15,22 +13,68 @@ cache:
|
||||||
- deps
|
- deps
|
||||||
- _build
|
- _build
|
||||||
stages:
|
stages:
|
||||||
- lint
|
- build
|
||||||
- test
|
- test
|
||||||
|
- deploy
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
- mix local.hex --force
|
- mix local.hex --force
|
||||||
- mix local.rebar --force
|
- mix local.rebar --force
|
||||||
- mix deps.get
|
|
||||||
- MIX_ENV=test mix ecto.create
|
|
||||||
- MIX_ENV=test mix ecto.migrate
|
|
||||||
|
|
||||||
lint:
|
build:
|
||||||
stage: lint
|
stage: build
|
||||||
script:
|
script:
|
||||||
- MIX_ENV=test mix format --check-formatted
|
- mix deps.get
|
||||||
|
- mix compile --force
|
||||||
|
|
||||||
|
docs-build:
|
||||||
|
stage: build
|
||||||
|
only:
|
||||||
|
- master@pleroma/pleroma
|
||||||
|
- develop@pleroma/pleroma
|
||||||
|
variables:
|
||||||
|
MIX_ENV: dev
|
||||||
|
script:
|
||||||
|
- mix deps.get
|
||||||
|
- mix compile
|
||||||
|
- mix docs
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- priv/static/doc
|
||||||
|
|
||||||
unit-testing:
|
unit-testing:
|
||||||
stage: test
|
stage: test
|
||||||
|
services:
|
||||||
|
- name: postgres:9.6.2
|
||||||
|
command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"]
|
||||||
script:
|
script:
|
||||||
- MIX_ENV=test mix test --trace
|
- mix ecto.create
|
||||||
|
- mix ecto.migrate
|
||||||
|
- mix test --trace --preload-modules
|
||||||
|
|
||||||
|
lint:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- mix format --check-formatted
|
||||||
|
|
||||||
|
analysis:
|
||||||
|
stage: test
|
||||||
|
script:
|
||||||
|
- mix deps.get
|
||||||
|
- mix credo --strict --only=warnings,todo,fixme,consistency,readability
|
||||||
|
|
||||||
|
|
||||||
|
docs-deploy:
|
||||||
|
stage: deploy
|
||||||
|
image: alpine:3.9
|
||||||
|
only:
|
||||||
|
- master@pleroma/pleroma
|
||||||
|
- develop@pleroma/pleroma
|
||||||
|
before_script:
|
||||||
|
- apk update && apk add openssh-client rsync
|
||||||
|
script:
|
||||||
|
- mkdir -p ~/.ssh
|
||||||
|
- echo "${SSH_HOST_KEY}" > ~/.ssh/known_hosts
|
||||||
|
- eval $(ssh-agent -s)
|
||||||
|
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
|
||||||
|
- rsync -hrvz --delete -e "ssh -p ${SSH_PORT}" priv/static/doc/ "${SSH_USER_HOST_LOCATION}/${CI_COMMIT_REF_NAME}"
|
||||||
|
|
|
@ -0,0 +1,403 @@
|
||||||
|
Attribution-NonCommercial-NoDerivatives 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-NonCommercial-NoDerivatives 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-NonCommercial-NoDerivatives 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. 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.
|
||||||
|
|
||||||
|
c. 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.
|
||||||
|
|
||||||
|
d. 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.
|
||||||
|
|
||||||
|
e. Licensed Material means the artistic or literary work, database,
|
||||||
|
or other material to which the Licensor applied this Public
|
||||||
|
License.
|
||||||
|
|
||||||
|
f. 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.
|
||||||
|
|
||||||
|
g. Licensor means the individual(s) or entity(ies) granting rights
|
||||||
|
under this Public License.
|
||||||
|
|
||||||
|
h. NonCommercial means not primarily intended for or directed towards
|
||||||
|
commercial advantage or monetary compensation. For purposes of
|
||||||
|
this Public License, the exchange of the Licensed Material for
|
||||||
|
other material subject to Copyright and Similar Rights by digital
|
||||||
|
file-sharing or similar means is NonCommercial provided there is
|
||||||
|
no payment of monetary compensation in connection with the
|
||||||
|
exchange.
|
||||||
|
|
||||||
|
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, for NonCommercial purposes only; and
|
||||||
|
|
||||||
|
b. produce and reproduce, but not Share, Adapted Material
|
||||||
|
for NonCommercial purposes only.
|
||||||
|
|
||||||
|
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, including when
|
||||||
|
the Licensed Material is used other than for NonCommercial
|
||||||
|
purposes.
|
||||||
|
|
||||||
|
|
||||||
|
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, 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.
|
||||||
|
|
||||||
|
For the avoidance of doubt, You do not have permission under
|
||||||
|
this Public License to Share Adapted Material.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
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 for NonCommercial purposes
|
||||||
|
only and provided You do not Share Adapted Material;
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
|
@ -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.
|
|
@ -0,0 +1,93 @@
|
||||||
|
# Changelog
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
|
## [unreleased]
|
||||||
|
### Added
|
||||||
|
- LDAP authentication
|
||||||
|
- External OAuth provider authentication
|
||||||
|
- A [job queue](https://git.pleroma.social/pleroma/pleroma_job_queue) for federation, emails, web push, etc.
|
||||||
|
- [Prometheus](https://prometheus.io/) metrics
|
||||||
|
- Support for Mastodon's remote interaction
|
||||||
|
- Federation: Support for reports
|
||||||
|
- Configuration: `safe_dm_mentions` option
|
||||||
|
- Configuration: `link_name` option
|
||||||
|
- Configuration: `fetch_initial_posts` option
|
||||||
|
- Pleroma API: User subscribtions
|
||||||
|
- Admin API: Endpoints for listing/revoking invite tokens
|
||||||
|
- Admin API: Endpoints for making users follow/unfollow each other
|
||||||
|
- Mastodon API: [Scheduled statuses](https://docs.joinmastodon.org/api/rest/scheduled-statuses/)
|
||||||
|
- Mastodon API: `/api/v1/notifications/destroy_multiple` (glitch-soc extension)
|
||||||
|
- Mastodon API: [Reports](https://docs.joinmastodon.org/api/rest/reports/)
|
||||||
|
- ActivityPub C2S: OAuth endpoints
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- **Breaking:** Configuration: move from Pleroma.Mailer to Pleroma.Emails.Mailer
|
||||||
|
- Enforcement of OAuth scopes
|
||||||
|
- Add multiple use/time expiring invite token
|
||||||
|
- Restyled OAuth pages to fit with Pleroma's default theme
|
||||||
|
- Link/mention/hashtag detection is now handled by [auto_linker](https://git.pleroma.social/pleroma/auto_linker)
|
||||||
|
- NodeInfo: Return `safe_dm_mentions` feature flag
|
||||||
|
- Federation: Expand the audience of delete activities to all recipients of the deleted object
|
||||||
|
- Federation: Removed `inReplyToStatusId` from objects
|
||||||
|
- Configuration: Dedupe enabled by default
|
||||||
|
- Configuration: Added `extra_cookie_attrs` for setting non-standard cookie attributes. Defaults to ["SameSite=Lax"] so that remote follows work.
|
||||||
|
- Pleroma API: Support for emoji tags in `/api/pleroma/emoji` resulting in a breaking API change
|
||||||
|
- Mastodon API: Support for `exclude_types`, `limit` and `min_id` in `/api/v1/notifications`
|
||||||
|
- Mastodon API: Add `languages` and `registrations` to `/api/v1/instance`
|
||||||
|
- Mastodon API: Provide plaintext versions of cw/content in the Status entity
|
||||||
|
- Mastodon API: Add `pleroma.conversation_id` field to the Status entity
|
||||||
|
- Mastodon API: Add `pleroma.tags`, `pleroma.relationship{}`, `pleroma.is_moderator`, `pleroma.is_admin`, `pleroma.confirmation_pending` fields to the User entity
|
||||||
|
- Mastodon API: Add `pleroma.is_seen` to the Notification entity
|
||||||
|
- Mastodon API: Add `pleroma.local` to the Status entity
|
||||||
|
- Mastodon API: Add `preview` parameter to `POST /api/v1/statuses`
|
||||||
|
- Mastodon API: Add `with_muted` parameter to timeline endpoints
|
||||||
|
- Mastodon API: Actual reblog hiding instead of a dummy
|
||||||
|
- Mastodon API: Remove attachment limit in the Status entity
|
||||||
|
- Deps: Updated Cowboy to 2.6
|
||||||
|
- Deps: Updated Ecto to 3.0.7
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Followers counter not being updated when a follower is blocked
|
||||||
|
- Deactivated users being able to request an access token
|
||||||
|
- Limit on request body in rich media/relme parsers being ignored resulting in a possible memory leak
|
||||||
|
- proper Twitter Card generation instead of a dummy
|
||||||
|
- NodeInfo: Include admins in `staffAccounts`
|
||||||
|
- ActivityPub: Crashing when requesting empty local user's outbox
|
||||||
|
- Federation: Handling of objects without `summary` property
|
||||||
|
- Federation: Add a language tag to activities as required by ActivityStreams 2.0
|
||||||
|
- Federation: Do not federate avatar/banner if set to default allowing other servers/clients to use their defaults
|
||||||
|
- Federation: Cope with missing or explicitly nulled address lists
|
||||||
|
- Federation: Explicitly ensure activities addressed to `as:Public` become addressed to the followers collection
|
||||||
|
- Federation: Better cope with actors which do not declare a followers collection and use `as:Public` with these semantics
|
||||||
|
- MediaProxy: Parse name from content disposition headers even for non-whitelisted types
|
||||||
|
- MediaProxy: S3 link encoding
|
||||||
|
- Rich Media: Reject any data which cannot be explicitly encoded into JSON
|
||||||
|
- Mastodon API: `/api/v1/favourites` serving only public activities
|
||||||
|
- Mastodon API: Reblogs having `in_reply_to_id` - `null` even when they are replies
|
||||||
|
- Mastodon API: Streaming API broadcasting wrong activity id
|
||||||
|
- Mastodon API: 500 errors when requesting a card for a private conversation
|
||||||
|
|
||||||
|
## [0.9.9999] - 2019-04-05
|
||||||
|
### Security
|
||||||
|
- Mastodon API: Fix content warnings skipping HTML sanitization
|
||||||
|
|
||||||
|
## [0.9.999] - 2019-03-13
|
||||||
|
Frontend changes only.
|
||||||
|
### Added
|
||||||
|
- Added floating action button for posting status on mobile
|
||||||
|
### Changed
|
||||||
|
- Changed user-settings icon to a pencil
|
||||||
|
### Fixed
|
||||||
|
- Keyboard shortcuts activating when typing a message
|
||||||
|
- Gaps when scrolling down on a timeline after showing new
|
||||||
|
|
||||||
|
## [0.9.99] - 2019-03-08
|
||||||
|
### Changed
|
||||||
|
- Update the frontend to the 0.9.99 tag
|
||||||
|
### Fixed
|
||||||
|
- Sign the date header in federation to fix Mastodon federation.
|
||||||
|
|
||||||
|
## [0.9.9] - 2019-02-22
|
||||||
|
This is our first stable release.
|
|
@ -0,0 +1,48 @@
|
||||||
|
Unless otherwise stated this repository is copyright © 2017-2019
|
||||||
|
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.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
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 © 2017-2019 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
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The files present under the priv/static/finmoji directory are copyright
|
||||||
|
Finland <https://finland.fi/emoji/>, and are distributed under the Creative
|
||||||
|
Commons Attribution-NonCommercial-NoDerivatives 4.0 International license, you
|
||||||
|
should have received a copy of the license file as CC-BY-NC-ND-4.0.
|
69
README.md
69
README.md
|
@ -1,84 +1,77 @@
|
||||||
# Pleroma
|
# Pleroma
|
||||||
|
|
||||||
|
**Note**: This readme as well as complete documentation is also availible at <https://docs-develop.pleroma.social>
|
||||||
|
|
||||||
## About Pleroma
|
## About Pleroma
|
||||||
|
|
||||||
Pleroma is a microblogging server software that can federate (= exchange messages with) other servers that support the same federation standards (OStatus and ActivityPub). What that means is that you can host a server for yourself or your friends and stay in control of your online identity, but still exchange messages with people on larger servers. Pleroma will federate with all servers that implement either OStatus or ActivityPub, like Friendica, GNU Social, Hubzilla, Mastodon, Misskey, Peertube, and Pixelfed.
|
Pleroma is a microblogging server software that can federate (= exchange messages with) other servers that support the same federation standards (OStatus and ActivityPub). What that means is that you can host a server for yourself or your friends and stay in control of your online identity, but still exchange messages with people on larger servers. Pleroma will federate with all servers that implement either OStatus or ActivityPub, like Friendica, GNU Social, Hubzilla, Mastodon, Misskey, Peertube, and Pixelfed.
|
||||||
|
|
||||||
Pleroma is written in Elixir, high-performance and can run on small devices like a Raspberry Pi.
|
Pleroma is written in Elixir, high-performance and can run on small devices like a Raspberry Pi.
|
||||||
|
|
||||||
For clients it supports both the [GNU Social API with Qvitter extensions](https://twitter-api.readthedocs.io/en/latest/index.html) and the [Mastodon client API](https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md).
|
For clients it supports both the [GNU Social API with Qvitter extensions](https://twitter-api.readthedocs.io/en/latest/index.html) and the [Mastodon client API](https://docs.joinmastodon.org/api/guidelines/).
|
||||||
|
|
||||||
Client applications that are known to work well:
|
- [Client Applications for Pleroma](https://docs-develop.pleroma.social/clients.html)
|
||||||
|
|
||||||
* Twidere
|
No release has been made yet, but several servers have been online for months already. If you want to run your own server, feel free to contact us at @lain@pleroma.soykaf.com or in our dev chat at #pleroma on freenode or via matrix at <https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org>.
|
||||||
* Tusky
|
|
||||||
* Pawoo (Android + iOS)
|
|
||||||
* Subway Tooter
|
|
||||||
* Amaroq (iOS)
|
|
||||||
* Tootdon (Android + iOS)
|
|
||||||
* Tootle (iOS)
|
|
||||||
* Whalebird (Windows + Mac + Linux)
|
|
||||||
|
|
||||||
No release has been made yet, but several servers have been online for months already. If you want to run your own server, feel free to contact us at @lain@pleroma.soykaf.com or in our dev chat at #pleroma on freenode or via matrix at https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
### Docker
|
### Docker
|
||||||
|
|
||||||
While we don't provide docker files, other people have written very good ones. Take a look at https://github.com/Angristan/dockerfiles/tree/master/pleroma or https://github.com/sn0w/pleroma-docker.
|
While we don’t provide docker files, other people have written very good ones. Take a look at <https://github.com/angristan/docker-pleroma> or <https://github.com/sn0w/pleroma-docker>.
|
||||||
|
|
||||||
### Dependencies
|
### Dependencies
|
||||||
|
|
||||||
* Postgresql version 9.6 or newer
|
* Postgresql version 9.6 or newer
|
||||||
* Elixir version 1.5 or newer. If your distribution only has an old version available, check [Elixir's install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
* Elixir version 1.7 or newer. If your distribution only has an old version available, check [Elixir’s install page](https://elixir-lang.org/install.html) or use a tool like [asdf](https://github.com/asdf-vm/asdf).
|
||||||
* Build-essential tools
|
* Build-essential tools
|
||||||
|
|
||||||
### Configuration
|
### Configuration
|
||||||
|
|
||||||
* Run `mix deps.get` to install elixir dependencies.
|
* Run `mix deps.get` to install elixir dependencies.
|
||||||
|
* Run `mix pleroma.instance gen`. This will ask you questions about your instance and generate a configuration file in `config/generated_config.exs`. Check that and copy it to either `config/dev.secret.exs` or `config/prod.secret.exs`. It will also create a `config/setup_db.psql`, which you should run as the PostgreSQL superuser (i.e., `sudo -u postgres psql -f config/setup_db.psql`). It will create the database, user, and password you gave `mix pleroma.gen.instance` earlier, as well as set up the necessary extensions in the database. PostgreSQL superuser privileges are only needed for this step.
|
||||||
* Run `mix generate_config`. This will ask you a few questions about your instance and generate a configuration file in `config/generated_config.exs`. Check that and copy it to either `config/dev.secret.exs` or `config/prod.secret.exs`. It will also create a `config/setup_db.psql`; you may want to double-check this file in case you wanted a different username, or database name than the default. Then you need to run the script as PostgreSQL superuser (i.e. `sudo su postgres -c "psql -f config/setup_db.psql"`). It will create a pleroma db user, database and will setup needed extensions that need to be set up. Postgresql super-user privileges are only needed for this step.
|
* For these next steps, the default will be to run pleroma using the dev configuration file, `config/dev.secret.exs`. To run them using the prod config file, prefix each command at the shell with `MIX_ENV=prod`. For example: `MIX_ENV=prod mix phx.server`. Documentation for the config can be found at [`docs/config.md`](docs/config.md) in the repository, or at the "Configuration" page on <https://docs-develop.pleroma.social/config.html>
|
||||||
|
* Run `mix ecto.migrate` to run the database migrations. You will have to do this again after certain updates.
|
||||||
* For these next steps, the default will be to run pleroma using the dev configuration file, `config/dev.secret.exs`. To run them using the prod config file, prefix each command at the shell with `MIX_ENV=prod`. For example: `MIX_ENV=prod mix phx.server`. Documentation for the config can be found at [``config/config.md``](config/config.md)
|
* You can check if your instance is configured correctly by running it with `mix phx.server` and checking the instance info endpoint at `/api/v1/instance`. If it shows your uri, name and email correctly, you are configured correctly. If it shows something like `localhost:4000`, your configuration is probably wrong, unless you are running a local development setup.
|
||||||
|
* The common and convenient way for adding HTTPS is by using Nginx as a reverse proxy. You can look at example Nginx configuration in `installation/pleroma.nginx`. If you need TLS/SSL certificates for HTTPS, you can look get some for free with letsencrypt: <https://letsencrypt.org/>. The simplest way to obtain and install a certificate is to use [Certbot.](https://certbot.eff.org) Depending on your specific setup, certbot may be able to get a certificate and configure your web server automatically.
|
||||||
* Run `mix ecto.migrate` to run the database migrations. You will have to do this again after certain updates.
|
|
||||||
|
|
||||||
* You can check if your instance is configured correctly by running it with `mix phx.server` and checking the instance info endpoint at `/api/v1/instance`. If it shows your uri, name and email correctly, you are configured correctly. If it shows something like `localhost:4000`, your configuration is probably wrong, unless you are running a local development setup.
|
|
||||||
|
|
||||||
* The common and convenient way for adding HTTPS is by using Nginx as a reverse proxy. You can look at example Nginx configuration in `installation/pleroma.nginx`. If you need TLS/SSL certificates for HTTPS, you can look get some for free with letsencrypt: https://letsencrypt.org/
|
|
||||||
The simplest way to obtain and install a certificate is to use [Certbot.](https://certbot.eff.org) Depending on your specific setup, certbot may be able to get a certificate and configure your web server automatically.
|
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
* By default, it listens on port 4000 (TCP), so you can access it on http://localhost:4000/ (if you are on the same machine). In case of an error it will restart automatically.
|
* By default, it listens on port 4000 (TCP), so you can access it on <http://localhost:4000/> (if you are on the same machine). In case of an error it will restart automatically.
|
||||||
|
|
||||||
### Frontends
|
### Frontends
|
||||||
|
|
||||||
Pleroma comes with two frontends. The first one, Pleroma FE, can be reached by normally visiting the site. The other one, based on the Mastodon project, can be found by visiting the /web path of your site.
|
Pleroma comes with two frontends. The first one, Pleroma FE, can be reached by normally visiting the site. The other one, based on the Mastodon project, can be found by visiting the /web path of your site.
|
||||||
|
|
||||||
### As systemd service (with provided .service file)
|
### As systemd service (with provided .service file)
|
||||||
Example .service file can be found in `installation/pleroma.service` you can put it in `/etc/systemd/system/`.
|
|
||||||
Running `service pleroma start`
|
Example .service file can be found in `installation/pleroma.service`. Copy this to `/etc/systemd/system/`. Running `systemctl enable --now pleroma.service` will run Pleroma and enable startup on boot. Logs can be watched by using `journalctl -fu pleroma.service`.
|
||||||
Logs can be watched by using `journalctl -fu pleroma.service`
|
|
||||||
|
|
||||||
### As OpenRC service (with provided RC file)
|
### As OpenRC service (with provided RC file)
|
||||||
Copy ``installation/init.d/pleroma`` to ``/etc/init.d/pleroma``.
|
|
||||||
You can add it to the services ran by default with:
|
Copy `installation/init.d/pleroma` to `/etc/init.d/pleroma`. You can add it to the services ran by default with: `rc-update add pleroma`
|
||||||
``rc-update add pleroma``
|
|
||||||
|
|
||||||
### Standalone/run by other means
|
### Standalone/run by other means
|
||||||
Run `mix phx.server` in repository's root, it will output log into stdout/stderr
|
|
||||||
|
Run `mix phx.server` in repository’s root, it will output log into stdout/stderr.
|
||||||
|
|
||||||
### Using an upstream proxy for federation
|
### Using an upstream proxy for federation
|
||||||
|
|
||||||
Add the following to your `dev.secret.exs` or `prod.secret.exs` if you want to proxify all http requests that pleroma makes to an upstream proxy server:
|
Add the following to your `dev.secret.exs` or `prod.secret.exs` if you want to proxify all http requests that Pleroma makes to an upstream proxy server:
|
||||||
|
|
||||||
config :pleroma, :http,
|
```elixir
|
||||||
|
config :pleroma, :http,
|
||||||
proxy_url: "127.0.0.1:8123"
|
proxy_url: "127.0.0.1:8123"
|
||||||
|
```
|
||||||
|
|
||||||
This is useful for running pleroma inside Tor or i2p.
|
This is useful for running Pleroma inside Tor or I2P.
|
||||||
|
|
||||||
|
## Customization and contribution
|
||||||
|
|
||||||
|
The [Pleroma Documentation](https://docs-develop.pleroma.social/readme.html) offers manuals and guides on how to further customize your instance to your liking and how you can contribute to the project.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
### No incoming federation
|
### No incoming federation
|
||||||
|
|
||||||
Check that you correctly forward the "host" header to backend. It is needed to validate signatures.
|
Check that you correctly forward the `host` header to the backend. It is needed to validate signatures.
|
||||||
|
|
12
TODO.txt
12
TODO.txt
|
@ -1,12 +0,0 @@
|
||||||
Unliking:
|
|
||||||
|
|
||||||
- Add a proper undo activity, find out how to ignore those in twitter api.
|
|
||||||
|
|
||||||
WEBSUB:
|
|
||||||
|
|
||||||
- Add unsubscription
|
|
||||||
|
|
||||||
OSTATUS:
|
|
||||||
|
|
||||||
- Save and output 'updated'
|
|
||||||
|
|
|
@ -1,3 +1,41 @@
|
||||||
|
# .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
|
# This file is responsible for configuring your application
|
||||||
# and its dependencies with the aid of the Mix.Config module.
|
# and its dependencies with the aid of the Mix.Config module.
|
||||||
#
|
#
|
||||||
|
@ -8,14 +46,45 @@
|
||||||
# General application configuration
|
# General application configuration
|
||||||
config :pleroma, ecto_repos: [Pleroma.Repo]
|
config :pleroma, ecto_repos: [Pleroma.Repo]
|
||||||
|
|
||||||
config :pleroma, Pleroma.Repo, types: Pleroma.PostgresTypes
|
config :pleroma, Pleroma.Repo,
|
||||||
|
types: Pleroma.PostgresTypes,
|
||||||
|
telemetry_event: [Pleroma.Repo.Instrumenter]
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Captcha,
|
||||||
|
enabled: false,
|
||||||
|
seconds_valid: 60,
|
||||||
|
method: Pleroma.Captcha.Kocaptcha
|
||||||
|
|
||||||
|
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, Pleroma.Captcha.Kocaptcha, endpoint: "https://captcha.kotobank.ch"
|
||||||
|
|
||||||
# Upload configuration
|
# Upload configuration
|
||||||
config :pleroma, Pleroma.Upload,
|
config :pleroma, Pleroma.Upload,
|
||||||
uploader: Pleroma.Uploaders.Local,
|
uploader: Pleroma.Uploaders.Local,
|
||||||
filters: [],
|
filters: [Pleroma.Upload.Filter.Dedupe],
|
||||||
|
link_name: true,
|
||||||
proxy_remote: false,
|
proxy_remote: false,
|
||||||
proxy_opts: []
|
proxy_opts: [
|
||||||
|
redirect_on_failure: false,
|
||||||
|
max_body_length: 25 * 1_048_576,
|
||||||
|
http: [
|
||||||
|
follow_redirect: true,
|
||||||
|
pool: :upload
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "uploads"
|
||||||
|
|
||||||
|
@ -27,7 +96,13 @@
|
||||||
cgi: "https://mdii.sakura.ne.jp/mdii-post.cgi",
|
cgi: "https://mdii.sakura.ne.jp/mdii-post.cgi",
|
||||||
files: "https://mdii.sakura.ne.jp"
|
files: "https://mdii.sakura.ne.jp"
|
||||||
|
|
||||||
config :pleroma, :emoji, shortcode_globs: ["/emoji/custom/**/*.png"]
|
config :pleroma, :emoji,
|
||||||
|
shortcode_globs: ["/emoji/custom/**/*.png"],
|
||||||
|
groups: [
|
||||||
|
# Put groups that have higher priority than defaults here. Example in `docs/config/custom_emoji.md`
|
||||||
|
Finmoji: "/finmoji/128px/*-128.png",
|
||||||
|
Custom: ["/emoji/*.png", "/emoji/custom/*.png"]
|
||||||
|
]
|
||||||
|
|
||||||
config :pleroma, :uri_schemes,
|
config :pleroma, :uri_schemes,
|
||||||
valid_schemes: [
|
valid_schemes: [
|
||||||
|
@ -47,24 +122,63 @@
|
||||||
"xmpp"
|
"xmpp"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
websocket_config = [
|
||||||
|
path: "/websocket",
|
||||||
|
serializer: [
|
||||||
|
{Phoenix.Socket.V1.JSONSerializer, "~> 1.0.0"},
|
||||||
|
{Phoenix.Socket.V2.JSONSerializer, "~> 2.0.0"}
|
||||||
|
],
|
||||||
|
timeout: 60_000,
|
||||||
|
transport_log: false,
|
||||||
|
compress: false
|
||||||
|
]
|
||||||
|
|
||||||
# Configures the endpoint
|
# Configures the endpoint
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
|
instrumenters: [Pleroma.Web.Endpoint.Instrumenter],
|
||||||
url: [host: "localhost"],
|
url: [host: "localhost"],
|
||||||
|
http: [
|
||||||
|
dispatch: [
|
||||||
|
{:_,
|
||||||
|
[
|
||||||
|
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
||||||
|
{"/websocket", Phoenix.Endpoint.CowboyWebSocket,
|
||||||
|
{Phoenix.Transports.WebSocket,
|
||||||
|
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
|
||||||
|
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
],
|
||||||
protocol: "https",
|
protocol: "https",
|
||||||
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
secret_key_base: "aK4Abxf29xU9TTDKre9coZPUgevcVCFQJe/5xP/7Lt4BEif6idBIbjupVbOrbKxl",
|
||||||
signing_salt: "CqaoopA2",
|
signing_salt: "CqaoopA2",
|
||||||
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
|
render_errors: [view: Pleroma.Web.ErrorView, accepts: ~w(json)],
|
||||||
pubsub: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2],
|
pubsub: [name: Pleroma.PubSub, adapter: Phoenix.PubSub.PG2],
|
||||||
secure_cookie_flag: true
|
secure_cookie_flag: true,
|
||||||
|
extra_cookie_attrs: [
|
||||||
|
"SameSite=Lax"
|
||||||
|
]
|
||||||
|
|
||||||
# Configures Elixir's Logger
|
# Configures Elixir's Logger
|
||||||
config :logger, :console,
|
config :logger, :console,
|
||||||
format: "$time $metadata[$level] $message\n",
|
format: "$time $metadata[$level] $message\n",
|
||||||
metadata: [:request_id]
|
metadata: [:request_id]
|
||||||
|
|
||||||
|
config :logger, :ex_syslogger,
|
||||||
|
level: :debug,
|
||||||
|
ident: "Pleroma",
|
||||||
|
format: "$metadata[$level] $message",
|
||||||
|
metadata: [:request_id]
|
||||||
|
|
||||||
|
config :quack,
|
||||||
|
level: :warn,
|
||||||
|
meta: [:all],
|
||||||
|
webhook_url: "https://hooks.slack.com/services/YOUR-KEY-HERE"
|
||||||
|
|
||||||
config :mime, :types, %{
|
config :mime, :types, %{
|
||||||
"application/xml" => ["xml"],
|
"application/xml" => ["xml"],
|
||||||
"application/xrd+xml" => ["xrd+xml"],
|
"application/xrd+xml" => ["xrd+xml"],
|
||||||
|
"application/jrd+json" => ["jrd+json"],
|
||||||
"application/activity+json" => ["activity+json"],
|
"application/activity+json" => ["activity+json"],
|
||||||
"application/ld+json" => ["activity+json"]
|
"application/ld+json" => ["activity+json"]
|
||||||
}
|
}
|
||||||
|
@ -72,33 +186,52 @@
|
||||||
config :pleroma, :websub, Pleroma.Web.Websub
|
config :pleroma, :websub, Pleroma.Web.Websub
|
||||||
config :pleroma, :ostatus, Pleroma.Web.OStatus
|
config :pleroma, :ostatus, Pleroma.Web.OStatus
|
||||||
config :pleroma, :httpoison, Pleroma.HTTP
|
config :pleroma, :httpoison, Pleroma.HTTP
|
||||||
|
config :tesla, adapter: Tesla.Adapter.Hackney
|
||||||
|
|
||||||
# Configures http settings, upstream proxy etc.
|
# Configures http settings, upstream proxy etc.
|
||||||
config :pleroma, :http, proxy_url: nil
|
config :pleroma, :http,
|
||||||
|
proxy_url: nil,
|
||||||
|
adapter: [
|
||||||
|
ssl_options: [
|
||||||
|
# We don't support TLS v1.3 yet
|
||||||
|
versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
name: "Pleroma",
|
name: "Pleroma",
|
||||||
email: "example@example.com",
|
email: "example@example.com",
|
||||||
|
notify_email: "noreply@example.com",
|
||||||
description: "A Pleroma instance, an alternative fediverse server",
|
description: "A Pleroma instance, an alternative fediverse server",
|
||||||
limit: 5000,
|
limit: 5_000,
|
||||||
|
remote_limit: 100_000,
|
||||||
upload_limit: 16_000_000,
|
upload_limit: 16_000_000,
|
||||||
avatar_upload_limit: 2_000_000,
|
avatar_upload_limit: 2_000_000,
|
||||||
background_upload_limit: 4_000_000,
|
background_upload_limit: 4_000_000,
|
||||||
banner_upload_limit: 4_000_000,
|
banner_upload_limit: 4_000_000,
|
||||||
registrations_open: true,
|
registrations_open: true,
|
||||||
federating: true,
|
federating: true,
|
||||||
|
federation_reachability_timeout_days: 7,
|
||||||
allow_relay: true,
|
allow_relay: true,
|
||||||
rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
|
rewrite_policy: Pleroma.Web.ActivityPub.MRF.NoOpPolicy,
|
||||||
public: true,
|
public: true,
|
||||||
quarantined_instances: [],
|
quarantined_instances: [],
|
||||||
managed_config: true,
|
managed_config: true,
|
||||||
|
static_dir: "instance/static/",
|
||||||
allowed_post_formats: [
|
allowed_post_formats: [
|
||||||
"text/plain",
|
"text/plain",
|
||||||
"text/html",
|
"text/html",
|
||||||
"text/markdown"
|
"text/markdown"
|
||||||
],
|
],
|
||||||
finmoji_enabled: true,
|
finmoji_enabled: true,
|
||||||
mrf_transparency: true
|
mrf_transparency: true,
|
||||||
|
autofollowed_nicknames: [],
|
||||||
|
max_pinned_statuses: 1,
|
||||||
|
no_attachment_links: false,
|
||||||
|
welcome_user_nickname: nil,
|
||||||
|
welcome_message: nil,
|
||||||
|
max_report_comment_size: 1000,
|
||||||
|
safe_dm_mentions: false
|
||||||
|
|
||||||
config :pleroma, :markup,
|
config :pleroma, :markup,
|
||||||
# XXX - unfortunately, inline images must be enabled by default right now, because
|
# XXX - unfortunately, inline images must be enabled by default right now, because
|
||||||
|
@ -112,6 +245,7 @@
|
||||||
Pleroma.HTML.Scrubber.Default
|
Pleroma.HTML.Scrubber.Default
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Deprecated, will be gone in 1.0
|
||||||
config :pleroma, :fe,
|
config :pleroma, :fe,
|
||||||
theme: "pleroma-dark",
|
theme: "pleroma-dark",
|
||||||
logo: "/static/logo.png",
|
logo: "/static/logo.png",
|
||||||
|
@ -125,7 +259,31 @@
|
||||||
formatting_options_enabled: false,
|
formatting_options_enabled: false,
|
||||||
collapse_message_with_subject: false,
|
collapse_message_with_subject: false,
|
||||||
hide_post_stats: false,
|
hide_post_stats: false,
|
||||||
hide_user_stats: false
|
hide_user_stats: false,
|
||||||
|
scope_copy: true,
|
||||||
|
subject_line_behavior: "email",
|
||||||
|
always_show_subject_input: true
|
||||||
|
|
||||||
|
config :pleroma, :frontend_configurations,
|
||||||
|
pleroma_fe: %{
|
||||||
|
theme: "pleroma-dark",
|
||||||
|
logo: "/static/logo.png",
|
||||||
|
background: "/images/city.jpg",
|
||||||
|
redirectRootNoLogin: "/main/all",
|
||||||
|
redirectRootLogin: "/main/friends",
|
||||||
|
showInstanceSpecificPanel: true,
|
||||||
|
scopeOptionsEnabled: false,
|
||||||
|
formattingOptionsEnabled: false,
|
||||||
|
collapseMessageWithSubject: false,
|
||||||
|
hidePostStats: false,
|
||||||
|
hideUserStats: false,
|
||||||
|
scopeCopy: true,
|
||||||
|
subjectLineBehavior: "email",
|
||||||
|
alwaysShowSubjectInput: true
|
||||||
|
},
|
||||||
|
masto_fe: %{
|
||||||
|
showInstanceSpecificPanel: true
|
||||||
|
}
|
||||||
|
|
||||||
config :pleroma, :activitypub,
|
config :pleroma, :activitypub,
|
||||||
accept_blocks: true,
|
accept_blocks: true,
|
||||||
|
@ -141,6 +299,10 @@
|
||||||
allow_followersonly: false,
|
allow_followersonly: false,
|
||||||
allow_direct: false
|
allow_direct: false
|
||||||
|
|
||||||
|
config :pleroma, :mrf_hellthread,
|
||||||
|
delist_threshold: 10,
|
||||||
|
reject_threshold: 20
|
||||||
|
|
||||||
config :pleroma, :mrf_simple,
|
config :pleroma, :mrf_simple,
|
||||||
media_removal: [],
|
media_removal: [],
|
||||||
media_nsfw: [],
|
media_nsfw: [],
|
||||||
|
@ -148,18 +310,26 @@
|
||||||
reject: [],
|
reject: [],
|
||||||
accept: []
|
accept: []
|
||||||
|
|
||||||
|
config :pleroma, :mrf_keyword,
|
||||||
|
reject: [],
|
||||||
|
federated_timeline_removal: [],
|
||||||
|
replace: []
|
||||||
|
|
||||||
|
config :pleroma, :rich_media, enabled: true
|
||||||
|
|
||||||
config :pleroma, :media_proxy,
|
config :pleroma, :media_proxy,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
# base_url: "https://cache.pleroma.social",
|
|
||||||
proxy_opts: [
|
proxy_opts: [
|
||||||
# inline_content_types: [] | false | true,
|
redirect_on_failure: false,
|
||||||
# http: [:insecure]
|
max_body_length: 25 * 1_048_576,
|
||||||
|
http: [
|
||||||
|
follow_redirect: true,
|
||||||
|
pool: :media
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
config :pleroma, :chat, enabled: true
|
config :pleroma, :chat, enabled: true
|
||||||
|
|
||||||
config :ecto, json_library: Jason
|
|
||||||
|
|
||||||
config :phoenix, :format_encoders, json: Jason
|
config :phoenix, :format_encoders, json: Jason
|
||||||
|
|
||||||
config :pleroma, :gopher,
|
config :pleroma, :gopher,
|
||||||
|
@ -167,6 +337,8 @@
|
||||||
ip: {0, 0, 0, 0},
|
ip: {0, 0, 0, 0},
|
||||||
port: 9999
|
port: 9999
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.Metadata, providers: [], unfurl_nsfw: false
|
||||||
|
|
||||||
config :pleroma, :suggestions,
|
config :pleroma, :suggestions,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
third_party_engine:
|
third_party_engine:
|
||||||
|
@ -196,6 +368,102 @@
|
||||||
credentials: true,
|
credentials: true,
|
||||||
headers: ["Authorization", "Content-Type", "Idempotency-Key"]
|
headers: ["Authorization", "Content-Type", "Idempotency-Key"]
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.User,
|
||||||
|
restricted_nicknames: [
|
||||||
|
".well-known",
|
||||||
|
"~",
|
||||||
|
"about",
|
||||||
|
"activities",
|
||||||
|
"api",
|
||||||
|
"auth",
|
||||||
|
"dev",
|
||||||
|
"friend-requests",
|
||||||
|
"inbox",
|
||||||
|
"internal",
|
||||||
|
"main",
|
||||||
|
"media",
|
||||||
|
"nodeinfo",
|
||||||
|
"notice",
|
||||||
|
"oauth",
|
||||||
|
"objects",
|
||||||
|
"ostatus_subscribe",
|
||||||
|
"pleroma",
|
||||||
|
"proxy",
|
||||||
|
"push",
|
||||||
|
"registration",
|
||||||
|
"relay",
|
||||||
|
"settings",
|
||||||
|
"status",
|
||||||
|
"tag",
|
||||||
|
"user-search",
|
||||||
|
"users",
|
||||||
|
"web"
|
||||||
|
]
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Web.Federator.RetryQueue,
|
||||||
|
enabled: false,
|
||||||
|
max_jobs: 20,
|
||||||
|
initial_timeout: 30,
|
||||||
|
max_retries: 5
|
||||||
|
|
||||||
|
config :pleroma_job_queue, :queues,
|
||||||
|
federator_incoming: 50,
|
||||||
|
federator_outgoing: 50,
|
||||||
|
web_push: 50,
|
||||||
|
mailer: 10,
|
||||||
|
transmogrifier: 20,
|
||||||
|
scheduled_activities: 10
|
||||||
|
|
||||||
|
config :pleroma, :fetch_initial_posts,
|
||||||
|
enabled: false,
|
||||||
|
pages: 5
|
||||||
|
|
||||||
|
config :auto_linker,
|
||||||
|
opts: [
|
||||||
|
scheme: true,
|
||||||
|
extra: true,
|
||||||
|
class: false,
|
||||||
|
strip_prefix: false,
|
||||||
|
new_window: false,
|
||||||
|
rel: false
|
||||||
|
]
|
||||||
|
|
||||||
|
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 = String.split(System.get_env("OAUTH_CONSUMER_STRATEGIES") || "")
|
||||||
|
|
||||||
|
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)
|
||||||
|
{String.to_atom(strategy), {strategy_module, [callback_params: ["state"]]}}
|
||||||
|
end
|
||||||
|
|
||||||
|
config :ueberauth,
|
||||||
|
Ueberauth,
|
||||||
|
base_path: "/oauth",
|
||||||
|
providers: ueberauth_providers
|
||||||
|
|
||||||
|
config :pleroma, :auth, oauth_consumer_strategies: oauth_consumer_strategies
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Sendmail
|
||||||
|
|
||||||
|
config :prometheus, Pleroma.Web.Endpoint.MetricsExporter, path: "/api/pleroma/app_metrics"
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.ScheduledActivity,
|
||||||
|
daily_user_limit: 25,
|
||||||
|
total_user_limit: 300,
|
||||||
|
enabled: true
|
||||||
|
|
||||||
# Import environment specific config. This must remain at the bottom
|
# Import environment specific config. This must remain at the bottom
|
||||||
# of this file so it overrides the configuration defined above.
|
# of this file so it overrides the configuration defined above.
|
||||||
import_config "#{Mix.env()}.exs"
|
import_config "#{Mix.env()}.exs"
|
||||||
|
|
111
config/config.md
111
config/config.md
|
@ -1,111 +0,0 @@
|
||||||
# Configuration
|
|
||||||
|
|
||||||
This file describe the configuration, it is recommended to edit the relevant *.secret.exs file instead of the others founds in the ``config`` directory.
|
|
||||||
If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.
|
|
||||||
|
|
||||||
## Pleroma.Upload
|
|
||||||
* `uploader`: Select which `Pleroma.Uploaders` to use
|
|
||||||
* `filters`: List of `Pleroma.Upload.Filter` to use.
|
|
||||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host.
|
|
||||||
* `proxy_remote`: If you're using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
|
||||||
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
|
||||||
|
|
||||||
Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
|
||||||
|
|
||||||
## Pleroma.Uploaders.Local
|
|
||||||
* `uploads`: Which directory to store the user-uploads in, relative to pleroma’s working directory
|
|
||||||
|
|
||||||
## Pleroma.Upload.Filter.Mogrify
|
|
||||||
|
|
||||||
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", {"impode", "1"}]`.
|
|
||||||
|
|
||||||
## :uri_schemes
|
|
||||||
* `valid_schemes`: List of the scheme part that is considered valid to be an URL
|
|
||||||
|
|
||||||
## :instance
|
|
||||||
* `name`: The instance’s name
|
|
||||||
* `email`: Email used to reach an Administrator/Moderator of the instance
|
|
||||||
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``
|
|
||||||
* `limit`: Posts character limit (CW/Subject included in the counter)
|
|
||||||
* `upload_limit`: File size limit of uploads (except for avatar, background, banner)
|
|
||||||
* `avatar_upload_limit`: File size limit of user’s profile avatars
|
|
||||||
* `background_upload_limit`: File size limit of user’s profile backgrounds
|
|
||||||
* `banner_upload_limit`: File size limit of user’s profile backgrounds
|
|
||||||
* `registerations_open`: Enable registerations for anyone, invitations can be used when false.
|
|
||||||
* `federating`
|
|
||||||
* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance
|
|
||||||
* `rewrite_policy`: Message Rewrite Policy, either one or a list. Here are the ones available by default:
|
|
||||||
* `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default)
|
|
||||||
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production
|
|
||||||
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See ``:mrf_simple`` section)
|
|
||||||
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section)
|
|
||||||
* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
|
|
||||||
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
|
||||||
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
|
|
||||||
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML)
|
|
||||||
* `finmoji_enabled`: Whenether to enable the finmojis in the custom emojis.
|
|
||||||
* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
|
||||||
|
|
||||||
## :fe
|
|
||||||
This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:instance`` is set to false.
|
|
||||||
|
|
||||||
* `theme`: Which theme to use, they are defined in ``styles.json``
|
|
||||||
* `logo`: URL of the logo, defaults to Pleroma’s logo
|
|
||||||
* `logo_mask`: Whenether to mask the logo
|
|
||||||
* `logo_margin`: What margin to use around the logo
|
|
||||||
* `background`: URL of the background, unless viewing a user profile with a background that is set
|
|
||||||
* `redirect_root_no_login`: relative URL which indicates where to redirect when a user isn’t logged in.
|
|
||||||
* `redirect_root_login`: relative URL which indicates where to redirect when a user is logged in.
|
|
||||||
* `show_instance_panel`: Whenether to show the instance’s specific panel.
|
|
||||||
* `scope_options_enabled`: Enable setting an notice visibility and subject/CW when posting
|
|
||||||
* `formatting_options_enabled`: Enable setting a formatting different than plain-text (ie. HTML, Markdown) when posting, relates to ``:instance, allowed_post_formats``
|
|
||||||
* `collapse_message_with_subjects`: When a message has a subject(aka Content Warning), collapse it by default
|
|
||||||
* `hide_post_stats`: Hide notices statistics(repeats, favorites, …)
|
|
||||||
* `hide_user_stats`: Hide profile statistics(posts, posts per day, followers, followings, …)
|
|
||||||
|
|
||||||
## :mrf_simple
|
|
||||||
* `media_removal`: List of instances to remove medias from
|
|
||||||
* `media_nsfw`: List of instances to put medias as NSFW(sensitive) from
|
|
||||||
* `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline
|
|
||||||
* `reject`: List of instances to reject any activities from
|
|
||||||
* `accept`: List of instances to accept any activities from
|
|
||||||
|
|
||||||
## :mrf_rejectnonpublic
|
|
||||||
* `allow_followersonly`: whether to allow followers-only posts
|
|
||||||
* `allow_direct`: whether to allow direct messages
|
|
||||||
|
|
||||||
## :media_proxy
|
|
||||||
* `enabled`: Enables proxying of remote media to the instance’s proxy
|
|
||||||
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
|
|
||||||
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
|
|
||||||
|
|
||||||
## :gopher
|
|
||||||
* `enabled`: Enables the gopher interface
|
|
||||||
* `ip`: IP address to bind to
|
|
||||||
* `port`: Port to bind to
|
|
||||||
|
|
||||||
## :activitypub
|
|
||||||
* ``accept_blocks``: Whether to accept incoming block activities from other instances
|
|
||||||
* ``unfollow_blocked``: Whether blocks result in people getting unfollowed
|
|
||||||
* ``outgoing_blocks``: Whether to federate blocks to other instances
|
|
||||||
* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question
|
|
||||||
|
|
||||||
## :http_security
|
|
||||||
* ``enabled``: Whether the managed content security policy is enabled
|
|
||||||
* ``sts``: Whether to additionally send a `Strict-Transport-Security` header
|
|
||||||
* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent
|
|
||||||
* ``ct_max_age``: The maximum age for the `Expect-CT` header if sent
|
|
||||||
* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`.
|
|
||||||
|
|
||||||
## :mrf_user_allowlist
|
|
||||||
|
|
||||||
The keys in this section are the domain names that the policy should apply to.
|
|
||||||
Each key should be assigned a list of users that should be allowed through by
|
|
||||||
their ActivityPub ID.
|
|
||||||
|
|
||||||
An example:
|
|
||||||
|
|
||||||
```
|
|
||||||
config :pleroma, :mrf_user_allowlist,
|
|
||||||
"example.org": ["https://example.org/users/admin"]
|
|
||||||
```
|
|
|
@ -15,7 +15,10 @@
|
||||||
debug_errors: true,
|
debug_errors: true,
|
||||||
code_reloader: true,
|
code_reloader: true,
|
||||||
check_origin: false,
|
check_origin: false,
|
||||||
watchers: []
|
watchers: [],
|
||||||
|
secure_cookie_flag: false
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Local
|
||||||
|
|
||||||
# ## SSL Support
|
# ## SSL Support
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
firefox, /emoji/Firefox.gif
|
firefox, /emoji/Firefox.gif, Gif,Fun
|
||||||
blank, /emoji/blank.png
|
blank, /emoji/blank.png, Fun
|
||||||
f_00b, /emoji/f_00b.png
|
f_00b, /emoji/f_00b.png
|
||||||
f_00b11b, /emoji/f_00b11b.png
|
f_00b11b, /emoji/f_00b11b.png
|
||||||
f_00b33b, /emoji/f_00b33b.png
|
f_00b33b, /emoji/f_00b33b.png
|
||||||
|
@ -28,4 +28,3 @@ f_33b00b, /emoji/f_33b00b.png
|
||||||
f_33b22b, /emoji/f_33b22b.png
|
f_33b22b, /emoji/f_33b22b.png
|
||||||
f_33h, /emoji/f_33h.png
|
f_33h, /emoji/f_33h.png
|
||||||
f_33t, /emoji/f_33t.png
|
f_33t, /emoji/f_33t.png
|
||||||
|
|
||||||
|
|
|
@ -4,13 +4,29 @@
|
||||||
# you can enable the server option below.
|
# you can enable the server option below.
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
http: [port: 4001],
|
http: [port: 4001],
|
||||||
server: false
|
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
|
# Print only warnings and errors during test
|
||||||
config :logger, level: :warn
|
config :logger, level: :warn
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Upload, filters: [], link_name: false
|
||||||
|
|
||||||
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
config :pleroma, Pleroma.Uploaders.Local, uploads: "test/uploads"
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.Emails.Mailer, adapter: Swoosh.Adapters.Test
|
||||||
|
|
||||||
|
config :pleroma, :instance,
|
||||||
|
email: "admin@example.com",
|
||||||
|
notify_email: "noreply@example.com"
|
||||||
|
|
||||||
# Configure your database
|
# Configure your database
|
||||||
config :pleroma, Pleroma.Repo,
|
config :pleroma, Pleroma.Repo,
|
||||||
adapter: Ecto.Adapters.Postgres,
|
adapter: Ecto.Adapters.Postgres,
|
||||||
|
@ -25,7 +41,23 @@
|
||||||
|
|
||||||
config :pleroma, :websub, Pleroma.Web.WebsubMock
|
config :pleroma, :websub, Pleroma.Web.WebsubMock
|
||||||
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
|
config :pleroma, :ostatus, Pleroma.Web.OStatusMock
|
||||||
config :pleroma, :httpoison, HTTPoisonMock
|
config :tesla, adapter: Tesla.Mock
|
||||||
|
config :pleroma, :rich_media, enabled: false
|
||||||
|
|
||||||
|
config :web_push_encryption, :vapid_details,
|
||||||
|
subject: "mailto:administrator@example.com",
|
||||||
|
public_key:
|
||||||
|
"BLH1qVhJItRGCfxgTtONfsOKDc9VRAraXw-3NsmjMngWSh7NxOizN6bkuRA7iLTMPS82PjwJAr3UoK9EC1IFrz4",
|
||||||
|
private_key: "_-XZ0iebPrRfZ_o0-IatTdszYa8VCH1yLN-JauK7HHA"
|
||||||
|
|
||||||
|
config :web_push_encryption, :http_client, Pleroma.Web.WebPushHttpClientMock
|
||||||
|
|
||||||
|
config :pleroma_job_queue, disabled: true
|
||||||
|
|
||||||
|
config :pleroma, Pleroma.ScheduledActivity,
|
||||||
|
daily_user_limit: 2,
|
||||||
|
total_user_limit: 3,
|
||||||
|
enabled: false
|
||||||
|
|
||||||
try do
|
try do
|
||||||
import_config "test.secret.exs"
|
import_config "test.secret.exs"
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Backup/Restore 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>`
|
||||||
|
4. Copy `pleroma.pgdump`, `config/prod.secret.exs` and the `uploads` folder to your backup destination. If you have other modifications, copy those changes too.
|
||||||
|
5. Restart the Pleroma service.
|
||||||
|
|
||||||
|
## Restore
|
||||||
|
|
||||||
|
1. Stop the Pleroma service.
|
||||||
|
2. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
||||||
|
3. Copy the above mentioned files back to their original position.
|
||||||
|
4. Run `sudo -Hu postgres pg_restore -d <pleroma_db> -v -1 </path/to/backup_location/pleroma.pgdump>`
|
||||||
|
5. Restart the Pleroma service.
|
|
@ -0,0 +1,9 @@
|
||||||
|
# Updating your instance
|
||||||
|
1. Go to the working directory of Pleroma (default is `/opt/pleroma`)
|
||||||
|
2. Run `git pull`. This pulls the latest changes from upstream.
|
||||||
|
3. Run `mix deps.get`. This pulls in any new dependencies.
|
||||||
|
4. Stop the Pleroma service.
|
||||||
|
5. Run `mix ecto.migrate`[^1]. This task performs database migrations, if there were any.
|
||||||
|
6. Start the Pleroma service.
|
||||||
|
|
||||||
|
[^1]: Prefix with `MIX_ENV=prod` to run it using the production config file.
|
|
@ -0,0 +1,277 @@
|
||||||
|
# Admin API
|
||||||
|
|
||||||
|
Authentication is required and the user must be an admin.
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/users`
|
||||||
|
|
||||||
|
### List users
|
||||||
|
|
||||||
|
- Method `GET`
|
||||||
|
- Query Params:
|
||||||
|
- *optional* `query`: **string** search term
|
||||||
|
- *optional* `filters`: **string** comma-separated string of filters:
|
||||||
|
- `local`: only local users
|
||||||
|
- `external`: only external users
|
||||||
|
- `active`: only active users
|
||||||
|
- `deactivated`: only deactivated users
|
||||||
|
- *optional* `page`: **integer** page number
|
||||||
|
- *optional* `page_size`: **integer** number of users per page (default is `50`)
|
||||||
|
- Example: `https://mypleroma.org/api/pleroma/admin/users?query=john&filters=local,active&page=1&page_size=10`
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"page_size": integer,
|
||||||
|
"count": integer,
|
||||||
|
"users": [
|
||||||
|
{
|
||||||
|
"deactivated": bool,
|
||||||
|
"id": integer,
|
||||||
|
"nickname": string,
|
||||||
|
"roles": {
|
||||||
|
"admin": bool,
|
||||||
|
"moderator": bool
|
||||||
|
},
|
||||||
|
"local": bool,
|
||||||
|
"tags": array
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/user`
|
||||||
|
|
||||||
|
### Remove a user
|
||||||
|
|
||||||
|
- Method `DELETE`
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
- Response: User’s nickname
|
||||||
|
|
||||||
|
### Create a user
|
||||||
|
|
||||||
|
- Method: `POST`
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
- `email`
|
||||||
|
- `password`
|
||||||
|
- Response: User’s nickname
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/user/follow`
|
||||||
|
### Make a user follow another user
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `follower`: The nickname of the follower
|
||||||
|
- `followed`: The nickname of the followed
|
||||||
|
- Response:
|
||||||
|
- "ok"
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/user/unfollow`
|
||||||
|
### Make a user unfollow another user
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `follower`: The nickname of the follower
|
||||||
|
- `followed`: The nickname of the followed
|
||||||
|
- Response:
|
||||||
|
- "ok"
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/users/:nickname/toggle_activation`
|
||||||
|
|
||||||
|
### Toggle user activation
|
||||||
|
|
||||||
|
- Method: `PATCH`
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
- Response: User’s object
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"deactivated": bool,
|
||||||
|
"id": integer,
|
||||||
|
"nickname": string
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/users/tag`
|
||||||
|
|
||||||
|
### Tag a list of users
|
||||||
|
|
||||||
|
- Method: `PUT`
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
- `tags`
|
||||||
|
|
||||||
|
### Untag a list of users
|
||||||
|
|
||||||
|
- Method: `DELETE`
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
- `tags`
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/permission_group/:nickname`
|
||||||
|
|
||||||
|
### Get user user permission groups membership
|
||||||
|
|
||||||
|
- Method: `GET`
|
||||||
|
- Params: none
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"is_moderator": bool,
|
||||||
|
"is_admin": bool
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/permission_group/:nickname/:permission_group`
|
||||||
|
|
||||||
|
Note: Available `:permission_group` is currently moderator and admin. 404 is returned when the permission group doesn’t exist.
|
||||||
|
|
||||||
|
### Get user user permission groups membership per permission group
|
||||||
|
|
||||||
|
- Method: `GET`
|
||||||
|
- Params: none
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"is_moderator": bool,
|
||||||
|
"is_admin": bool
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add user in permission group
|
||||||
|
|
||||||
|
- Method: `POST`
|
||||||
|
- Params: none
|
||||||
|
- Response:
|
||||||
|
- On failure: `{"error": "…"}`
|
||||||
|
- On success: JSON of the `user.info`
|
||||||
|
|
||||||
|
### Remove user from permission group
|
||||||
|
|
||||||
|
- Method: `DELETE`
|
||||||
|
- Params: none
|
||||||
|
- Response:
|
||||||
|
- On failure: `{"error": "…"}`
|
||||||
|
- On success: JSON of the `user.info`
|
||||||
|
- Note: An admin cannot revoke their own admin status.
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/activation_status/:nickname`
|
||||||
|
|
||||||
|
### Active or deactivate a user
|
||||||
|
|
||||||
|
- Method: `PUT`
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
- `status` BOOLEAN field, false value means deactivation.
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/users/:nickname`
|
||||||
|
|
||||||
|
### Retrive the details of a user
|
||||||
|
|
||||||
|
- Method: `GET`
|
||||||
|
- Params:
|
||||||
|
- `nickname`
|
||||||
|
- Response:
|
||||||
|
- On failure: `Not found`
|
||||||
|
- On success: JSON of the user
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/relay`
|
||||||
|
|
||||||
|
### Follow a Relay
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `relay_url`
|
||||||
|
- Response:
|
||||||
|
- On success: URL of the followed relay
|
||||||
|
|
||||||
|
### Unfollow a Relay
|
||||||
|
|
||||||
|
- Methods: `DELETE`
|
||||||
|
- Params:
|
||||||
|
- `relay_url`
|
||||||
|
- Response:
|
||||||
|
- On success: URL of the unfollowed relay
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/invite_token`
|
||||||
|
|
||||||
|
### Get an account registration invite token
|
||||||
|
|
||||||
|
- Methods: `GET`
|
||||||
|
- Params:
|
||||||
|
- *optional* `invite` => [
|
||||||
|
- *optional* `max_use` (integer)
|
||||||
|
- *optional* `expires_at` (date string e.g. "2019-04-07")
|
||||||
|
]
|
||||||
|
- Response: invite token (base64 string)
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/invites`
|
||||||
|
|
||||||
|
### Get a list of generated invites
|
||||||
|
|
||||||
|
- Methods: `GET`
|
||||||
|
- Params: none
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
|
||||||
|
"invites": [
|
||||||
|
{
|
||||||
|
"id": integer,
|
||||||
|
"token": string,
|
||||||
|
"used": boolean,
|
||||||
|
"expires_at": date,
|
||||||
|
"uses": integer,
|
||||||
|
"max_use": integer,
|
||||||
|
"invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
|
||||||
|
},
|
||||||
|
...
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/revoke_invite`
|
||||||
|
|
||||||
|
### Revoke invite by token
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `token`
|
||||||
|
- Response:
|
||||||
|
|
||||||
|
```JSON
|
||||||
|
{
|
||||||
|
"id": integer,
|
||||||
|
"token": string,
|
||||||
|
"used": boolean,
|
||||||
|
"expires_at": date,
|
||||||
|
"uses": integer,
|
||||||
|
"max_use": integer,
|
||||||
|
"invite_type": string (possible values: `one_time`, `reusable`, `date_limited`, `reusable_date_limited`)
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/email_invite`
|
||||||
|
|
||||||
|
### Sends registration invite via email
|
||||||
|
|
||||||
|
- Methods: `POST`
|
||||||
|
- Params:
|
||||||
|
- `email`
|
||||||
|
- `name`, optional
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/password_reset`
|
||||||
|
|
||||||
|
### Get a password reset token for a given nickname
|
||||||
|
|
||||||
|
- Methods: `GET`
|
||||||
|
- Params: none
|
||||||
|
- Response: password reset token (base64 string)
|
|
@ -0,0 +1,54 @@
|
||||||
|
# Differences in Mastodon API responses from vanilla Mastodon
|
||||||
|
|
||||||
|
A Pleroma instance can be identified by "<Mastodon version> (compatible; Pleroma <version>)" present in `version` field in response from `/api/v1/instance`
|
||||||
|
|
||||||
|
## Flake IDs
|
||||||
|
|
||||||
|
Pleroma uses 128-bit ids as opposed to Mastodon's 64 bits. However just like Mastodon's ids they are sortable strings
|
||||||
|
|
||||||
|
## Attachment cap
|
||||||
|
|
||||||
|
Some apps operate under the assumption that no more than 4 attachments can be returned or uploaded. Pleroma however does not enforce any limits on attachment count neither when returning the status object nor when posting.
|
||||||
|
|
||||||
|
## Timelines
|
||||||
|
|
||||||
|
Adding the parameter `with_muted=true` to the timeline queries will also return activities by muted (not by blocked!) users.
|
||||||
|
|
||||||
|
## Statuses
|
||||||
|
|
||||||
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
|
- `local`: true if the post was made on the local instance.
|
||||||
|
- `conversation_id`: the ID of the conversation the status is associated with (if any)
|
||||||
|
- `content`: a map consisting of alternate representations of the `content` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||||
|
- `spoiler_text`: a map consisting of alternate representations of the `spoiler_text` property with the key being it's mimetype. Currently the only alternate representation supported is `text/plain`
|
||||||
|
|
||||||
|
## Attachments
|
||||||
|
|
||||||
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
|
- `mime_type`: mime type of the attachment.
|
||||||
|
|
||||||
|
## Accounts
|
||||||
|
|
||||||
|
- `/api/v1/accounts/:id`: The `id` parameter can also be the `nickname` of the user. This only works in this endpoint, not the deeper nested ones for following etc.
|
||||||
|
|
||||||
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
|
- `tags`: Lists an array of tags for the user
|
||||||
|
- `relationship{}`: Includes fields as documented for Mastodon API https://docs.joinmastodon.org/api/entities/#relationship
|
||||||
|
- `is_moderator`: boolean, true if user is a moderator
|
||||||
|
- `is_admin`: boolean, true if user is an admin
|
||||||
|
- `confirmation_pending`: boolean, true if a new user account is waiting on email confirmation to be activated
|
||||||
|
|
||||||
|
## Notifications
|
||||||
|
|
||||||
|
Has these additional fields under the `pleroma` object:
|
||||||
|
|
||||||
|
- `is_seen`: true if the notification was read by the user
|
||||||
|
|
||||||
|
## POST `/api/v1/statuses`
|
||||||
|
|
||||||
|
Additional parameters can be added to the JSON body/Form data:
|
||||||
|
|
||||||
|
- `preview`: boolean, if set to `true` the post won't be actually posted, but the status entitiy would still be rendered back. This could be useful for previewing rich text/custom emoji, for example.
|
|
@ -0,0 +1,199 @@
|
||||||
|
# Pleroma API
|
||||||
|
|
||||||
|
Requests that require it can be authenticated with [an OAuth token](https://tools.ietf.org/html/rfc6749), the `_pleroma_key` cookie, or [HTTP Basic Authentication](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization).
|
||||||
|
|
||||||
|
Request parameters can be passed via [query strings](https://en.wikipedia.org/wiki/Query_string) or as [form data](https://www.w3.org/TR/html401/interact/forms.html). Files must be uploaded as `multipart/form-data`.
|
||||||
|
|
||||||
|
## `/api/pleroma/emoji`
|
||||||
|
### Lists the custom emoji on that server.
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"girlpower": {
|
||||||
|
"tags": [
|
||||||
|
"Finmoji"
|
||||||
|
],
|
||||||
|
"image_url": "/finmoji/128px/girlpower-128.png"
|
||||||
|
},
|
||||||
|
"education": {
|
||||||
|
"tags": [
|
||||||
|
"Finmoji"
|
||||||
|
],
|
||||||
|
"image_url": "/finmoji/128px/education-128.png"
|
||||||
|
},
|
||||||
|
"finnishlove": {
|
||||||
|
"tags": [
|
||||||
|
"Finmoji"
|
||||||
|
],
|
||||||
|
"image_url": "/finmoji/128px/finnishlove-128.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
* Note: Same data as Mastodon API’s `/api/v1/custom_emojis` but in a different format
|
||||||
|
|
||||||
|
## `/api/pleroma/follow_import`
|
||||||
|
### Imports your follows, for example from a Mastodon CSV file.
|
||||||
|
* Method: `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `list`: STRING or FILE containing a whitespace-separated list of accounts to follow
|
||||||
|
* Response: HTTP 200 on success, 500 on error
|
||||||
|
* Note: Users that can't be followed are silently skipped.
|
||||||
|
|
||||||
|
## `/api/pleroma/captcha`
|
||||||
|
### Get a new captcha
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: Provider specific JSON, the only guaranteed parameter is `type`
|
||||||
|
* Example response: `{"type": "kocaptcha", "token": "whatever", "url": "https://captcha.kotobank.ch/endpoint"}`
|
||||||
|
|
||||||
|
## `/api/pleroma/delete_account`
|
||||||
|
### Delete an account
|
||||||
|
* Method `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `password`: user's password
|
||||||
|
* Response: JSON. Returns `{"status": "success"}` if the deletion was successful, `{"error": "[error message]"}` otherwise
|
||||||
|
* Example response: `{"error": "Invalid password."}`
|
||||||
|
|
||||||
|
## `/api/account/register`
|
||||||
|
### Register a new user
|
||||||
|
* Method `POST`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params:
|
||||||
|
* `nickname`
|
||||||
|
* `fullname`
|
||||||
|
* `bio`
|
||||||
|
* `email`
|
||||||
|
* `password`
|
||||||
|
* `confirm`
|
||||||
|
* `captcha_solution`: optional, contains provider-specific captcha solution,
|
||||||
|
* `captcha_token`: optional, contains provider-specific captcha token
|
||||||
|
* `token`: invite token required when the registrations aren't public.
|
||||||
|
* Response: JSON. Returns a user object on success, otherwise returns `{"error": "error_msg"}`
|
||||||
|
* Example response:
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"background_image": null,
|
||||||
|
"cover_photo": "https://pleroma.soykaf.com/images/banner.png",
|
||||||
|
"created_at": "Tue Dec 18 16:55:56 +0000 2018",
|
||||||
|
"default_scope": "public",
|
||||||
|
"description": "blushy-crushy fediverse idol + pleroma dev\nlet's be friends \nぷれろまの生徒会長。謎の外人。日本語OK. \n公主病.",
|
||||||
|
"description_html": "blushy-crushy fediverse idol + pleroma dev.<br />let's be friends <br />ぷれろまの生徒会長。謎の外人。日本語OK. <br />公主病.",
|
||||||
|
"favourites_count": 0,
|
||||||
|
"fields": [],
|
||||||
|
"followers_count": 0,
|
||||||
|
"following": false,
|
||||||
|
"follows_you": false,
|
||||||
|
"friends_count": 0,
|
||||||
|
"id": 6,
|
||||||
|
"is_local": true,
|
||||||
|
"locked": false,
|
||||||
|
"name": "lain",
|
||||||
|
"name_html": "lain",
|
||||||
|
"no_rich_text": false,
|
||||||
|
"pleroma": {
|
||||||
|
"tags": []
|
||||||
|
},
|
||||||
|
"profile_image_url": "https://pleroma.soykaf.com/images/avi.png",
|
||||||
|
"profile_image_url_https": "https://pleroma.soykaf.com/images/avi.png",
|
||||||
|
"profile_image_url_original": "https://pleroma.soykaf.com/images/avi.png",
|
||||||
|
"profile_image_url_profile_size": "https://pleroma.soykaf.com/images/avi.png",
|
||||||
|
"rights": {
|
||||||
|
"delete_others_notice": false
|
||||||
|
},
|
||||||
|
"screen_name": "lain",
|
||||||
|
"statuses_count": 0,
|
||||||
|
"statusnet_blocking": false,
|
||||||
|
"statusnet_profile_url": "https://pleroma.soykaf.com/users/lain"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/admin/`…
|
||||||
|
See [Admin-API](Admin-API.md)
|
||||||
|
|
||||||
|
## `/api/v1/pleroma/flavour/:flavour`
|
||||||
|
* Method `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Response: JSON string. Returns the user flavour or the default one on success, otherwise returns `{"error": "error_msg"}`
|
||||||
|
* Example response: "glitch"
|
||||||
|
* Note: This is intended to be used only by mastofe
|
||||||
|
|
||||||
|
## `/api/v1/pleroma/flavour`
|
||||||
|
* Method `GET`
|
||||||
|
* Authentication: required
|
||||||
|
* Response: JSON string. Returns the user flavour or the default one.
|
||||||
|
* Example response: "glitch"
|
||||||
|
* Note: This is intended to be used only by mastofe
|
||||||
|
|
||||||
|
## `/api/pleroma/notifications/read`
|
||||||
|
### Mark a single notification as read
|
||||||
|
* Method `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `id`: notification's id
|
||||||
|
* Response: JSON. Returns `{"status": "success"}` if the reading was successful, otherwise returns `{"error": "error_msg"}`
|
||||||
|
|
||||||
|
## `/api/v1/pleroma/accounts/:id/subscribe`
|
||||||
|
### Subscribe to receive notifications for all statuses posted by a user
|
||||||
|
* Method `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `id`: account id to subscribe to
|
||||||
|
* Response: JSON, returns a mastodon relationship object on success, otherwise returns `{"error": "error_msg"}`
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "abcdefg",
|
||||||
|
"following": true,
|
||||||
|
"followed_by": false,
|
||||||
|
"blocking": false,
|
||||||
|
"muting": false,
|
||||||
|
"muting_notifications": false,
|
||||||
|
"subscribing": true,
|
||||||
|
"requested": false,
|
||||||
|
"domain_blocking": false,
|
||||||
|
"showing_reblogs": true,
|
||||||
|
"endorsed": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/v1/pleroma/accounts/:id/unsubscribe`
|
||||||
|
### Unsubscribe to stop receiving notifications from user statuses
|
||||||
|
* Method `POST`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `id`: account id to unsubscribe from
|
||||||
|
* Response: JSON, returns a mastodon relationship object on success, otherwise returns `{"error": "error_msg"}`
|
||||||
|
* Example response:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "abcdefg",
|
||||||
|
"following": true,
|
||||||
|
"followed_by": false,
|
||||||
|
"blocking": false,
|
||||||
|
"muting": false,
|
||||||
|
"muting_notifications": false,
|
||||||
|
"subscribing": false,
|
||||||
|
"requested": false,
|
||||||
|
"domain_blocking": false,
|
||||||
|
"showing_reblogs": true,
|
||||||
|
"endorsed": false
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## `/api/pleroma/notification_settings`
|
||||||
|
### Updates user notification settings
|
||||||
|
* Method `PUT`
|
||||||
|
* Authentication: required
|
||||||
|
* Params:
|
||||||
|
* `followers`: BOOLEAN field, receives notifications from followers
|
||||||
|
* `follows`: BOOLEAN field, receives notifications from people the user follows
|
||||||
|
* `remote`: BOOLEAN field, receives notifications from people on remote instances
|
||||||
|
* `local`: BOOLEAN field, receives notifications from people on the local instance
|
||||||
|
* Response: JSON. Returns `{"status": "success"}` if the update was successful, otherwise returns `{"error": "error_msg"}`
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Prometheus Metrics
|
||||||
|
|
||||||
|
Pleroma includes support for exporting metrics via the [prometheus_ex](https://github.com/deadtrickster/prometheus.ex) library.
|
||||||
|
|
||||||
|
## `/api/pleroma/app_metrics`
|
||||||
|
### Exports Prometheus application metrics
|
||||||
|
* Method: `GET`
|
||||||
|
* Authentication: not required
|
||||||
|
* Params: none
|
||||||
|
* Response: JSON
|
||||||
|
|
||||||
|
## Grafana
|
||||||
|
### Config example
|
||||||
|
The following is a config example to use with [Grafana](https://grafana.com)
|
||||||
|
|
||||||
|
```
|
||||||
|
- job_name: 'beam'
|
||||||
|
metrics_path: /api/pleroma/app_metrics
|
||||||
|
scheme: https
|
||||||
|
static_configs:
|
||||||
|
- targets: ['pleroma.soykaf.com']
|
||||||
|
```
|
|
@ -0,0 +1,100 @@
|
||||||
|
# Pleroma Clients
|
||||||
|
Note: Additionnal clients may be working but theses are officially supporting Pleroma.
|
||||||
|
Feel free to contact us to be added to this list!
|
||||||
|
|
||||||
|
## Desktop
|
||||||
|
### Roma for Desktop
|
||||||
|
- Homepage: <https://www.pleroma.com/#desktopApp>
|
||||||
|
- Source Code: <https://github.com/roma-apps/roma-desktop>
|
||||||
|
- Platforms: Windows, Mac, (Linux?)
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
### Social
|
||||||
|
- Source Code: <https://gitlab.gnome.org/BrainBlasted/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
|
||||||
|
|
||||||
|
### Whalebird
|
||||||
|
- Homepage: <https://whalebird.org/>
|
||||||
|
- Source Code: <https://github.com/h3poteto/whalebird-desktop>
|
||||||
|
- Contact: [@h3poteto@pleroma.io](https://pleroma.io/users/h3poteto)
|
||||||
|
- Platforms: Windows, Mac, Linux
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
## Handheld
|
||||||
|
### 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: No Streaming
|
||||||
|
|
||||||
|
### Fedilab
|
||||||
|
- Source Code: <https://gitlab.com/tom79/mastalab/>
|
||||||
|
- Contact: [@tom79@mastodon.social](https://mastodon.social/users/tom79)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
### Nekonium
|
||||||
|
- Homepage: [F-Droid Repository](https://repo.gdgd.jp.net/), [Google Play](https://play.google.com/store/apps/details?id=com.apps.nekonium), [Amazon](https://www.amazon.co.jp/dp/B076FXPRBC/)
|
||||||
|
- Source: <https://git.gdgd.jp.net/lin/nekonium/>
|
||||||
|
- Contact: [@lin@pleroma.gdgd.jp.net](https://pleroma.gdgd.jp.net/users/lin)
|
||||||
|
- Platforms: Android
|
||||||
|
- Features: Streaming Ready
|
||||||
|
|
||||||
|
### Roma
|
||||||
|
- Homepage: <https://www.pleroma.com/#mobileApps>
|
||||||
|
- Source Code: [iOS](https://github.com/roma-apps/roma-ios), [Android](https://github.com/roma-apps/roma-android)
|
||||||
|
- Platforms: iOS, Android
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Tootdon
|
||||||
|
- Homepage: <http://tootdon.club/>, <http://blog.mastodon-tootdon.com/>
|
||||||
|
- Source Code: ???
|
||||||
|
- Contact: [@tootdon@mstdn.jp](https://mstdn.jp/users/tootdon)
|
||||||
|
- Platforms: Android, iOS
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### 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: No Streaming
|
||||||
|
|
||||||
|
### Twidere
|
||||||
|
- Homepage: <https://twidere.mariotaku.org/>
|
||||||
|
- Source Code: <https://github.com/TwidereProject/Twidere-Android/>, <https://github.com/TwidereProject/Twidere-iOS/>
|
||||||
|
- Contact: <me@mariotaku.org>
|
||||||
|
- Platform: Android, iOS
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
## Alternative Web Interfaces
|
||||||
|
### Brutaldon
|
||||||
|
- Homepage: <https://jfm.carcosa.net/projects/software/brutaldon/>
|
||||||
|
- Source Code: <https://github.com/jfmcbrayer/brutaldon>
|
||||||
|
- Contact: [@gcupc@glitch.social](https://glitch.social/users/gcupc)
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Feather
|
||||||
|
- Source Code: <https://github.com/kaniini/feather>
|
||||||
|
- Contact: [@kaniini@pleroma.site](https://pleroma.site/kaniini)
|
||||||
|
- Features: No Streaming
|
||||||
|
|
||||||
|
### Halcyon
|
||||||
|
- Source Code: <https://notabug.org/halcyon-suite/halcyon>
|
||||||
|
- Contact: [@halcyon@social.csswg.org](https://social.csswg.org/users/halcyon)
|
||||||
|
- Features: 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: No Streaming
|
||||||
|
|
||||||
|
### Sengi
|
||||||
|
- Source Code: <https://github.com/NicolasConstant/sengi>
|
||||||
|
- Contact: [@sengi_app@mastodon.social](https://mastodon.social/users/sengi_app)
|
||||||
|
- Note(2019-01-28): The development is currently in a early stage.
|
|
@ -0,0 +1,488 @@
|
||||||
|
# Configuration
|
||||||
|
|
||||||
|
This file describe the configuration, it is recommended to edit the relevant *.secret.exs file instead of the others founds in the ``config`` directory.
|
||||||
|
If you run Pleroma with ``MIX_ENV=prod`` the file is ``prod.secret.exs``, otherwise it is ``dev.secret.exs``.
|
||||||
|
|
||||||
|
## Pleroma.Upload
|
||||||
|
* `uploader`: Select which `Pleroma.Uploaders` to use
|
||||||
|
* `filters`: List of `Pleroma.Upload.Filter` to use.
|
||||||
|
* `link_name`: When enabled Pleroma will add a `name` parameter to the url of the upload, for example `https://instance.tld/media/corndog.png?name=corndog.png`. This is needed to provide the correct filename in Content-Disposition headers when using filters like `Pleroma.Upload.Filter.Dedupe`
|
||||||
|
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host.
|
||||||
|
* `proxy_remote`: If you\'re using a remote uploader, Pleroma will proxy media requests instead of redirecting to it.
|
||||||
|
* `proxy_opts`: Proxy options, see `Pleroma.ReverseProxy` documentation.
|
||||||
|
|
||||||
|
Note: `strip_exif` has been replaced by `Pleroma.Upload.Filter.Mogrify`.
|
||||||
|
|
||||||
|
## Pleroma.Uploaders.Local
|
||||||
|
* `uploads`: Which directory to store the user-uploads in, relative to pleroma’s working directory
|
||||||
|
|
||||||
|
## Pleroma.Upload.Filter.Mogrify
|
||||||
|
|
||||||
|
* `args`: List of actions for the `mogrify` command like `"strip"` or `["strip", "auto-orient", {"impode", "1"}]`.
|
||||||
|
|
||||||
|
## Pleroma.Upload.Filter.Dedupe
|
||||||
|
|
||||||
|
No specific configuration.
|
||||||
|
|
||||||
|
## Pleroma.Upload.Filter.AnonymizeFilename
|
||||||
|
|
||||||
|
This filter replaces the filename (not the path) of an upload. For complete obfuscation, add
|
||||||
|
`Pleroma.Upload.Filter.Dedupe` before AnonymizeFilename.
|
||||||
|
|
||||||
|
* `text`: Text to replace filenames in links. If empty, `{random}.extension` will be used.
|
||||||
|
|
||||||
|
## Pleroma.Emails.Mailer
|
||||||
|
* `adapter`: one of the mail adapters listed in [Swoosh readme](https://github.com/swoosh/swoosh#adapters), or `Swoosh.Adapters.Local` for in-memory mailbox.
|
||||||
|
* `api_key` / `password` and / or other adapter-specific settings, per the above documentation.
|
||||||
|
|
||||||
|
An example for Sendgrid adapter:
|
||||||
|
|
||||||
|
```exs
|
||||||
|
config :pleroma, Pleroma.Emails.Mailer,
|
||||||
|
adapter: Swoosh.Adapters.Sendgrid,
|
||||||
|
api_key: "YOUR_API_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
An example for SMTP adapter:
|
||||||
|
|
||||||
|
```exs
|
||||||
|
config :pleroma, Pleroma.Emails.Mailer,
|
||||||
|
adapter: Swoosh.Adapters.SMTP,
|
||||||
|
relay: "smtp.gmail.com",
|
||||||
|
username: "YOUR_USERNAME@gmail.com",
|
||||||
|
password: "YOUR_SMTP_PASSWORD",
|
||||||
|
port: 465,
|
||||||
|
ssl: true,
|
||||||
|
tls: :always,
|
||||||
|
auth: :always
|
||||||
|
```
|
||||||
|
|
||||||
|
## :uri_schemes
|
||||||
|
* `valid_schemes`: List of the scheme part that is considered valid to be an URL
|
||||||
|
|
||||||
|
## :instance
|
||||||
|
* `name`: The instance’s name
|
||||||
|
* `email`: Email used to reach an Administrator/Moderator of the instance
|
||||||
|
* `notify_email`: Email used for notifications.
|
||||||
|
* `description`: The instance’s description, can be seen in nodeinfo and ``/api/v1/instance``
|
||||||
|
* `limit`: Posts character limit (CW/Subject included in the counter)
|
||||||
|
* `remote_limit`: Hard character limit beyond which remote posts will be dropped.
|
||||||
|
* `upload_limit`: File size limit of uploads (except for avatar, background, banner)
|
||||||
|
* `avatar_upload_limit`: File size limit of user’s profile avatars
|
||||||
|
* `background_upload_limit`: File size limit of user’s profile backgrounds
|
||||||
|
* `banner_upload_limit`: File size limit of user’s profile banners
|
||||||
|
* `registrations_open`: Enable registrations for anyone, invitations can be enabled when false.
|
||||||
|
* `invites_enabled`: Enable user invitations for admins (depends on `registrations_open: false`).
|
||||||
|
* `account_activation_required`: Require users to confirm their emails before signing in.
|
||||||
|
* `federating`: Enable federation with other instances
|
||||||
|
* `federation_reachability_timeout_days`: Timeout (in days) of each external federation target being unreachable prior to pausing federating to it.
|
||||||
|
* `allow_relay`: Enable Pleroma’s Relay, which makes it possible to follow a whole instance
|
||||||
|
* `rewrite_policy`: Message Rewrite Policy, either one or a list. Here are the ones available by default:
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.NoOpPolicy`: Doesn’t modify activities (default)
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.DropPolicy`: Drops all activities. It generally doesn’t makes sense to use in production
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.SimplePolicy`: Restrict the visibility of activities from certains instances (See ``:mrf_simple`` section)
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.RejectNonPublic`: Drops posts with non-public visibility settings (See ``:mrf_rejectnonpublic`` section)
|
||||||
|
* `Pleroma.Web.ActivityPub.MRF.EnsureRePrepended`: Rewrites posts to ensure that replies to posts with subjects do not have an identical subject and instead begin with re:.
|
||||||
|
* `public`: Makes the client API in authentificated mode-only except for user-profiles. Useful for disabling the Local Timeline and The Whole Known Network.
|
||||||
|
* `quarantined_instances`: List of ActivityPub instances where private(DMs, followers-only) activities will not be send.
|
||||||
|
* `managed_config`: Whenether the config for pleroma-fe is configured in this config or in ``static/config.json``
|
||||||
|
* `allowed_post_formats`: MIME-type list of formats allowed to be posted (transformed into HTML)
|
||||||
|
* `finmoji_enabled`: Whenether to enable the finmojis in the custom emojis.
|
||||||
|
* `mrf_transparency`: Make the content of your Message Rewrite Facility settings public (via nodeinfo).
|
||||||
|
* `scope_copy`: Copy the scope (private/unlisted/public) in replies to posts by default.
|
||||||
|
* `subject_line_behavior`: Allows changing the default behaviour of subject lines in replies. Valid values:
|
||||||
|
* "email": Copy and preprend re:, as in email.
|
||||||
|
* "masto": Copy verbatim, as in Mastodon.
|
||||||
|
* "noop": Don't copy the subject.
|
||||||
|
* `always_show_subject_input`: When set to false, auto-hide the subject field when it's empty.
|
||||||
|
* `extended_nickname_format`: Set to `true` to use extended local nicknames format (allows underscores/dashes). This will break federation with
|
||||||
|
older software for theses nicknames.
|
||||||
|
* `max_pinned_statuses`: The maximum number of pinned statuses. `0` will disable the feature.
|
||||||
|
* `autofollowed_nicknames`: Set to nicknames of (local) users that every new user should automatically follow.
|
||||||
|
* `no_attachment_links`: Set to true to disable automatically adding attachment link text to statuses
|
||||||
|
* `welcome_message`: A message that will be send to a newly registered users as a direct message.
|
||||||
|
* `welcome_user_nickname`: The nickname of the local user that sends the welcome message.
|
||||||
|
* `max_report_comment_size`: The maximum size of the report comment (Default: `1000`)
|
||||||
|
* `safe_dm_mentions`: If set to true, only mentions at the beginning of a post will be used to address people in direct messages. This is to prevent accidental mentioning of people when talking about them (e.g. "@friend hey i really don't like @enemy"). (Default: `false`)
|
||||||
|
|
||||||
|
## :logger
|
||||||
|
* `backends`: `:console` is used to send logs to stdout, `{ExSyslogger, :ex_syslogger}` to log to syslog, and `Quack.Logger` to log to Slack
|
||||||
|
|
||||||
|
An example to enable ONLY ExSyslogger (f/ex in ``prod.secret.exs``) with info and debug suppressed:
|
||||||
|
```
|
||||||
|
config :logger,
|
||||||
|
backends: [{ExSyslogger, :ex_syslogger}]
|
||||||
|
|
||||||
|
config :logger, :ex_syslogger,
|
||||||
|
level: :warn
|
||||||
|
```
|
||||||
|
|
||||||
|
Another example, keeping console output and adding the pid to syslog output:
|
||||||
|
```
|
||||||
|
config :logger,
|
||||||
|
backends: [:console, {ExSyslogger, :ex_syslogger}]
|
||||||
|
|
||||||
|
config :logger, :ex_syslogger,
|
||||||
|
level: :warn,
|
||||||
|
option: [:pid, :ndelay]
|
||||||
|
```
|
||||||
|
|
||||||
|
See: [logger’s documentation](https://hexdocs.pm/logger/Logger.html) and [ex_syslogger’s documentation](https://hexdocs.pm/ex_syslogger/)
|
||||||
|
|
||||||
|
An example of logging info to local syslog, but warn to a Slack channel:
|
||||||
|
```
|
||||||
|
config :logger,
|
||||||
|
backends: [ {ExSyslogger, :ex_syslogger}, Quack.Logger ],
|
||||||
|
level: :info
|
||||||
|
|
||||||
|
config :logger, :ex_syslogger,
|
||||||
|
level: :info,
|
||||||
|
ident: "pleroma",
|
||||||
|
format: "$metadata[$level] $message"
|
||||||
|
|
||||||
|
config :quack,
|
||||||
|
level: :warn,
|
||||||
|
meta: [:all],
|
||||||
|
webhook_url: "https://hooks.slack.com/services/YOUR-API-KEY-HERE"
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [Quack Github](https://github.com/azohra/quack) for more details
|
||||||
|
|
||||||
|
## :frontend_configurations
|
||||||
|
|
||||||
|
This can be used to configure a keyword list that keeps the configuration data for any kind of frontend. By default, settings for `pleroma_fe` and `masto_fe` are configured.
|
||||||
|
|
||||||
|
Frontends can access these settings at `/api/pleroma/frontend_configurations`
|
||||||
|
|
||||||
|
To add your own configuration for PleromaFE, use it like this:
|
||||||
|
|
||||||
|
`config :pleroma, :frontend_configurations, pleroma_fe: %{redirectRootNoLogin: "/main/all", ...}`
|
||||||
|
|
||||||
|
These settings need to be complete, they will override the defaults. See `priv/static/static/config.json` for the available keys.
|
||||||
|
|
||||||
|
## :fe
|
||||||
|
__THIS IS DEPRECATED__
|
||||||
|
|
||||||
|
If you are using this method, please change it to the `frontend_configurations` method. Please set this option to false in your config like this: `config :pleroma, :fe, false`.
|
||||||
|
|
||||||
|
This section is used to configure Pleroma-FE, unless ``:managed_config`` in ``:instance`` is set to false.
|
||||||
|
|
||||||
|
* `theme`: Which theme to use, they are defined in ``styles.json``
|
||||||
|
* `logo`: URL of the logo, defaults to Pleroma’s logo
|
||||||
|
* `logo_mask`: Whether to use only the logo's shape as a mask (true) or as a regular image (false)
|
||||||
|
* `logo_margin`: What margin to use around the logo
|
||||||
|
* `background`: URL of the background, unless viewing a user profile with a background that is set
|
||||||
|
* `redirect_root_no_login`: relative URL which indicates where to redirect when a user isn’t logged in.
|
||||||
|
* `redirect_root_login`: relative URL which indicates where to redirect when a user is logged in.
|
||||||
|
* `show_instance_panel`: Whenether to show the instance’s specific panel.
|
||||||
|
* `scope_options_enabled`: Enable setting an notice visibility and subject/CW when posting
|
||||||
|
* `formatting_options_enabled`: Enable setting a formatting different than plain-text (ie. HTML, Markdown) when posting, relates to ``:instance, allowed_post_formats``
|
||||||
|
* `collapse_message_with_subjects`: When a message has a subject(aka Content Warning), collapse it by default
|
||||||
|
* `hide_post_stats`: Hide notices statistics(repeats, favorites, …)
|
||||||
|
* `hide_user_stats`: Hide profile statistics(posts, posts per day, followers, followings, …)
|
||||||
|
|
||||||
|
## :mrf_simple
|
||||||
|
* `media_removal`: List of instances to remove medias from
|
||||||
|
* `media_nsfw`: List of instances to put medias as NSFW(sensitive) from
|
||||||
|
* `federated_timeline_removal`: List of instances to remove from Federated (aka The Whole Known Network) Timeline
|
||||||
|
* `reject`: List of instances to reject any activities from
|
||||||
|
* `accept`: List of instances to accept any activities from
|
||||||
|
|
||||||
|
## :mrf_rejectnonpublic
|
||||||
|
* `allow_followersonly`: whether to allow followers-only posts
|
||||||
|
* `allow_direct`: whether to allow direct messages
|
||||||
|
|
||||||
|
## :mrf_hellthread
|
||||||
|
* `delist_threshold`: Number of mentioned users after which the message gets delisted (the message can still be seen, but it will not show up in public timelines and mentioned users won't get notifications about it). Set to 0 to disable.
|
||||||
|
* `reject_threshold`: Number of mentioned users after which the messaged gets rejected. Set to 0 to disable.
|
||||||
|
|
||||||
|
## :mrf_keyword
|
||||||
|
* `reject`: A list of patterns which result in message being rejected, each pattern can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html)
|
||||||
|
* `federated_timeline_removal`: A list of patterns which result in message being removed from federated timelines (a.k.a unlisted), each pattern can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html)
|
||||||
|
* `replace`: A list of tuples containing `{pattern, replacement}`, `pattern` can be a string or a [regular expression](https://hexdocs.pm/elixir/Regex.html)
|
||||||
|
|
||||||
|
## :media_proxy
|
||||||
|
* `enabled`: Enables proxying of remote media to the instance’s proxy
|
||||||
|
* `base_url`: The base URL to access a user-uploaded file. Useful when you want to proxy the media files via another host/CDN fronts.
|
||||||
|
* `proxy_opts`: All options defined in `Pleroma.ReverseProxy` documentation, defaults to `[max_body_length: (25*1_048_576)]`.
|
||||||
|
|
||||||
|
## :gopher
|
||||||
|
* `enabled`: Enables the gopher interface
|
||||||
|
* `ip`: IP address to bind to
|
||||||
|
* `port`: Port to bind to
|
||||||
|
* `dstport`: Port advertised in urls (optional, defaults to `port`)
|
||||||
|
|
||||||
|
## Pleroma.Web.Endpoint
|
||||||
|
`Phoenix` endpoint configuration, all configuration options can be viewed [here](https://hexdocs.pm/phoenix/Phoenix.Endpoint.html#module-dynamic-configuration), only common options are listed here
|
||||||
|
* `http` - a list containing http protocol configuration, all configuration options can be viewed [here](https://hexdocs.pm/plug_cowboy/Plug.Cowboy.html#module-options), only common options are listed here
|
||||||
|
- `ip` - a tuple consisting of 4 integers
|
||||||
|
- `port`
|
||||||
|
* `url` - a list containing the configuration for generating urls, accepts
|
||||||
|
- `host` - the host without the scheme and a post (e.g `example.com`, not `https://example.com:2020`)
|
||||||
|
- `scheme` - e.g `http`, `https`
|
||||||
|
- `port`
|
||||||
|
- `path`
|
||||||
|
* `extra_cookie_attrs` - a list of `Key=Value` strings to be added as non-standard cookie attributes. Defaults to `["SameSite=Lax"]`. See the [SameSite article](https://www.owasp.org/index.php/SameSite) on OWASP for more info.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
**Important note**: if you modify anything inside these lists, default `config.exs` values will be overwritten, which may result in breakage, to make sure this does not happen please copy the default value for the list from `config.exs` and modify/add only what you need
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```elixir
|
||||||
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
|
url: [host: "example.com", port: 2020, scheme: "https"],
|
||||||
|
http: [
|
||||||
|
# start copied from config.exs
|
||||||
|
dispatch: [
|
||||||
|
{:_,
|
||||||
|
[
|
||||||
|
{"/api/v1/streaming", Pleroma.Web.MastodonAPI.WebsocketHandler, []},
|
||||||
|
{"/websocket", Phoenix.Endpoint.CowboyWebSocket,
|
||||||
|
{Phoenix.Transports.WebSocket,
|
||||||
|
{Pleroma.Web.Endpoint, Pleroma.Web.UserSocket, websocket_config}}},
|
||||||
|
{:_, Phoenix.Endpoint.Cowboy2Handler, {Pleroma.Web.Endpoint, []}}
|
||||||
|
]}
|
||||||
|
# end copied from config.exs
|
||||||
|
],
|
||||||
|
port: 8080,
|
||||||
|
ip: {127, 0, 0, 1}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
This will make Pleroma listen on `127.0.0.1` port `8080` and generate urls starting with `https://example.com:2020`
|
||||||
|
|
||||||
|
## :activitypub
|
||||||
|
* ``accept_blocks``: Whether to accept incoming block activities from other instances
|
||||||
|
* ``unfollow_blocked``: Whether blocks result in people getting unfollowed
|
||||||
|
* ``outgoing_blocks``: Whether to federate blocks to other instances
|
||||||
|
* ``deny_follow_blocked``: Whether to disallow following an account that has blocked the user in question
|
||||||
|
|
||||||
|
## :http_security
|
||||||
|
* ``enabled``: Whether the managed content security policy is enabled
|
||||||
|
* ``sts``: Whether to additionally send a `Strict-Transport-Security` header
|
||||||
|
* ``sts_max_age``: The maximum age for the `Strict-Transport-Security` header if sent
|
||||||
|
* ``ct_max_age``: The maximum age for the `Expect-CT` header if sent
|
||||||
|
* ``referrer_policy``: The referrer policy to use, either `"same-origin"` or `"no-referrer"`.
|
||||||
|
|
||||||
|
## :mrf_user_allowlist
|
||||||
|
|
||||||
|
The keys in this section are the domain names that the policy should apply to.
|
||||||
|
Each key should be assigned a list of users that should be allowed through by
|
||||||
|
their ActivityPub ID.
|
||||||
|
|
||||||
|
An example:
|
||||||
|
|
||||||
|
```exs
|
||||||
|
config :pleroma, :mrf_user_allowlist,
|
||||||
|
"example.org": ["https://example.org/users/admin"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## :web_push_encryption, :vapid_details
|
||||||
|
|
||||||
|
Web Push Notifications configuration. You can use the mix task `mix web_push.gen.keypair` to generate it.
|
||||||
|
|
||||||
|
* ``subject``: a mailto link for the administrative contact. It’s best if this email is not a personal email address, but rather a group email so that if a person leaves an organization, is unavailable for an extended period, or otherwise can’t respond, someone else on the list can.
|
||||||
|
* ``public_key``: VAPID public key
|
||||||
|
* ``private_key``: VAPID private key
|
||||||
|
|
||||||
|
## Pleroma.Captcha
|
||||||
|
* `enabled`: Whether the captcha should be shown on registration
|
||||||
|
* `method`: The method/service to use for captcha
|
||||||
|
* `seconds_valid`: The time in seconds for which the captcha is valid
|
||||||
|
|
||||||
|
### Pleroma.Captcha.Kocaptcha
|
||||||
|
Kocaptcha is a very simple captcha service with a single API endpoint,
|
||||||
|
the source code is here: https://github.com/koto-bank/kocaptcha. The default endpoint
|
||||||
|
`https://captcha.kotobank.ch` is hosted by the developer.
|
||||||
|
|
||||||
|
* `endpoint`: the kocaptcha endpoint to use
|
||||||
|
|
||||||
|
## :admin_token
|
||||||
|
|
||||||
|
Allows to set a token that can be used to authenticate with the admin api without using an actual user by giving it as the 'admin_token' parameter. Example:
|
||||||
|
|
||||||
|
```exs
|
||||||
|
config :pleroma, :admin_token, "somerandomtoken"
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then do
|
||||||
|
|
||||||
|
```sh
|
||||||
|
curl "http://localhost:4000/api/pleroma/admin/invite_token?admin_token=somerandomtoken"
|
||||||
|
```
|
||||||
|
|
||||||
|
## :pleroma_job_queue
|
||||||
|
|
||||||
|
[Pleroma Job Queue](https://git.pleroma.social/pleroma/pleroma_job_queue) configuration: a list of queues with maximum concurrent jobs.
|
||||||
|
|
||||||
|
Pleroma has the following queues:
|
||||||
|
|
||||||
|
* `federator_outgoing` - Outgoing federation
|
||||||
|
* `federator_incoming` - Incoming federation
|
||||||
|
* `mailer` - Email sender, see [`Pleroma.Emails.Mailer`](#pleroma-emails-mailer)
|
||||||
|
* `transmogrifier` - Transmogrifier
|
||||||
|
* `web_push` - Web push notifications
|
||||||
|
* `scheduled_activities` - Scheduled activities, see [`Pleroma.ScheduledActivities`](#pleromascheduledactivity)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
config :pleroma_job_queue, :queues,
|
||||||
|
federator_incoming: 50,
|
||||||
|
federator_outgoing: 50
|
||||||
|
```
|
||||||
|
|
||||||
|
This config contains two queues: `federator_incoming` and `federator_outgoing`. Both have the `max_jobs` set to `50`.
|
||||||
|
|
||||||
|
## Pleroma.Web.Federator.RetryQueue
|
||||||
|
|
||||||
|
* `enabled`: If set to `true`, failed federation jobs will be retried
|
||||||
|
* `max_jobs`: The maximum amount of parallel federation jobs running at the same time.
|
||||||
|
* `initial_timeout`: The initial timeout in seconds
|
||||||
|
* `max_retries`: The maximum number of times a federation job is retried
|
||||||
|
|
||||||
|
## Pleroma.Web.Metadata
|
||||||
|
* `providers`: a list of metadata providers to enable. Providers availible:
|
||||||
|
* Pleroma.Web.Metadata.Providers.OpenGraph
|
||||||
|
* Pleroma.Web.Metadata.Providers.TwitterCard
|
||||||
|
* `unfurl_nsfw`: If set to `true` nsfw attachments will be shown in previews
|
||||||
|
|
||||||
|
## :rich_media
|
||||||
|
* `enabled`: if enabled the instance will parse metadata from attached links to generate link previews
|
||||||
|
|
||||||
|
## :fetch_initial_posts
|
||||||
|
* `enabled`: if enabled, when a new user is federated with, fetch some of their latest posts
|
||||||
|
* `pages`: the amount of pages to fetch
|
||||||
|
|
||||||
|
## :hackney_pools
|
||||||
|
|
||||||
|
Advanced. Tweaks Hackney (http client) connections pools.
|
||||||
|
|
||||||
|
There's three pools used:
|
||||||
|
|
||||||
|
* `:federation` for the federation jobs.
|
||||||
|
You may want this pool max_connections to be at least equal to the number of federator jobs + retry queue jobs.
|
||||||
|
* `:media` for rich media, media proxy
|
||||||
|
* `:upload` for uploaded media (if using a remote uploader and `proxy_remote: true`)
|
||||||
|
|
||||||
|
For each pool, the options are:
|
||||||
|
|
||||||
|
* `max_connections` - how much connections a pool can hold
|
||||||
|
* `timeout` - retention duration for connections
|
||||||
|
|
||||||
|
## :auto_linker
|
||||||
|
|
||||||
|
Configuration for the `auto_linker` library:
|
||||||
|
|
||||||
|
* `class: "auto-linker"` - specify the class to be added to the generated link. false to clear
|
||||||
|
* `rel: "noopener noreferrer"` - override the rel attribute. false to clear
|
||||||
|
* `new_window: true` - set to false to remove `target='_blank'` attribute
|
||||||
|
* `scheme: false` - Set to true to link urls with schema `http://google.com`
|
||||||
|
* `truncate: false` - Set to a number to truncate urls longer then the number. Truncated urls will end in `..`
|
||||||
|
* `strip_prefix: true` - Strip the scheme prefix
|
||||||
|
* `extra: false` - link urls with rarely used schemes (magnet, ipfs, irc, etc.)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```exs
|
||||||
|
config :auto_linker,
|
||||||
|
opts: [
|
||||||
|
scheme: true,
|
||||||
|
extra: true,
|
||||||
|
class: false,
|
||||||
|
strip_prefix: false,
|
||||||
|
new_window: false,
|
||||||
|
rel: false
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pleroma.ScheduledActivity
|
||||||
|
|
||||||
|
* `daily_user_limit`: the number of scheduled activities a user is allowed to create in a single day (Default: `25`)
|
||||||
|
* `total_user_limit`: the number of scheduled activities a user is allowed to create in total (Default: `300`)
|
||||||
|
* `enabled`: whether scheduled activities are sent to the job queue to be executed
|
||||||
|
|
||||||
|
## Pleroma.Web.Auth.Authenticator
|
||||||
|
|
||||||
|
* `Pleroma.Web.Auth.PleromaAuthenticator`: default database authenticator
|
||||||
|
* `Pleroma.Web.Auth.LDAPAuthenticator`: LDAP authentication
|
||||||
|
|
||||||
|
## :ldap
|
||||||
|
|
||||||
|
Use LDAP for user authentication. When a user logs in to the Pleroma
|
||||||
|
instance, the name and password will be verified by trying to authenticate
|
||||||
|
(bind) to an LDAP server. If a user exists in the LDAP directory but there
|
||||||
|
is no account with the same name yet on the Pleroma instance then a new
|
||||||
|
Pleroma account will be created with the same name as the LDAP user name.
|
||||||
|
|
||||||
|
* `enabled`: enables LDAP authentication
|
||||||
|
* `host`: LDAP server hostname
|
||||||
|
* `port`: LDAP port, e.g. 389 or 636
|
||||||
|
* `ssl`: true to use SSL, usually implies the port 636
|
||||||
|
* `sslopts`: additional SSL options
|
||||||
|
* `tls`: true to start TLS, usually implies the port 389
|
||||||
|
* `tlsopts`: additional TLS options
|
||||||
|
* `base`: LDAP base, e.g. "dc=example,dc=com"
|
||||||
|
* `uid`: LDAP attribute name to authenticate the user, e.g. when "cn", the filter will be "cn=username,base"
|
||||||
|
|
||||||
|
## :auth
|
||||||
|
|
||||||
|
Authentication / authorization settings.
|
||||||
|
|
||||||
|
* `auth_template`: authentication form template. By default it's `show.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/show.html.eex`.
|
||||||
|
* `oauth_consumer_template`: OAuth consumer mode authentication form template. By default it's `consumer.html` which corresponds to `lib/pleroma/web/templates/o_auth/o_auth/consumer.html.eex`.
|
||||||
|
* `oauth_consumer_strategies`: the list of enabled OAuth consumer strategies; by default it's set by OAUTH_CONSUMER_STRATEGIES environment variable.
|
||||||
|
|
||||||
|
# OAuth consumer mode
|
||||||
|
|
||||||
|
OAuth consumer mode allows sign in / sign up via external OAuth providers (e.g. Twitter, Facebook, Google, Microsoft, etc.).
|
||||||
|
Implementation is based on Ueberauth; see the list of [available strategies](https://github.com/ueberauth/ueberauth/wiki/List-of-Strategies).
|
||||||
|
|
||||||
|
Note: each strategy is shipped as a separate dependency; in order to get the strategies, run `OAUTH_CONSUMER_STRATEGIES="..." mix deps.get`,
|
||||||
|
e.g. `OAUTH_CONSUMER_STRATEGIES="twitter facebook google microsoft" mix deps.get`.
|
||||||
|
The server should also be started with `OAUTH_CONSUMER_STRATEGIES="..." mix phx.server` in case you enable any strategies.
|
||||||
|
|
||||||
|
Note: each strategy requires separate setup (on external provider side and Pleroma side). Below are the guidelines on setting up most popular strategies.
|
||||||
|
|
||||||
|
Note: make sure that `"SameSite=Lax"` is set in `extra_cookie_attrs` when you have this feature enabled. OAuth consumer mode will not work with `"SameSite=Strict"`
|
||||||
|
|
||||||
|
* For Twitter, [register an app](https://developer.twitter.com/en/apps), configure callback URL to https://<your_host>/oauth/twitter/callback
|
||||||
|
|
||||||
|
* For Facebook, [register an app](https://developers.facebook.com/apps), configure callback URL to https://<your_host>/oauth/facebook/callback, enable Facebook Login service at https://developers.facebook.com/apps/<app_id>/fb-login/settings/
|
||||||
|
|
||||||
|
* For Google, [register an app](https://console.developers.google.com), configure callback URL to https://<your_host>/oauth/google/callback
|
||||||
|
|
||||||
|
* For Microsoft, [register an app](https://portal.azure.com), configure callback URL to https://<your_host>/oauth/microsoft/callback
|
||||||
|
|
||||||
|
Once the app is configured on external OAuth provider side, add app's credentials and strategy-specific settings (if any — e.g. see Microsoft below) to `config/prod.secret.exs`,
|
||||||
|
per strategy's documentation (e.g. [ueberauth_twitter](https://github.com/ueberauth/ueberauth_twitter)). Example config basing on environment variables:
|
||||||
|
|
||||||
|
```
|
||||||
|
# Twitter
|
||||||
|
config :ueberauth, Ueberauth.Strategy.Twitter.OAuth,
|
||||||
|
consumer_key: System.get_env("TWITTER_CONSUMER_KEY"),
|
||||||
|
consumer_secret: System.get_env("TWITTER_CONSUMER_SECRET")
|
||||||
|
|
||||||
|
# Facebook
|
||||||
|
config :ueberauth, Ueberauth.Strategy.Facebook.OAuth,
|
||||||
|
client_id: System.get_env("FACEBOOK_APP_ID"),
|
||||||
|
client_secret: System.get_env("FACEBOOK_APP_SECRET"),
|
||||||
|
redirect_uri: System.get_env("FACEBOOK_REDIRECT_URI")
|
||||||
|
|
||||||
|
# Google
|
||||||
|
config :ueberauth, Ueberauth.Strategy.Google.OAuth,
|
||||||
|
client_id: System.get_env("GOOGLE_CLIENT_ID"),
|
||||||
|
client_secret: System.get_env("GOOGLE_CLIENT_SECRET"),
|
||||||
|
redirect_uri: System.get_env("GOOGLE_REDIRECT_URI")
|
||||||
|
|
||||||
|
# Microsoft
|
||||||
|
config :ueberauth, Ueberauth.Strategy.Microsoft.OAuth,
|
||||||
|
client_id: System.get_env("MICROSOFT_CLIENT_ID"),
|
||||||
|
client_secret: System.get_env("MICROSOFT_CLIENT_SECRET")
|
||||||
|
|
||||||
|
config :ueberauth, Ueberauth,
|
||||||
|
providers: [
|
||||||
|
microsoft: {Ueberauth.Strategy.Microsoft, [callback_params: []]}
|
||||||
|
]
|
||||||
|
```
|
|
@ -0,0 +1,17 @@
|
||||||
|
# General tips for customizing Pleroma FE
|
||||||
|
There are some configuration scripts for Pleroma BE and FE:
|
||||||
|
|
||||||
|
1. `config/prod.secret.exs`
|
||||||
|
1. `config/config.exs`
|
||||||
|
1. `priv/static/static/config.json`
|
||||||
|
|
||||||
|
The `prod.secret.exs` affects first. `config.exs` is for fallback or default. `config.json` is for GNU-social-BE-Pleroma-FE instances.
|
||||||
|
|
||||||
|
Usually all you have to do is:
|
||||||
|
|
||||||
|
1. Copy the section in the `config/config.exs` which you want to activate.
|
||||||
|
1. Paste into `config/prod.secret.exs`.
|
||||||
|
1. Edit `config/prod.secret.exs`.
|
||||||
|
1. Restart the Pleroma daemon.
|
||||||
|
|
||||||
|
`prod.secret.exs` is for the `MIX_ENV=prod` environment. `dev.secret.exs` is for the `MIX_ENV=dev` environment respectively.
|
|
@ -0,0 +1,53 @@
|
||||||
|
# Custom Emoji
|
||||||
|
|
||||||
|
To add custom emoji:
|
||||||
|
* Add the image file(s) to `priv/static/emoji/custom`
|
||||||
|
* In case of conflicts: add the desired shortcode with the path to `config/custom_emoji.txt`, comma-separated and one per line
|
||||||
|
* Force recompilation (``mix clean && mix compile``)
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
image files (in `/priv/static/emoji/custom`): `happy.png` and `sad.png`
|
||||||
|
|
||||||
|
content of `config/custom_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.
|
||||||
|
|
||||||
|
## 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`
|
|
@ -0,0 +1,103 @@
|
||||||
|
# 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: 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))
|
||||||
|
|
||||||
|
## 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,32 @@
|
||||||
|
# How to activate mediaproxy
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
Without the `mediaproxy` function, Pleroma don'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 the cache ability of nginx, to cache these content, so user can access it faster, cause 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,
|
||||||
|
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
|
|
@ -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}
|
||||||
|
```
|
|
@ -0,0 +1,31 @@
|
||||||
|
# How to activate user recommendation (Who to follow panel)
|
||||||
|
![who-to-follow-panel-small](/uploads/9de1b1300436c32461d272945f1bc23e/who-to-follow-panel-small.png)
|
||||||
|
|
||||||
|
To show the *who to follow* panel, edit `config/prod.secret.exs` in the Pleroma backend. Following code activates the *who to follow* panel:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
config :pleroma, :suggestions,
|
||||||
|
enabled: true,
|
||||||
|
third_party_engine:
|
||||||
|
"http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-suggestions-api.cgi?{{host}}+{{user}}",
|
||||||
|
timeout: 300_000,
|
||||||
|
limit: 23,
|
||||||
|
web: "https://vinayaka.distsn.org/?{{host}}+{{user}}"
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
`config/config.exs` already includes this code, but `enabled:` is `false`.
|
||||||
|
|
||||||
|
`/api/v1/suggestions` is also provided when *who to follow* panel is enabled.
|
||||||
|
|
||||||
|
For advanced customization, following code shows the newcomers of the fediverse at the *who to follow* panel:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
config :pleroma, :suggestions,
|
||||||
|
enabled: true,
|
||||||
|
third_party_engine:
|
||||||
|
"http://vinayaka.distsn.org/cgi-bin/vinayaka-user-new-suggestions-api.cgi?{{host}}+{{user}}",
|
||||||
|
timeout: 60_000,
|
||||||
|
limit: 23,
|
||||||
|
web: "https://vinayaka.distsn.org/user-new.html"
|
||||||
|
```
|
|
@ -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 seperately 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.
|
|
@ -0,0 +1,119 @@
|
||||||
|
# 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
|
||||||
|
* 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.
|
||||||
|
```
|
||||||
|
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:
|
||||||
|
|
||||||
|
```
|
||||||
|
config :pleroma, :instance,
|
||||||
|
[...]
|
||||||
|
rewrite_policy: Pleroma.Web.ActivityPub.MRF.SimplePolicy
|
||||||
|
```
|
||||||
|
|
||||||
|
Once `SimplePolicy` is enabled, you can configure various groups in the `:mrf_simple` config object. These groups are:
|
||||||
|
|
||||||
|
* `media_removal`: Servers in this group will have media stripped from incoming messages.
|
||||||
|
* `media_nsfw`: Servers in this group will have the #nsfw tag and sensitive setting injected into incoming messages which contain media.
|
||||||
|
* `reject`: Servers in this group will have their messages rejected.
|
||||||
|
* `federated_timeline_removal`: Servers in this group will have their messages unlisted from the public timelines by flipping the `to` and `cc` fields.
|
||||||
|
|
||||||
|
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` and remove messages from `spam.university` from the federated timeline:
|
||||||
|
|
||||||
|
```
|
||||||
|
config :pleroma, :instance,
|
||||||
|
rewrite_policy: [Pleroma.Web.ActivityPub.MRF.SimplePolicy]
|
||||||
|
|
||||||
|
config :pleroma, :mrf_simple,
|
||||||
|
media_removal: ["illegalporn.biz"],
|
||||||
|
media_nsfw: ["porn.biz", "porn.business"],
|
||||||
|
reject: ["spam.com"],
|
||||||
|
federated_timeline_removal: ["spam.university"]
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### 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 `rewrite_policy` config setting.
|
||||||
|
|
||||||
|
For example, here is a sample policy module which rewrites all messages to "new message content":
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
# This is a sample MRF policy which rewrites all Notes to have "new message
|
||||||
|
# content."
|
||||||
|
defmodule Site.RewritePolicy do
|
||||||
|
@behavior Pleroma.Web.ActivityPub.MRF
|
||||||
|
|
||||||
|
# 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}
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
If you save this file as `lib/site/mrf/rewrite_policy.ex`, it will be included when you next rebuild Pleroma. You can enable it in the configuration like so:
|
||||||
|
|
||||||
|
```
|
||||||
|
config :pleroma, :instance,
|
||||||
|
rewrite_policy: [
|
||||||
|
Pleroma.Web.ActivityPub.MRF.SimplePolicy,
|
||||||
|
Site.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.
|
|
@ -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 seperately 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.
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Small customizations
|
||||||
|
Replace `dev.secret.exs` with `prod.secret.exs` according to your setup.
|
||||||
|
|
||||||
|
# Thumbnail
|
||||||
|
|
||||||
|
Replace `priv/static/instance/thumbnail.jpeg` with your selfie or other neat picture. It will appear in [Pleroma Instances](http://distsn.org/pleroma-instances.html).
|
||||||
|
|
||||||
|
# Instance-specific panel
|
||||||
|
|
||||||
|
![instance-specific panel demo](/uploads/296b19ec806b130e0b49b16bfe29ce8a/image.png)
|
||||||
|
|
||||||
|
To show the instance specific panel, set `show_instance_panel` to `true` in `config/dev.secret.exs`. You can modify its content by editing `priv/static/instance/panel.html`.
|
||||||
|
|
||||||
|
# Background
|
||||||
|
|
||||||
|
You can change the background of your Pleroma instance by uploading it to `priv/static/static`, and then changing `"background"` in `config/dev.secret.exs` accordingly.
|
||||||
|
|
||||||
|
# Logo
|
||||||
|
|
||||||
|
![logo modification demo](/uploads/c70b14de60fa74245e7f0dcfa695ebff/image.png)
|
||||||
|
|
||||||
|
If you want to give a brand to your instance, look no further. You can change the logo of your instance by uploading it to `priv/static/static`, and then changing `logo` in `config/dev.secret.exs` accordingly.
|
||||||
|
|
||||||
|
# Theme
|
||||||
|
|
||||||
|
All users of your instance will be able to change the theme they use by going to the settings (the cog in the top-right hand corner). However, if you wish to change the default theme, you can do so by editing `theme` in `config/dev.secret.exs` accordingly.
|
||||||
|
|
||||||
|
# Terms of Service
|
||||||
|
|
||||||
|
Terms of Service will be shown to all users on the registration page. It's the best place where to write down the rules for your instance. You can modify the rules by changing `priv/static/static/terms-of-service.html`.
|
||||||
|
|
||||||
|
# Message Visibility
|
||||||
|
|
||||||
|
To enable message visibility options when posting like in the Mastodon frontend, set
|
||||||
|
`scope_options_enabled` to `true` in `config/dev.secret.exs`.
|
|
@ -0,0 +1,20 @@
|
||||||
|
# Static Directory
|
||||||
|
|
||||||
|
Static frontend files are shipped in `priv/static/` and tracked by version control in this repository. If you want to overwrite or update these without the possibility of merge conflicts, you can write your custom versions to `instance/static/`.
|
||||||
|
|
||||||
|
```
|
||||||
|
config :pleroma, :instance,
|
||||||
|
static_dir: "instance/static/",
|
||||||
|
```
|
||||||
|
|
||||||
|
You can overwrite this value in your configuration to use a different static instance directory.
|
||||||
|
|
||||||
|
## robots.txt
|
||||||
|
|
||||||
|
By default, the `robots.txt` that ships in `priv/static/` 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.
|
||||||
|
|
||||||
|
```
|
||||||
|
mix pleroma.robots_txt disallow_all
|
||||||
|
```
|
|
@ -0,0 +1,215 @@
|
||||||
|
# Installing on Alpine Linux
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This guide is a step-by-step installation guide for Alpine Linux. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.linode.com/docs/tools-reference/custom-kernels-distros/install-alpine-linux-on-your-linode/#configuration). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su -l <username> -s $SHELL -c 'command'` instead.
|
||||||
|
|
||||||
|
### Required packages
|
||||||
|
|
||||||
|
* `postgresql`
|
||||||
|
* `elixir`
|
||||||
|
* `erlang`
|
||||||
|
* `erlang-parsetools`
|
||||||
|
* `erlang-xmerl`
|
||||||
|
* `git`
|
||||||
|
* Development Tools
|
||||||
|
|
||||||
|
#### Optional packages used in this guide
|
||||||
|
|
||||||
|
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||||
|
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||||
|
|
||||||
|
### Prepare the system
|
||||||
|
|
||||||
|
* First make sure to have the community repository enabled:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
echo "https://nl.alpinelinux.org/alpine/latest-stable/community" | sudo tee -a /etc/apk/repository
|
||||||
|
```
|
||||||
|
|
||||||
|
* Then update the system, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apk update
|
||||||
|
sudo apk upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install some tools, which are needed later:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apk add git build-base
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Elixir and Erlang
|
||||||
|
|
||||||
|
* Install Erlang and Elixir:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apk add erlang erlang-runtime-tools erlang-xmerl elixir
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install `erlang-eldap` if you want to enable ldap authenticator
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apk add erlang-eldap
|
||||||
|
```
|
||||||
|
### Install PostgreSQL
|
||||||
|
|
||||||
|
* Install Postgresql server:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apk add postgresql postgresql-contrib
|
||||||
|
```
|
||||||
|
|
||||||
|
* Initialize database:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo /etc/init.d/postgresql start
|
||||||
|
```
|
||||||
|
|
||||||
|
* Enable and start postgresql server:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo rc-update add postgresql
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install PleromaBE
|
||||||
|
|
||||||
|
* Add a new system user for the Pleroma service:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo adduser -S -s /bin/false -h /opt/pleroma -H pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell.
|
||||||
|
|
||||||
|
* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /opt/pleroma
|
||||||
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
|
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change to the new directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma mix deps.get
|
||||||
|
```
|
||||||
|
|
||||||
|
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
||||||
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
|
* This may take some time, because parts of pleroma get compiled first.
|
||||||
|
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||||
|
|
||||||
|
* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mv config/{generated_config.exs,prod.secret.exs}
|
||||||
|
```
|
||||||
|
|
||||||
|
* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu postgres psql -f config/setup_db.psql
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now run the database migration:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now you can start Pleroma already
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix phx.server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finalize installation
|
||||||
|
|
||||||
|
If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create an OpenRC service file for Pleroma.
|
||||||
|
|
||||||
|
#### Nginx
|
||||||
|
|
||||||
|
* Install nginx, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apk add nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apk add certbot
|
||||||
|
```
|
||||||
|
|
||||||
|
and then set it up:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /var/lib/letsencrypt/
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
|
||||||
|
```
|
||||||
|
|
||||||
|
If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again).
|
||||||
|
|
||||||
|
* Copy the example nginx configuration to the nginx folder
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
* Before starting nginx edit the configuration and change it to your needs (e.g. change servername, change cert paths)
|
||||||
|
* Enable and start nginx:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo rc-update add nginx
|
||||||
|
sudo service nginx start
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### OpenRC service
|
||||||
|
|
||||||
|
* Copy example service file:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/init.d/pleroma /etc/init.d/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Make sure to start it during the boot
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo rc-update add pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create your first user
|
||||||
|
|
||||||
|
If your instance is up and running, you can create your first user with administrative rights with the following task:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Further reading
|
||||||
|
|
||||||
|
* [Admin tasks](Admin tasks)
|
||||||
|
* [Backup your instance](Backup-your-instance)
|
||||||
|
* [Configuration tips](General tips for customizing pleroma fe)
|
||||||
|
* [Hardening your instance](Hardening-your-instance)
|
||||||
|
* [How to activate mediaproxy](How-to-activate-mediaproxy)
|
||||||
|
* [Small Pleroma-FE customizations](Small customizations)
|
||||||
|
* [Updating your instance](Updating-your-instance)
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
|
@ -0,0 +1,213 @@
|
||||||
|
# Installing on Arch Linux
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This guide will assume that you have administrative rights, either as root or a user with [sudo permissions](https://wiki.archlinux.org/index.php/Sudo). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||||
|
|
||||||
|
### Required packages
|
||||||
|
|
||||||
|
* `postgresql`
|
||||||
|
* `elixir`
|
||||||
|
* `git`
|
||||||
|
* `base-devel`
|
||||||
|
|
||||||
|
#### Optional packages used in this guide
|
||||||
|
|
||||||
|
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||||
|
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||||
|
|
||||||
|
### Prepare the system
|
||||||
|
|
||||||
|
* First update the system, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo pacman -Syu
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install some of the above mentioned programs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo pacman -S git base-devel elixir
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install PostgreSQL
|
||||||
|
|
||||||
|
[Arch Wiki article](https://wiki.archlinux.org/index.php/PostgreSQL)
|
||||||
|
|
||||||
|
* Install the `postgresql` package:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo pacman -S postgresql
|
||||||
|
```
|
||||||
|
|
||||||
|
* Initialize the database cluster:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -iu postgres initdb -D /var/lib/postgres/data
|
||||||
|
```
|
||||||
|
|
||||||
|
* Start and enable the `postgresql.service`
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now postgresql.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install PleromaBE
|
||||||
|
|
||||||
|
* Add a new system user for the Pleroma service:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell.
|
||||||
|
|
||||||
|
* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /opt/pleroma
|
||||||
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
|
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change to the new directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma mix deps.get
|
||||||
|
```
|
||||||
|
|
||||||
|
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
||||||
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
|
* This may take some time, because parts of pleroma get compiled first.
|
||||||
|
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||||
|
|
||||||
|
* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mv config/{generated_config.exs,prod.secret.exs}
|
||||||
|
```
|
||||||
|
|
||||||
|
* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu postgres psql -f config/setup_db.psql
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now run the database migration:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now you can start Pleroma already
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix phx.server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finalize installation
|
||||||
|
|
||||||
|
If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create a systemd service file for Pleroma.
|
||||||
|
|
||||||
|
#### Nginx
|
||||||
|
|
||||||
|
* Install nginx, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo pacman -S nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create directories for available and enabled sites:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /etc/nginx/sites-{available,enabled}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Append the following line at the end of the `http` block in `/etc/nginx/nginx.conf`:
|
||||||
|
|
||||||
|
```Nginx
|
||||||
|
include sites-enabled/*;
|
||||||
|
```
|
||||||
|
|
||||||
|
* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo pacman -S certbot certbot-nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
and then set it up:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /var/lib/letsencrypt/
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
|
||||||
|
```
|
||||||
|
|
||||||
|
If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* Copy the example nginx configuration and activate it:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.nginx
|
||||||
|
sudo ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* Before starting nginx edit the configuration and change it to your needs (e.g. change servername, change cert paths)
|
||||||
|
* Enable and start nginx:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now nginx.service
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Other webserver/proxies
|
||||||
|
|
||||||
|
You can find example configurations for them in `/opt/pleroma/installation/`.
|
||||||
|
|
||||||
|
#### Systemd service
|
||||||
|
|
||||||
|
* Copy example service file
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
* Edit the service file and make sure that all paths fit your installation
|
||||||
|
* Enable and start `pleroma.service`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create your first user
|
||||||
|
|
||||||
|
If your instance is up and running, you can create your first user with administrative rights with the following task:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Further reading
|
||||||
|
|
||||||
|
* [Admin tasks](Admin tasks)
|
||||||
|
* [Backup your instance](Backup-your-instance)
|
||||||
|
* [Configuration tips](General tips for customizing pleroma fe)
|
||||||
|
* [Hardening your instance](Hardening-your-instance)
|
||||||
|
* [How to activate mediaproxy](How-to-activate-mediaproxy)
|
||||||
|
* [Small Pleroma-FE customizations](Small customizations)
|
||||||
|
* [Updating your instance](Updating-your-instance)
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
|
@ -0,0 +1,277 @@
|
||||||
|
# Installing on CentOS 7
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This guide is a step-by-step installation guide for CentOS 7. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-create-a-sudo-user-on-centos-quickstart). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||||
|
|
||||||
|
### Required packages
|
||||||
|
|
||||||
|
* `postgresql` (9,6+, CentOS 7 comes with 9.2, we will install version 11 in this guide)
|
||||||
|
* `elixir` (1.5+)
|
||||||
|
* `erlang`
|
||||||
|
* `erlang-parsetools`
|
||||||
|
* `erlang-xmerl`
|
||||||
|
* `git`
|
||||||
|
* Development Tools
|
||||||
|
|
||||||
|
#### Optional packages used in this guide
|
||||||
|
|
||||||
|
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||||
|
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||||
|
|
||||||
|
### Prepare the system
|
||||||
|
|
||||||
|
* First update the system, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum update
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install some of the above mentioned programs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum install wget git unzip
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install development tools:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum group install "Development Tools"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Elixir and Erlang
|
||||||
|
|
||||||
|
* Add the EPEL repo:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum install epel-release
|
||||||
|
sudo yum -y update
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install Erlang repository:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions-1.0-1.noarch.rpm
|
||||||
|
sudo rpm -Uvh erlang-solutions-1.0-1.noarch.rpm
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install Erlang:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum install erlang erlang-parsetools erlang-xmerl
|
||||||
|
```
|
||||||
|
|
||||||
|
* Download [latest Elixir release from Github](https://github.com/elixir-lang/elixir/releases/tag/v1.8.1) (Example for the newest version at the time when this manual was written)
|
||||||
|
|
||||||
|
```shell
|
||||||
|
wget -P /tmp/ https://github.com/elixir-lang/elixir/releases/download/v1.8.1/Precompiled.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create folder where you want to install Elixir, we’ll use:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /opt/elixir
|
||||||
|
```
|
||||||
|
|
||||||
|
* Unzip downloaded file there:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo unzip /tmp/Precompiled.zip -d /opt/elixir
|
||||||
|
```
|
||||||
|
|
||||||
|
* Create symlinks for the pre-compiled binaries:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
for e in elixir elixirc iex mix; do sudo ln -s /opt/elixir/bin/${e} /usr/local/bin/${e}; done
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install PostgreSQL
|
||||||
|
|
||||||
|
* Add the Postgresql repository:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum install https://download.postgresql.org/pub/repos/yum/11/redhat/rhel-7-x86_64/pgdg-centos11-11-2.noarch.rpm
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install the Postgresql server:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum install postgresql11-server postgresql11-contrib
|
||||||
|
```
|
||||||
|
|
||||||
|
* Initialize database:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo /usr/pgsql-11/bin/postgresql-11-setup initdb
|
||||||
|
```
|
||||||
|
|
||||||
|
* Open configuration file `/var/lib/pgsql/11/data/pg_hba.conf` and change the following lines from:
|
||||||
|
|
||||||
|
```plain
|
||||||
|
# IPv4 local connections:
|
||||||
|
host all all 127.0.0.1/32 ident
|
||||||
|
# IPv6 local connections:
|
||||||
|
host all all ::1/128 ident
|
||||||
|
```
|
||||||
|
|
||||||
|
to
|
||||||
|
|
||||||
|
```plain
|
||||||
|
# IPv4 local connections:
|
||||||
|
host all all 127.0.0.1/32 md5
|
||||||
|
# IPv6 local connections:
|
||||||
|
host all all ::1/128 md5
|
||||||
|
```
|
||||||
|
|
||||||
|
* Enable and start postgresql server:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now postgresql-11.service
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install PleromaBE
|
||||||
|
|
||||||
|
* Add a new system user for the Pleroma service:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell.
|
||||||
|
|
||||||
|
* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /opt/pleroma
|
||||||
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
|
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change to the new directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma mix deps.get
|
||||||
|
```
|
||||||
|
|
||||||
|
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
||||||
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
|
* This may take some time, because parts of pleroma get compiled first.
|
||||||
|
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||||
|
|
||||||
|
* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mv config/{generated_config.exs,prod.secret.exs}
|
||||||
|
```
|
||||||
|
|
||||||
|
* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu postgres psql -f config/setup_db.psql
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now run the database migration:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now you can start Pleroma already
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix phx.server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finalize installation
|
||||||
|
|
||||||
|
If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create a systemd service file for Pleroma.
|
||||||
|
|
||||||
|
#### Nginx
|
||||||
|
|
||||||
|
* Install nginx, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum install nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo yum install certbot-nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
and then set it up:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /var/lib/letsencrypt/
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
|
||||||
|
```
|
||||||
|
|
||||||
|
If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* Copy the example nginx configuration to the nginx folder
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/conf.d/pleroma.conf
|
||||||
|
```
|
||||||
|
|
||||||
|
* Before starting nginx edit the configuration and change it to your needs (e.g. change servername, change cert paths)
|
||||||
|
* Enable and start nginx:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Other webserver/proxies
|
||||||
|
|
||||||
|
You can find example configurations for them in `/opt/pleroma/installation/`.
|
||||||
|
|
||||||
|
#### Systemd service
|
||||||
|
|
||||||
|
* Copy example service file
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
* Edit the service file and make sure that all paths fit your installation
|
||||||
|
* Enable and start `pleroma.service`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create your first user
|
||||||
|
|
||||||
|
If your instance is up and running, you can create your first user with administrative rights with the following task:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Further reading
|
||||||
|
|
||||||
|
* [Admin tasks](Admin tasks)
|
||||||
|
* [Backup your instance](Backup-your-instance)
|
||||||
|
* [Configuration tips](General tips for customizing pleroma fe)
|
||||||
|
* [Hardening your instance](Hardening-your-instance)
|
||||||
|
* [How to activate mediaproxy](How-to-activate-mediaproxy)
|
||||||
|
* [Small Pleroma-FE customizations](Small customizations)
|
||||||
|
* [Updating your instance](Updating-your-instance)
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
|
@ -0,0 +1,202 @@
|
||||||
|
# Installing on Debian Based Distributions
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
This guide will assume you are on Debian Stretch. This guide should also work with Ubuntu 16.04 and 18.04. It also assumes that you have administrative rights, either as root or a user with [sudo permissions](https://www.digitalocean.com/community/tutorials/how-to-add-delete-and-grant-sudo-privileges-to-users-on-a-debian-vps). If you want to run this guide with root, ignore the `sudo` at the beginning of the lines, unless it calls a user like `sudo -Hu pleroma`; in this case, use `su <username> -s $SHELL -c 'command'` instead.
|
||||||
|
|
||||||
|
### Required packages
|
||||||
|
|
||||||
|
* `postgresql` (9.6+, Ubuntu 16.04 comes with 9.5, you can get a newer version from [here](https://www.postgresql.org/download/linux/ubuntu/))
|
||||||
|
* `postgresql-contrib` (9.6+, same situtation as above)
|
||||||
|
* `elixir` (1.5+, [install from here, Debian and Ubuntu ship older versions](https://elixir-lang.org/install.html#unix-and-unix-like) or use [asdf](https://github.com/asdf-vm/asdf) as the pleroma user)
|
||||||
|
* `erlang-dev`
|
||||||
|
* `erlang-tools`
|
||||||
|
* `erlang-parsetools`
|
||||||
|
* `erlang-eldap`, if you want to enable ldap authenticator
|
||||||
|
* `erlang-xmerl`
|
||||||
|
* `git`
|
||||||
|
* `build-essential`
|
||||||
|
|
||||||
|
#### Optional packages used in this guide
|
||||||
|
|
||||||
|
* `nginx` (preferred, example configs for other reverse proxies can be found in the repo)
|
||||||
|
* `certbot` (or any other ACME client for Let’s Encrypt certificates)
|
||||||
|
|
||||||
|
### Prepare the system
|
||||||
|
|
||||||
|
* First update the system, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apt update
|
||||||
|
sudo apt full-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install some of the above mentioned programs:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apt install git build-essential postgresql postgresql-contrib
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install Elixir and Erlang
|
||||||
|
|
||||||
|
* Download and add the Erlang repository:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb
|
||||||
|
sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install Elixir and Erlang:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apt update
|
||||||
|
sudo apt install elixir erlang-dev erlang-parsetools erlang-xmerl erlang-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
### Install PleromaBE
|
||||||
|
|
||||||
|
* Add a new system user for the Pleroma service:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo useradd -r -s /bin/false -m -d /var/lib/pleroma -U pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note**: To execute a single command as the Pleroma system user, use `sudo -Hu pleroma command`. You can also switch to a shell by using `sudo -Hu pleroma $SHELL`. If you don’t have and want `sudo` on your system, you can use `su` as root user (UID 0) for a single command by using `su -l pleroma -s $SHELL -c 'command'` and `su -l pleroma -s $SHELL` for starting a shell.
|
||||||
|
|
||||||
|
* Git clone the PleromaBE repository and make the Pleroma user the owner of the directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /opt/pleroma
|
||||||
|
sudo chown -R pleroma:pleroma /opt/pleroma
|
||||||
|
sudo -Hu pleroma git clone https://git.pleroma.social/pleroma/pleroma /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Change to the new directory:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
cd /opt/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* Install the dependencies for Pleroma and answer with `yes` if it asks you to install `Hex`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma mix deps.get
|
||||||
|
```
|
||||||
|
|
||||||
|
* Generate the configuration: `sudo -Hu pleroma mix pleroma.instance gen`
|
||||||
|
* Answer with `yes` if it asks you to install `rebar3`.
|
||||||
|
* This may take some time, because parts of pleroma get compiled first.
|
||||||
|
* After that it will ask you a few questions about your instance and generates a configuration file in `config/generated_config.exs`.
|
||||||
|
|
||||||
|
* Check the configuration and if all looks right, rename it, so Pleroma will load it (`prod.secret.exs` for productive instance, `dev.secret.exs` for development instances):
|
||||||
|
|
||||||
|
```shell
|
||||||
|
mv config/{generated_config.exs,prod.secret.exs}
|
||||||
|
```
|
||||||
|
|
||||||
|
* The previous command creates also the file `config/setup_db.psql`, with which you can create the database:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu postgres psql -f config/setup_db.psql
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now run the database migration:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix ecto.migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
* Now you can start Pleroma already
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix phx.server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Finalize installation
|
||||||
|
|
||||||
|
If you want to open your newly installed instance to the world, you should run nginx or some other webserver/proxy in front of Pleroma and you should consider to create a systemd service file for Pleroma.
|
||||||
|
|
||||||
|
#### Nginx
|
||||||
|
|
||||||
|
* Install nginx, if not already done:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apt install nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* Setup your SSL cert, using your method of choice or certbot. If using certbot, first install it:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo apt install certbot
|
||||||
|
```
|
||||||
|
|
||||||
|
and then set it up:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo mkdir -p /var/lib/letsencrypt/
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --standalone
|
||||||
|
```
|
||||||
|
|
||||||
|
If that doesn’t work, make sure, that nginx is not already running. If it still doesn’t work, try setting up nginx first (change ssl “on” to “off” and try again).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* Copy the example nginx configuration and activate it:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/pleroma.nginx /etc/nginx/sites-available/pleroma.nginx
|
||||||
|
sudo ln -s /etc/nginx/sites-available/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* Before starting nginx edit the configuration and change it to your needs (e.g. change servername, change cert paths)
|
||||||
|
* Enable and start nginx:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now nginx.service
|
||||||
|
```
|
||||||
|
|
||||||
|
If you need to renew the certificate in the future, uncomment the relevant location block in the nginx config and run:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo certbot certonly --email <your@emailaddress> -d <yourdomain> --webroot -w /var/lib/letsencrypt/
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Other webserver/proxies
|
||||||
|
|
||||||
|
You can find example configurations for them in `/opt/pleroma/installation/`.
|
||||||
|
|
||||||
|
#### Systemd service
|
||||||
|
|
||||||
|
* Copy example service file
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo cp /opt/pleroma/installation/pleroma.service /etc/systemd/system/pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
* Edit the service file and make sure that all paths fit your installation
|
||||||
|
* Enable and start `pleroma.service`:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo systemctl enable --now pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Create your first user
|
||||||
|
|
||||||
|
If your instance is up and running, you can create your first user with administrative rights with the following task:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
sudo -Hu pleroma MIX_ENV=prod mix pleroma.user new <username> <your@emailaddress> --admin
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Further reading
|
||||||
|
|
||||||
|
* [Admin tasks](Admin tasks)
|
||||||
|
* [Backup your instance](Backup-your-instance)
|
||||||
|
* [Configuration tips](General tips for customizing pleroma fe)
|
||||||
|
* [Hardening your instance](Hardening-your-instance)
|
||||||
|
* [How to activate mediaproxy](How-to-activate-mediaproxy)
|
||||||
|
* [Small Pleroma-FE customizations](Small customizations)
|
||||||
|
* [Updating your instance](Updating-your-instance)
|
||||||
|
|
||||||
|
## Questions
|
||||||
|
|
||||||
|
Questions about the installation or didn’t it work as it should be, ask in [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org) or IRC Channel **#pleroma** on **Freenode**.
|
|
@ -0,0 +1,191 @@
|
||||||
|
# Pleromaの入れ方
|
||||||
|
## 日本語訳について
|
||||||
|
|
||||||
|
この記事は [Installing on Debian based distributions](Installing on Debian based distributions) の日本語訳です。何かがおかしいと思ったら、原文を見てください。
|
||||||
|
|
||||||
|
## インストール
|
||||||
|
|
||||||
|
このガイドはDebian Stretchを仮定しています。Ubuntu 16.04でも可能です。
|
||||||
|
|
||||||
|
### 必要なソフトウェア
|
||||||
|
|
||||||
|
- PostgreSQL 9.6+ (postgresql-contrib-9.6 または他のバージョンの PSQL をインストールしてください)
|
||||||
|
- Elixir 1.5 以上 ([Debianのリポジトリからインストールしないこと!!! ここからインストールすること!](https://elixir-lang.org/install.html#unix-and-unix-like))。または [asdf](https://github.com/asdf-vm/asdf) を pleroma ユーザーでインストール。
|
||||||
|
- erlang-dev
|
||||||
|
- erlang-tools
|
||||||
|
- erlang-parsetools
|
||||||
|
- erlang-xmerl (Jessieではバックポートからインストールすること!)
|
||||||
|
- git
|
||||||
|
- build-essential
|
||||||
|
- openssh
|
||||||
|
- openssl
|
||||||
|
- nginx prefered (Apacheも動くかもしれませんが、誰もテストしていません!)
|
||||||
|
- certbot (または何らかのACME Let's encryptクライアント)
|
||||||
|
|
||||||
|
### システムを準備する
|
||||||
|
|
||||||
|
* まずシステムをアップデートしてください。
|
||||||
|
```
|
||||||
|
apt update && apt dist-upgrade
|
||||||
|
```
|
||||||
|
|
||||||
|
* 複数のツールとpostgresqlをインストールします。あとで必要になるので。
|
||||||
|
```
|
||||||
|
apt install git build-essential openssl ssh sudo postgresql-9.6 postgresql-contrib-9.6
|
||||||
|
```
|
||||||
|
(postgresqlのバージョンは、あなたのディストロにあわせて変えてください。または、バージョン番号がいらないかもしれません。)
|
||||||
|
|
||||||
|
### ElixirとErlangをインストールします
|
||||||
|
|
||||||
|
* Erlangのリポジトリをダウンロードおよびインストールします。
|
||||||
|
```
|
||||||
|
wget -P /tmp/ https://packages.erlang-solutions.com/erlang-solutions_1.0_all.deb && sudo dpkg -i /tmp/erlang-solutions_1.0_all.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
* ElixirとErlangをインストールします、
|
||||||
|
```
|
||||||
|
apt update && apt install elixir erlang-dev erlang-parsetools erlang-xmerl erlang-tools
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pleroma BE (バックエンド) をインストールします
|
||||||
|
|
||||||
|
* 新しいユーザーを作ります。
|
||||||
|
```
|
||||||
|
adduser pleroma
|
||||||
|
```
|
||||||
|
(Give it any password you want, make it STRONG)
|
||||||
|
|
||||||
|
* 新しいユーザーをsudoグループに入れます。
|
||||||
|
```
|
||||||
|
usermod -aG sudo pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* 新しいユーザーに変身し、ホームディレクトリに移動します。
|
||||||
|
```
|
||||||
|
su pleroma
|
||||||
|
cd ~
|
||||||
|
```
|
||||||
|
|
||||||
|
* Gitリポジトリをクローンします。
|
||||||
|
```
|
||||||
|
git clone https://git.pleroma.social/pleroma/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
* 新しいディレクトリに移動します。
|
||||||
|
```
|
||||||
|
cd pleroma/
|
||||||
|
```
|
||||||
|
|
||||||
|
* Pleromaが依存するパッケージをインストールします。Hexをインストールしてもよいか聞かれたら、yesを入力してください。
|
||||||
|
```
|
||||||
|
mix deps.get
|
||||||
|
```
|
||||||
|
|
||||||
|
* コンフィギュレーションを生成します。
|
||||||
|
```
|
||||||
|
mix pleroma.instance gen
|
||||||
|
```
|
||||||
|
* rebar3をインストールしてもよいか聞かれたら、yesを入力してください。
|
||||||
|
* この処理には時間がかかります。私もよく分かりませんが、何らかのコンパイルが行われているようです。
|
||||||
|
* あなたのインスタンスについて、いくつかの質問があります。その回答は `config/generated_config.exs` というコンフィギュレーションファイルに保存されます。
|
||||||
|
|
||||||
|
**注意**: メディアプロクシを有効にすると回答して、なおかつ、キャッシュのURLは空欄のままにしている場合は、`generated_config.exs` を編集して、`base_url` で始まる行をコメントアウトまたは削除してください。そして、上にある行の `true` の後にあるコンマを消してください。
|
||||||
|
|
||||||
|
* コンフィギュレーションを確認して、もし問題なければ、ファイル名を変更してください。
|
||||||
|
```
|
||||||
|
mv config/{generated_config.exs,prod.secret.exs}
|
||||||
|
```
|
||||||
|
|
||||||
|
* これまでのコマンドで、すでに `config/setup_db.psql` というファイルが作られています。このファイルをもとに、データベースを作成します。
|
||||||
|
```
|
||||||
|
sudo su postgres -c 'psql -f config/setup_db.psql'
|
||||||
|
```
|
||||||
|
|
||||||
|
* そして、データベースのミグレーションを実行します。
|
||||||
|
```
|
||||||
|
MIX_ENV=prod mix ecto.migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
* Pleromaを起動できるようになりました。
|
||||||
|
```
|
||||||
|
MIX_ENV=prod mix phx.server
|
||||||
|
```
|
||||||
|
|
||||||
|
### インストールを終わらせる
|
||||||
|
|
||||||
|
あなたの新しいインスタンスを世界に向けて公開するには、nginxまたは何らかのウェブサーバー (プロクシ) を使用する必要があります。また、Pleroma のためにシステムサービスファイルを作成する必要があります。
|
||||||
|
|
||||||
|
#### Nginx
|
||||||
|
|
||||||
|
* まだインストールしていないなら、nginxをインストールします。
|
||||||
|
```
|
||||||
|
apt install nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* SSLをセットアップします。他の方法でもよいですが、ここではcertbotを説明します。
|
||||||
|
certbotを使うならば、まずそれをインストールします。
|
||||||
|
```
|
||||||
|
apt install certbot
|
||||||
|
```
|
||||||
|
そしてセットアップします。
|
||||||
|
```
|
||||||
|
mkdir -p /var/lib/letsencrypt/.well-known
|
||||||
|
% certbot certonly --email your@emailaddress --webroot -w /var/lib/letsencrypt/ -d yourdomain
|
||||||
|
```
|
||||||
|
もしうまくいかないときは、先にnginxを設定してください。ssl "on" を "off" に変えてから再試行してください。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
* nginxコンフィギュレーションの例をnginxフォルダーにコピーします。
|
||||||
|
```
|
||||||
|
cp /home/pleroma/pleroma/installation/pleroma.nginx /etc/nginx/sites-enabled/pleroma.nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
* nginxを起動する前に、コンフィギュレーションを編集してください。例えば、サーバー名、証明書のパスなどを変更する必要があります。
|
||||||
|
* nginxを再起動します。
|
||||||
|
```
|
||||||
|
systemctl reload nginx.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Systemd サービス
|
||||||
|
|
||||||
|
* サービスファイルの例をコピーします。
|
||||||
|
```
|
||||||
|
cp /home/pleroma/pleroma/installation/pleroma.service /usr/lib/systemd/system/pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
* サービスファイルを変更します。すべてのパスが正しいことを確認してください。また、`[Service]` セクションに以下の行があることを確認してください。
|
||||||
|
```
|
||||||
|
Environment="MIX_ENV=prod"
|
||||||
|
```
|
||||||
|
|
||||||
|
* `pleroma.service` を enable および start してください。
|
||||||
|
```
|
||||||
|
systemctl enable --now pleroma.service
|
||||||
|
```
|
||||||
|
|
||||||
|
#### モデレーターを作る
|
||||||
|
|
||||||
|
新たにユーザーを作ったら、モデレーター権限を与えたいかもしれません。以下のタスクで可能です。
|
||||||
|
```
|
||||||
|
mix set_moderator username [true|false]
|
||||||
|
```
|
||||||
|
|
||||||
|
モデレーターはすべてのポストを消すことができます。将来的には他のことも可能になるかもしれません。
|
||||||
|
|
||||||
|
#### メディアプロクシを有効にする
|
||||||
|
|
||||||
|
`generate_config` でメディアプロクシを有効にしているなら、すでにメディアプロクシが動作しています。あとから設定を変更したいなら、[How to activate mediaproxy](How-to-activate-mediaproxy) を見てください。
|
||||||
|
|
||||||
|
#### コンフィギュレーションとカスタマイズ
|
||||||
|
|
||||||
|
* [Configuration tips](General tips for customizing pleroma fe)
|
||||||
|
* [Small Pleroma-FE customizations](Small customizations)
|
||||||
|
* [Admin tasks](Admin tasks)
|
||||||
|
|
||||||
|
## 質問ある?
|
||||||
|
|
||||||
|
インストールについて質問がある、もしくは、うまくいかないときは、以下のところで質問できます。
|
||||||
|
|
||||||
|
* [#pleroma:matrix.org](https://matrix.heldscal.la/#/room/#freenode_#pleroma:matrix.org)
|
||||||
|
* **Freenode** の **#pleroma** IRCチャンネル
|
|
@ -0,0 +1,198 @@
|
||||||
|
# Installing on NetBSD
|
||||||
|
|
||||||
|
## Required software
|
||||||
|
|
||||||
|
pkgin should have been installed by the NetBSD installer if you selected
|
||||||
|
the right options. If it isn't installed, install it using pkg_add.
|
||||||
|
|
||||||
|
Note that `postgresql11-contrib` is needed for the Postgres extensions
|
||||||
|
Pleroma uses.
|
||||||
|
|
||||||
|
The `mksh` shell is needed to run the Elixir `mix` script.
|
||||||
|
|
||||||
|
`# pkgin install acmesh elixir git-base git-docs mksh nginx postgresql11-server postgresql11-client postgresql11-contrib sudo`
|
||||||
|
|
||||||
|
You can also build these packages using pkgsrc:
|
||||||
|
```
|
||||||
|
databases/postgresql11-contrib
|
||||||
|
databases/postgresql11-client
|
||||||
|
databases/postgresql11-server
|
||||||
|
devel/git-base
|
||||||
|
devel/git-docs
|
||||||
|
lang/elixir
|
||||||
|
security/acmesh
|
||||||
|
security/sudo
|
||||||
|
shells/mksh
|
||||||
|
www/nginx
|
||||||
|
```
|
||||||
|
|
||||||
|
Copy the rc.d scripts to the right directory:
|
||||||
|
|
||||||
|
```
|
||||||
|
# cp /usr/pkg/share/examples/rc.d/nginx /usr/pkg/share/examples/rc.d/pgsql /etc/rc.d
|
||||||
|
```
|
||||||
|
|
||||||
|
Add nginx and Postgres to `/etc/rc.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
nginx=YES
|
||||||
|
pgsql=YES
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuring postgres
|
||||||
|
|
||||||
|
First, run `# /etc/rc.d/pgsql start`. Then, `$ sudo -Hu pgsql -g pgsql createdb`.
|
||||||
|
|
||||||
|
## Configuring Pleroma
|
||||||
|
|
||||||
|
Create a user for Pleroma:
|
||||||
|
|
||||||
|
```
|
||||||
|
# groupadd pleroma
|
||||||
|
# useradd -d /home/pleroma -m -g pleroma -s /usr/pkg/bin/mksh pleroma
|
||||||
|
# echo 'export LC_ALL="en_GB.UTF-8"' >> /home/pleroma/.profile
|
||||||
|
# su -l pleroma -c $SHELL
|
||||||
|
```
|
||||||
|
|
||||||
|
Clone the repository:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd /home/pleroma
|
||||||
|
$ git clone https://git.pleroma.social/pleroma/pleroma.git
|
||||||
|
```
|
||||||
|
|
||||||
|
Configure Pleroma. Note that you need a domain name at this point:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ cd /home/pleroma/pleroma
|
||||||
|
$ mix deps.get
|
||||||
|
$ mix pleroma.instance gen # You will be asked a few questions here.
|
||||||
|
```
|
||||||
|
|
||||||
|
Since Postgres is configured, we can now initialize the database. There should
|
||||||
|
now be a file in `config/setup_db.psql` that makes this easier. Edit it, and
|
||||||
|
*change the password* to a password of your choice. Make sure it is secure, since
|
||||||
|
it'll be protecting your database. Now initialize the database:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo -Hu pgsql -g pgsql psql -f config/setup_db.psql
|
||||||
|
```
|
||||||
|
|
||||||
|
Postgres allows connections from all users without a password by default. To
|
||||||
|
fix this, edit `/usr/pkg/pgsql/data/pg_hba.conf`. Change every `trust` to
|
||||||
|
`password`.
|
||||||
|
|
||||||
|
Once this is done, restart Postgres with `# /etc/rc.d/pgsql restart`.
|
||||||
|
|
||||||
|
Run the database migrations.
|
||||||
|
You will need to do this whenever you update with `git pull`:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ MIX_ENV=prod mix ecto.migrate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuring nginx
|
||||||
|
|
||||||
|
Install the example configuration file
|
||||||
|
`/home/pleroma/pleroma/installation/pleroma.nginx` to
|
||||||
|
`/usr/pkg/etc/nginx.conf`.
|
||||||
|
|
||||||
|
Note that it will need to be wrapped in a `http {}` block. You should add
|
||||||
|
settings for the nginx daemon outside of the http block, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
user nginx nginx;
|
||||||
|
error_log /var/log/nginx/error.log;
|
||||||
|
worker_processes 4;
|
||||||
|
|
||||||
|
events {
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit the defaults:
|
||||||
|
|
||||||
|
* Change `ssl_certificate` and `ssl_trusted_certificate` to
|
||||||
|
`/etc/nginx/tls/fullchain`.
|
||||||
|
* Change `ssl_certificate_key` to `/etc/nginx/tls/key`.
|
||||||
|
* Change `example.tld` to your instance's domain name.
|
||||||
|
|
||||||
|
## Configuring acme.sh
|
||||||
|
|
||||||
|
We'll be using acme.sh in Stateless Mode for TLS certificate renewal.
|
||||||
|
|
||||||
|
First, get your account fingerprint:
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo -Hu nginx -g nginx acme.sh --register-account
|
||||||
|
```
|
||||||
|
|
||||||
|
You need to add the following to your nginx configuration for the server
|
||||||
|
running on port 80:
|
||||||
|
|
||||||
|
```
|
||||||
|
location ~ ^/\.well-known/acme-challenge/([-_a-zA-Z0-9]+)$ {
|
||||||
|
default_type text/plain;
|
||||||
|
return 200 "$1.6fXAG9VyG0IahirPEU2ZerUtItW2DHzDzD9wZaEKpqd";
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace the string after after `$1.` with your fingerprint.
|
||||||
|
|
||||||
|
Start nginx:
|
||||||
|
|
||||||
|
```
|
||||||
|
# /etc/rc.d/nginx start
|
||||||
|
```
|
||||||
|
|
||||||
|
It should now be possible to issue a cert (replace `example.com`
|
||||||
|
with your domain name):
|
||||||
|
|
||||||
|
```
|
||||||
|
$ sudo -Hu nginx -g nginx acme.sh --issue -d example.com --stateless
|
||||||
|
```
|
||||||
|
|
||||||
|
Let's add auto-renewal to `/etc/daily.local`
|
||||||
|
(replace `example.com` with your domain):
|
||||||
|
|
||||||
|
```
|
||||||
|
/usr/pkg/bin/sudo -Hu nginx -g nginx \
|
||||||
|
/usr/pkg/sbin/acme.sh -r \
|
||||||
|
-d example.com \
|
||||||
|
--cert-file /etc/nginx/tls/cert \
|
||||||
|
--key-file /etc/nginx/tls/key \
|
||||||
|
--ca-file /etc/nginx/tls/ca \
|
||||||
|
--fullchain-file /etc/nginx/tls/fullchain \
|
||||||
|
--stateless
|
||||||
|
```
|
||||||
|
|
||||||
|
## Creating a startup script for Pleroma
|
||||||
|
|
||||||
|
Copy the startup script to the correct location and make sure it's executable:
|
||||||
|
|
||||||
|
```
|
||||||
|
# cp /home/pleroma/pleroma/installation/netbsd/rc.d/pleroma /etc/rc.d/pleroma
|
||||||
|
# chmod +x /etc/rc.d/pleroma
|
||||||
|
```
|
||||||
|
|
||||||
|
Add the following to `/etc/rc.conf`:
|
||||||
|
|
||||||
|
```
|
||||||
|
pleroma=YES
|
||||||
|
pleroma_home="/home/pleroma"
|
||||||
|
pleroma_user="pleroma"
|
||||||
|
```
|
||||||
|
|
||||||
|
Run `# /etc/rc.d/pleroma start` to start Pleroma.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Restart nginx with `# /etc/rc.d/nginx restart` and you should be up and running.
|
||||||
|
|
||||||
|
If you need further help, contact niaa on freenode.
|
||||||
|
|
||||||
|
Make sure your time is in sync, or other instances will receive your posts with
|
||||||
|
incorrect timestamps. You should have ntpd running.
|
||||||
|
|
||||||
|
## Instances running NetBSD
|
||||||
|
|
||||||
|
* <https://catgirl.science>
|
|
@ -0,0 +1,222 @@
|
||||||
|
# Installing on OpenBSD
|
||||||
|
This guide describes the installation and configuration of pleroma (and the required software to run it) on a single OpenBSD 6.4 server.
|
||||||
|
For any additional information regarding commands and configuration files mentioned here, check the man pages [online](https://man.openbsd.org/) or directly on your server with the man command.
|
||||||
|
|
||||||
|
#### Required software
|
||||||
|
The following packages need to be installed:
|
||||||
|
* elixir
|
||||||
|
* gmake
|
||||||
|
* ImageMagick
|
||||||
|
* git
|
||||||
|
* postgresql-server
|
||||||
|
* postgresql-contrib
|
||||||
|
|
||||||
|
To install them, run the following command (with doas or as root):
|
||||||
|
`pkg_add elixir gmake ImageMagick git postgresql-server postgresql-contrib`
|
||||||
|
|
||||||
|
Pleroma requires a reverse proxy, OpenBSD has relayd in base (and is used in this guide) and packages/ports are available for nginx (www/nginx) and apache (www/apache-httpd). Independently of the reverse proxy, [acme-client(1)](https://man.openbsd.org/acme-client) can be used to get a certificate from Let's Encrypt.
|
||||||
|
|
||||||
|
#### Creating the pleroma user
|
||||||
|
Pleroma will be run by a dedicated user, \_pleroma. Before creating it, insert the following lines in login.conf:
|
||||||
|
```
|
||||||
|
pleroma:\
|
||||||
|
:datasize-max=1536M:\
|
||||||
|
:datasize-cur=1536M:\
|
||||||
|
:openfiles-max=4096
|
||||||
|
```
|
||||||
|
This creates a "pleroma" login class and sets higher values than default for datasize and openfiles (see [login.conf(5)](https://man.openbsd.org/login.conf)), this is required to avoid having pleroma crash some time after starting.
|
||||||
|
|
||||||
|
Create the \_pleroma user, assign it the pleroma login class and create its home directory (/home/\_pleroma/): `useradd -m -L pleroma _pleroma`
|
||||||
|
|
||||||
|
#### Clone pleroma's directory
|
||||||
|
Enter a shell as the \_pleroma user. As root, run `su _pleroma -;cd`. Then clone the repository with `git clone https://git.pleroma.social/pleroma/pleroma.git`. Pleroma is now installed in /home/\_pleroma/pleroma/, it will be configured and started at the end of this guide.
|
||||||
|
|
||||||
|
#### Postgresql
|
||||||
|
Start a shell as the \_postgresql user (as root run `su _postgresql -` then run the `initdb` command to initialize postgresql:
|
||||||
|
If you wish to not use the default location for postgresql's data (/var/postgresql/data), add the following switch at the end of the command: `-D <path>` and modify the `datadir` variable in the /etc/rc.d/postgresql script.
|
||||||
|
|
||||||
|
When this is done, enable postgresql so that it starts on boot and start it. As root, run:
|
||||||
|
```
|
||||||
|
rcctl enable postgresql
|
||||||
|
rcctl start postgresql
|
||||||
|
```
|
||||||
|
To check that it started properly and didn't fail right after starting, you can run `ps aux | grep postgres`, there should be multiple lines of output.
|
||||||
|
|
||||||
|
#### httpd
|
||||||
|
httpd will have three fuctions:
|
||||||
|
* redirect requests trying to reach the instance over http to the https URL
|
||||||
|
* serve a robots.txt file
|
||||||
|
* get Let's Encrypt certificates, with acme-client
|
||||||
|
|
||||||
|
Insert the following config in httpd.conf:
|
||||||
|
```
|
||||||
|
# $OpenBSD: httpd.conf,v 1.17 2017/04/16 08:50:49 ajacoutot Exp $
|
||||||
|
|
||||||
|
ext_inet="<IPv4 address>"
|
||||||
|
ext_inet6="<IPv6 address>"
|
||||||
|
|
||||||
|
server "default" {
|
||||||
|
listen on $ext_inet port 80 # Comment to disable listening on IPv4
|
||||||
|
listen on $ext_inet6 port 80 # Comment to disable listening on IPv6
|
||||||
|
listen on 127.0.0.1 port 80 # Do NOT comment this line
|
||||||
|
|
||||||
|
log syslog
|
||||||
|
directory no index
|
||||||
|
|
||||||
|
location "/.well-known/acme-challenge/*" {
|
||||||
|
root "/acme"
|
||||||
|
request strip 2
|
||||||
|
}
|
||||||
|
|
||||||
|
location "/robots.txt" { root "/htdocs/local/" }
|
||||||
|
location "/*" { block return 302 "https://$HTTP_HOST$REQUEST_URI" }
|
||||||
|
}
|
||||||
|
|
||||||
|
types {
|
||||||
|
include "/usr/share/misc/mime.types"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Do not forget to change *\<IPv4/6 address\>* to your server's address(es). If httpd should only listen on one protocol family, comment one of the two first *listen* options.
|
||||||
|
|
||||||
|
Create the /var/www/htdocs/local/ folder and write the content of your robots.txt in /var/www/htdocs/local/robots.txt.
|
||||||
|
Check the configuration with `httpd -n`, if it is OK enable and start httpd (as root):
|
||||||
|
```
|
||||||
|
rcctl enable httpd
|
||||||
|
rcctl start httpd
|
||||||
|
```
|
||||||
|
|
||||||
|
#### acme-client
|
||||||
|
acme-client is used to get SSL/TLS certificates from Let's Encrypt.
|
||||||
|
Insert the following configuration in /etc/acme-client.conf:
|
||||||
|
```
|
||||||
|
#
|
||||||
|
# $OpenBSD: acme-client.conf,v 1.4 2017/03/22 11:14:14 benno Exp $
|
||||||
|
#
|
||||||
|
|
||||||
|
authority letsencrypt-<domain name> {
|
||||||
|
#agreement url "https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf"
|
||||||
|
api url "https://acme-v01.api.letsencrypt.org/directory"
|
||||||
|
account key "/etc/acme/letsencrypt-privkey-<domain name>.pem"
|
||||||
|
}
|
||||||
|
|
||||||
|
domain <domain name> {
|
||||||
|
domain key "/etc/ssl/private/<domain name>.key"
|
||||||
|
domain certificate "/etc/ssl/<domain name>.crt"
|
||||||
|
domain full chain certificate "/etc/ssl/<domain name>.fullchain.pem"
|
||||||
|
sign with letsencrypt-<domain name>
|
||||||
|
challengedir "/var/www/acme/"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Replace *\<domain name\>* by the domain name you'll use for your instance. As root, run `acme-client -n` to check the config, then `acme-client -ADv <domain name>` to create account and domain keys, and request a certificate for the first time.
|
||||||
|
Make acme-client run everyday by adding it in /etc/daily.local. As root, run the following command: `echo "acme-client <domain name>" >> /etc/daily.local`.
|
||||||
|
|
||||||
|
Relayd will look for certificates and keys based on the address it listens on (see next part), the easiest way to make them available to relayd is to create a link, as root run:
|
||||||
|
```
|
||||||
|
ln -s /etc/ssl/<domain name>.fullchain.pem /etc/ssl/<IP address>.crt
|
||||||
|
ln -s /etc/ssl/private/<domain name>.key /etc/ssl/private/<IP address>.key
|
||||||
|
```
|
||||||
|
This will have to be done for each IPv4 and IPv6 address relayd listens on.
|
||||||
|
|
||||||
|
#### relayd
|
||||||
|
relayd will be used as the reverse proxy sitting in front of pleroma.
|
||||||
|
Insert the following configuration in /etc/relayd.conf:
|
||||||
|
```
|
||||||
|
# $OpenBSD: relayd.conf,v 1.4 2018/03/23 09:55:06 claudio Exp $
|
||||||
|
|
||||||
|
ext_inet="<IPv4 address>"
|
||||||
|
ext_inet6="<IPv6 address>"
|
||||||
|
|
||||||
|
table <pleroma_server> { 127.0.0.1 }
|
||||||
|
table <httpd_server> { 127.0.0.1 }
|
||||||
|
|
||||||
|
http protocol plerup { # Protocol for upstream pleroma server
|
||||||
|
#tcp { nodelay, sack, socket buffer 65536, backlog 128 } # Uncomment and adjust as you see fit
|
||||||
|
tls ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305"
|
||||||
|
tls ecdhe secp384r1
|
||||||
|
|
||||||
|
# Forward some paths to the local server (as pleroma won't respond to them as you might want)
|
||||||
|
pass request quick path "/robots.txt" forward to <httpd_server>
|
||||||
|
|
||||||
|
# Append a bunch of headers
|
||||||
|
match request header append "X-Forwarded-For" value "$REMOTE_ADDR" # This two header and the next one are not strictly required by pleroma but adding them won't hurt
|
||||||
|
match request header append "X-Forwarded-By" value "$SERVER_ADDR:$SERVER_PORT"
|
||||||
|
|
||||||
|
match response header append "X-XSS-Protection" value "1; mode=block"
|
||||||
|
match response header append "X-Permitted-Cross-Domain-Policies" value "none"
|
||||||
|
match response header append "X-Frame-Options" value "DENY"
|
||||||
|
match response header append "X-Content-Type-Options" value "nosniff"
|
||||||
|
match response header append "Referrer-Policy" value "same-origin"
|
||||||
|
match response header append "X-Download-Options" value "noopen"
|
||||||
|
match response header append "Content-Security-Policy" value "default-src 'none'; base-uri 'self'; form-action 'self'; img-src 'self' data: https:; media-src 'self' https:; style-src 'self' 'unsafe-inline'; font-src 'self'; script-src 'self'; connect-src 'self' wss://CHANGEME.tld; upgrade-insecure-requests;" # Modify "CHANGEME.tld" and set your instance's domain here
|
||||||
|
match request header append "Connection" value "upgrade"
|
||||||
|
#match response header append "Strict-Transport-Security" value "max-age=31536000; includeSubDomains" # Uncomment this only after you get HTTPS working.
|
||||||
|
|
||||||
|
# If you do not want remote frontends to be able to access your Pleroma backend server, comment these lines
|
||||||
|
match response header append "Access-Control-Allow-Origin" value "*"
|
||||||
|
match response header append "Access-Control-Allow-Methods" value "POST, PUT, DELETE, GET, PATCH, OPTIONS"
|
||||||
|
match response header append "Access-Control-Allow-Headers" value "Authorization, Content-Type, Idempotency-Key"
|
||||||
|
match response header append "Access-Control-Expose-Headers" value "Link, X-RateLimit-Reset, X-RateLimit-Limit, X-RateLimit-Remaining, X-Request-Id"
|
||||||
|
# Stop commenting lines here
|
||||||
|
}
|
||||||
|
|
||||||
|
relay wwwtls {
|
||||||
|
listen on $ext_inet port https tls # Comment to disable listening on IPv4
|
||||||
|
listen on $ext_inet6 port https tls # Comment to disable listening on IPv6
|
||||||
|
|
||||||
|
protocol plerup
|
||||||
|
|
||||||
|
forward to <pleroma_server> port 4000 check http "/" code 200
|
||||||
|
forward to <httpd_server> port 80 check http "/robots.txt" code 200
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Again, change *\<IPv4/6 address\>* to your server's address(es) and comment one of the two *listen* options if needed. Also change *wss://CHANGEME.tld* to *wss://\<your instance's domain name\>*.
|
||||||
|
Check the configuration with `relayd -n`, if it is OK enable and start relayd (as root):
|
||||||
|
```
|
||||||
|
rcctl enable relayd
|
||||||
|
rcctl start relayd
|
||||||
|
```
|
||||||
|
|
||||||
|
#### pf
|
||||||
|
Enabling and configuring pf is highly recommended.
|
||||||
|
In /etc/pf.conf, insert the following configuration:
|
||||||
|
```
|
||||||
|
# Macros
|
||||||
|
if="<network interface>"
|
||||||
|
authorized_ssh_clients="any"
|
||||||
|
|
||||||
|
# Skip traffic on loopback interface
|
||||||
|
set skip on lo
|
||||||
|
|
||||||
|
# Default behavior
|
||||||
|
set block-policy drop
|
||||||
|
block in log all
|
||||||
|
pass out quick
|
||||||
|
|
||||||
|
# Security features
|
||||||
|
match in all scrub (no-df random-id)
|
||||||
|
block in log from urpf-failed
|
||||||
|
|
||||||
|
# Rules
|
||||||
|
pass in quick on $if inet proto icmp to ($if) icmp-type { echoreq unreach paramprob trace } # ICMP
|
||||||
|
pass in quick on $if inet6 proto icmp6 to ($if) icmp6-type { echoreq unreach paramprob timex toobig } # ICMPv6
|
||||||
|
pass in quick on $if proto tcp to ($if) port { http https } # relayd/httpd
|
||||||
|
pass in quick on $if proto tcp from $authorized_ssh_clients to ($if) port ssh
|
||||||
|
```
|
||||||
|
Replace *\<network interface\>* by your server's network interface name (which you can get with ifconfig). Consider replacing the content of the authorized\_ssh\_clients macro by, for exemple, your home IP address, to avoid SSH connection attempts from bots.
|
||||||
|
|
||||||
|
Check pf's configuration by running `pfctl -nf /etc/pf.conf`, load it with `pfctl -f /etc/pf.conf` and enable pf at boot with `rcctl enable pf`.
|
||||||
|
|
||||||
|
#### Configure and start pleroma
|
||||||
|
Enter a shell as \_pleroma (as root `su _pleroma -`) and enter pleroma's installation directory (`cd ~/pleroma/`).
|
||||||
|
Then follow the main installation guide:
|
||||||
|
* run `mix deps.get`
|
||||||
|
* run `mix pleroma.instance gen` and enter your instance's information when asked
|
||||||
|
* copy config/generated\_config.exs to config/prod.secret.exs. The default values should be sufficient but you should edit it and check that everything seems OK.
|
||||||
|
* exit your current shell back to a root one and run `psql -U postgres -f /home/_pleroma/config/setup_db.psql` to setup the database.
|
||||||
|
* return to a \_pleroma shell into pleroma's installation directory (`su _pleroma -;cd ~/pleroma`) and run `MIX_ENV=prod mix ecto.migrate`
|
||||||
|
|
||||||
|
As \_pleroma in /home/\_pleroma/pleroma, you can now run `LC_ALL=en_US.UTF-8 MIX_ENV=prod mix phx.server` to start your instance.
|
||||||
|
In another SSH session/tmux window, check that it is working properly by running `ftp -MVo - http://127.0.0.1:4000/api/v1/instance`, you should get json output. Double-check that *uri*'s value is your instance's domain name.
|
||||||
|
|
||||||
|
##### Starting pleroma at boot
|
||||||
|
An rc script to automatically start pleroma at boot hasn't been written yet, it can be run in a tmux session (tmux is in base).
|
|
@ -0,0 +1,110 @@
|
||||||
|
# Pleroman asennus OpenBSD:llä
|
||||||
|
|
||||||
|
Tarvitset:
|
||||||
|
* Oman domainin
|
||||||
|
* OpenBSD 6.3 -serverin
|
||||||
|
* Auttavan ymmärryksen unix-järjestelmistä
|
||||||
|
|
||||||
|
Komennot, joiden edessä on '#', tulee ajaa käyttäjänä `root`. Tämä on
|
||||||
|
suositeltavaa tehdä komennon `doas` avulla, katso `doas (1)` ja `doas.conf (5)`.
|
||||||
|
Tästä eteenpäin oletuksena on, että domain "esimerkki.com" osoittaa
|
||||||
|
serverin IP-osoitteeseen.
|
||||||
|
|
||||||
|
Jos asennuksen kanssa on ongelmia, IRC-kanava #pleroma Freenodessa tai
|
||||||
|
Matrix-kanava #freenode_#pleroma:matrix.org ovat hyviä paikkoja löytää apua
|
||||||
|
(englanniksi), `/msg eal kukkuu` jos haluat välttämättä puhua härmää.
|
||||||
|
|
||||||
|
Asenna tarvittava ohjelmisto:
|
||||||
|
|
||||||
|
`# pkg_add git elixir gmake postgresql-server-10.3 postgresql-contrib-10.3`
|
||||||
|
|
||||||
|
Luo postgresql-tietokanta:
|
||||||
|
|
||||||
|
`# su - _postgresql`
|
||||||
|
|
||||||
|
`$ mkdir /var/postgresql/data`
|
||||||
|
|
||||||
|
`$ initdb -D /var/postgresql/data -E UTF8`
|
||||||
|
|
||||||
|
`$ createdb`
|
||||||
|
|
||||||
|
Käynnistä tietokanta ja aseta se käynnistymään automaattisesti.
|
||||||
|
|
||||||
|
`# rcctl start postgresql`
|
||||||
|
|
||||||
|
`# rcctl enable postgresql`
|
||||||
|
|
||||||
|
Luo käyttäjä pleromaa varten (kysyy muutaman kysymyksen):
|
||||||
|
|
||||||
|
`# adduser pleroma`
|
||||||
|
|
||||||
|
Vaihda pleroma-käyttäjään ja mene kotihakemistoosi:
|
||||||
|
|
||||||
|
`# su - pleroma`
|
||||||
|
|
||||||
|
Lataa pleroman lähdekoodi:
|
||||||
|
|
||||||
|
`$ git clone https://git.pleroma.social/pleroma/pleroma.git`
|
||||||
|
|
||||||
|
`$ cd pleroma`
|
||||||
|
|
||||||
|
Asenna tarvittavat elixir-kirjastot:
|
||||||
|
|
||||||
|
`$ mix deps.get`
|
||||||
|
|
||||||
|
`$ mix deps.compile`
|
||||||
|
|
||||||
|
Luo tarvittava konfiguraatio:
|
||||||
|
|
||||||
|
`$ mix generate_config`
|
||||||
|
|
||||||
|
`$ cp config/generated_config.exs config/prod.secret.exs`
|
||||||
|
|
||||||
|
Aja luodut tietokantakomennot:
|
||||||
|
|
||||||
|
`# su _postgres -c 'psql -f config/setup_db.psql'`
|
||||||
|
|
||||||
|
`$ MIX_ENV=prod mix ecto.migrate`
|
||||||
|
|
||||||
|
Käynnistä pleroma-prosessi:
|
||||||
|
|
||||||
|
`$ MIX_ENV=prod mix compile`
|
||||||
|
|
||||||
|
`$ MIX_ENV=prod mix phx.server`
|
||||||
|
|
||||||
|
Tässä vaiheessa on hyvä tarkistaa että asetukset ovat oikein. Avaa selaimella,
|
||||||
|
curlilla tai vastaavalla työkalulla `esimerkki.com:4000/api/v1/instance` ja katso
|
||||||
|
että kohta "uri" on "https://esimerkki.com".
|
||||||
|
|
||||||
|
Huom! Muista varmistaa että muuttuja MIX_ENV on "prod" mix-komentoja ajaessasi.
|
||||||
|
Mix lukee oikean konfiguraatiotiedoston sen mukaisesti.
|
||||||
|
|
||||||
|
Ohessa enimmäkseen toimivaksi todettu rc.d-skripti pleroman käynnistämiseen.
|
||||||
|
Kirjoita se tiedostoon /etc/rc.d/pleroma. Tämän jälkeen aja
|
||||||
|
`# chmod +x /etc/rc.d/pleroma`, ja voit käynnistää pleroman komennolla
|
||||||
|
`# /etc/rc.d/pleroma start`.
|
||||||
|
|
||||||
|
```
|
||||||
|
#!/bin/ksh
|
||||||
|
#/etc/rc.d/pleroma
|
||||||
|
|
||||||
|
daemon="cd /home/pleroma/pleroma;MIX_ENV=prod /usr/local/bin/elixir"
|
||||||
|
daemon_flags="--detached /usr/local/bin/mix phx.server"
|
||||||
|
daemon_user="pleroma"
|
||||||
|
rc_reload="NO"
|
||||||
|
rc_bg="YES"
|
||||||
|
|
||||||
|
pexp="beam"
|
||||||
|
|
||||||
|
. /etc/rc.d/rc.subr
|
||||||
|
|
||||||
|
rc_cmd $1
|
||||||
|
```
|
||||||
|
|
||||||
|
Tämän jälkeen tarvitset enää HTTP-serverin välittämään kutsut pleroma-prosessille.
|
||||||
|
Tiedostosta `install/pleroma.nginx` löytyy esimerkkikonfiguraatio, ja TLS-sertifikaatit
|
||||||
|
saat ilmaiseksi esimerkiksi [letsencryptiltä](https://certbot.eff.org/lets-encrypt/opbsd-nginx.html).
|
||||||
|
Nginx asentuu yksinkertaisesti komennolla `# pkg_add nginx`.
|
||||||
|
|
||||||
|
Kun olet valmis, avaa https://esimerkki.com selaimessasi. Luo käyttäjä ja seuraa kiinnostavia
|
||||||
|
tyyppejä muilla palvelimilla!
|
|
@ -0,0 +1,55 @@
|
||||||
|
# Introduction to Pleroma
|
||||||
|
## What is Pleroma?
|
||||||
|
Pleroma is a federated social networking platform, compatible with GNU social, Mastodon and other OStatus and ActivityPub implementations. It is free software licensed under the AGPLv3.
|
||||||
|
It actually consists of two components: a backend, named simply Pleroma, and a user-facing frontend, named Pleroma-FE. It also includes the Mastodon frontend, if that's your thing.
|
||||||
|
It's part of what we call the fediverse, a federated network of instances which speak common protocols and can communicate with each other.
|
||||||
|
One account on a instance is enough to talk to the entire fediverse!
|
||||||
|
|
||||||
|
## How can I use it?
|
||||||
|
|
||||||
|
Pleroma instances are already widely deployed, a list can be found here:
|
||||||
|
http://distsn.org/pleroma-instances.html
|
||||||
|
|
||||||
|
If you don't feel like joining an existing instance, but instead prefer to deploy your own instance, that's easy too!
|
||||||
|
Installation instructions can be found here:
|
||||||
|
[main Pleroma wiki](/)
|
||||||
|
|
||||||
|
## I got an account, now what?
|
||||||
|
Great! Now you can explore the fediverse!
|
||||||
|
- Open the login page for your Pleroma instance (for ex. https://pleroma.soykaf.com) and login with your username and password.
|
||||||
|
(If you don't have one yet, click on Register) :slightly_smiling_face:
|
||||||
|
|
||||||
|
At this point you will have two columns in front of you.
|
||||||
|
|
||||||
|
### Left column
|
||||||
|
- first block: here you can see your avatar, your nickname a bio, and statistics (Statuses, Following, Followers).
|
||||||
|
Under that you have a text form which allows you to post new statuses. The icon on the left is for uploading media files and attach them to your post. The number under the text form is a character counter, every instance can have a different character limit (the default is 5000).
|
||||||
|
If you want to mention someone, type @ + name of the person. A drop-down menu will help you in finding the right person. :slight_smile:
|
||||||
|
To post your status, simply press Submit.
|
||||||
|
|
||||||
|
- second block: Here you can switch between the different timelines:
|
||||||
|
- Timeline: all the people that you follow
|
||||||
|
- Mentions: all the statutes where you are mentioned
|
||||||
|
- Public Timeline: all the statutes from the local instance
|
||||||
|
- The Whole Known Network: everything, local and remote!
|
||||||
|
|
||||||
|
- third block: this is the Chat block, where you communicate with people on the same instance in realtime. It is local-only, for now, but we're planning to make it extendable to the entire fediverse! :sweat_smile:
|
||||||
|
|
||||||
|
- fourth block: This is the Notifications block, here you will get notified whenever somebody mentions you, follows you, repeats or favorites one of your statuses.
|
||||||
|
|
||||||
|
### Right column
|
||||||
|
This is where the interesting stuff happens! :slight_smile:
|
||||||
|
Depending on the timeline you will see different statuses, but each status has a standard structure:
|
||||||
|
- Icon + name + link to profile. An optional left-arrow if it's a reply to another status (hovering will reveal the replied-to status).
|
||||||
|
- A + button on the right allows you to Expand/Collapse an entire discussion thread. It also updates in realtime!
|
||||||
|
- A binocular icon allows you to open the status on the instance where it's originating from.
|
||||||
|
- The text of the status, including mentions. If you click on a mention, it will automatically open the profile page of that person.
|
||||||
|
- Four buttons (left to right): Reply, Repeat, Favorite, Delete.
|
||||||
|
|
||||||
|
## Mastodon interface
|
||||||
|
If the Pleroma interface isn't your thing, or you're just trying something new but you want to keep using the familiar Mastodon interface, we got that too! :smile:
|
||||||
|
Just add a "/web" after your instance url (for ex. https://pleroma.soycaf.com/web) and you'll end on the Mastodon web interface, but with a Pleroma backend! MAGIC! :fireworks:
|
||||||
|
For more information on the Mastodon interface, please look here:
|
||||||
|
https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/User-guide.md
|
||||||
|
|
||||||
|
Remember, what you see is only the frontend part of Mastodon, the backend is still Pleroma.
|
|
@ -23,6 +23,11 @@ example.tld {
|
||||||
|
|
||||||
# If you do not want to use the mediaproxy function, remove these lines.
|
# If you do not want to use the mediaproxy function, remove these lines.
|
||||||
# To use this directive, you need the http.cache plugin for Caddy.
|
# To use this directive, you need the http.cache plugin for Caddy.
|
||||||
|
cache {
|
||||||
|
match_path /media
|
||||||
|
default_max_age 720m
|
||||||
|
}
|
||||||
|
|
||||||
cache {
|
cache {
|
||||||
match_path /proxy
|
match_path /proxy
|
||||||
default_max_age 720m
|
default_max_age 720m
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/sbin/openrc-run
|
#!/sbin/openrc-run
|
||||||
|
|
||||||
# Requires OpenRC >= 0.35
|
# Requires OpenRC >= 0.35
|
||||||
directory=~pleroma/pleroma
|
directory=/opt/pleroma
|
||||||
|
|
||||||
command=/usr/bin/mix
|
command=/usr/bin/mix
|
||||||
command_args="phx.server"
|
command_args="phx.server"
|
||||||
|
@ -12,7 +12,7 @@ export PORT=4000
|
||||||
export MIX_ENV=prod
|
export MIX_ENV=prod
|
||||||
|
|
||||||
# Ask process to terminate within 30 seconds, otherwise kill it
|
# Ask process to terminate within 30 seconds, otherwise kill it
|
||||||
retry="SIGTERM/30 SIGKILL/5"
|
retry="SIGTERM/30/SIGKILL/5"
|
||||||
|
|
||||||
pidfile="/var/run/pleroma.pid"
|
pidfile="/var/run/pleroma.pid"
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# PROVIDE: pleroma
|
||||||
|
# REQUIRE: DAEMON pgsql
|
||||||
|
|
||||||
|
if [ -f /etc/rc.subr ]; then
|
||||||
|
. /etc/rc.subr
|
||||||
|
fi
|
||||||
|
|
||||||
|
name="pleroma"
|
||||||
|
rcvar=${name}
|
||||||
|
command="/usr/pkg/bin/elixir"
|
||||||
|
command_args="--detached -S /usr/pkg/bin/mix phx.server"
|
||||||
|
start_precmd="ulimit -n unlimited"
|
||||||
|
pidfile="/dev/null"
|
||||||
|
|
||||||
|
pleroma_chdir="${pleroma_home}/pleroma"
|
||||||
|
pleroma_env="HOME=${pleroma_home} MIX_ENV=prod"
|
||||||
|
|
||||||
|
check_pidfile()
|
||||||
|
{
|
||||||
|
pid=$(pgrep -U "${pleroma_user}" /bin/beam.smp$)
|
||||||
|
echo -n "${pid}"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -f /etc/rc.subr -a -d /etc/rc.d -a -f /etc/rc.d/DAEMON ]; then
|
||||||
|
# newer NetBSD
|
||||||
|
load_rc_config ${name}
|
||||||
|
run_rc_command "$1"
|
||||||
|
else
|
||||||
|
# ancient NetBSD, Solaris and illumos, Linux, etc...
|
||||||
|
cmd=${1:-start}
|
||||||
|
|
||||||
|
case ${cmd} in
|
||||||
|
start)
|
||||||
|
echo "Starting ${name}."
|
||||||
|
${start_cmd}
|
||||||
|
;;
|
||||||
|
|
||||||
|
stop)
|
||||||
|
echo "Stopping ${name}."
|
||||||
|
check_pidfile
|
||||||
|
! [ -n ${pid} ] && kill ${pid}
|
||||||
|
;;
|
||||||
|
|
||||||
|
restart)
|
||||||
|
( $0 stop )
|
||||||
|
sleep 5
|
||||||
|
$0 start
|
||||||
|
;;
|
||||||
|
|
||||||
|
*)
|
||||||
|
echo 1>&2 "Usage: $0 [start|stop|restart]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
exit 0
|
||||||
|
fi
|
|
@ -0,0 +1,36 @@
|
||||||
|
#
|
||||||
|
# Default httpd.conf file for Pleroma on OpenBSD
|
||||||
|
# Simple installation instructions
|
||||||
|
# 1. Place file in /etc
|
||||||
|
# 2. Replace <IPv4 address> with your public IP address
|
||||||
|
# 3. If using IPv6, uncomment IPv6 lines and replace <IPv6 address> with your public IPv6 address
|
||||||
|
# 4. Check file using 'doas httpd -n'
|
||||||
|
# 5. Enable and start httpd:
|
||||||
|
# # doas rcctl enable httpd
|
||||||
|
# # doas rcctl start httpd
|
||||||
|
#
|
||||||
|
|
||||||
|
ext_inet="<IPv4 address>"
|
||||||
|
#ext_inet6="<IPv6 address>"
|
||||||
|
|
||||||
|
server "default" {
|
||||||
|
listen on $ext_inet port 80 # Comment to disable listening on IPv4
|
||||||
|
# listen on $ext_inet6 port 80 # Comment to disable listening on IPv6
|
||||||
|
listen on 127.0.0.1 port 80 # Do NOT comment this line
|
||||||
|
|
||||||
|
log syslog
|
||||||
|
directory no index
|
||||||
|
|
||||||
|
location "/.well-known/acme-challenge/*" {
|
||||||
|
root "/acme"
|
||||||
|
request strip 2
|
||||||
|
}
|
||||||
|
|
||||||
|
location "/robots.txt" { root "/htdocs/local/" }
|
||||||
|
location "/*" { block return 302 "https://$HTTP_HOST$REQUEST_URI" }
|
||||||
|
}
|
||||||
|
|
||||||
|
types {
|
||||||
|
include "/usr/share/misc/mime.types"
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
#!/bin/ksh
|
||||||
|
#
|
||||||
|
# Default init file for Pleroma on OpenBSD
|
||||||
|
#
|
||||||
|
# Simple installation instructions:
|
||||||
|
# 1. Install Pleroma per wiki instructions
|
||||||
|
# 2. Place this pleromad file in /etc/rc.d
|
||||||
|
# 3. Enable and start Pleroma
|
||||||
|
# # doas rcctl enable pleromad
|
||||||
|
# # doas rcctl start pleromad
|
||||||
|
#
|
||||||
|
|
||||||
|
daemon="/usr/local/bin/elixir"
|
||||||
|
daemon_flags="--detached -S /usr/local/bin/mix phx.server"
|
||||||
|
daemon_user="_pleroma"
|
||||||
|
|
||||||
|
. /etc/rc.d/rc.subr
|
||||||
|
|
||||||
|
rc_reload=NO
|
||||||
|
pexp="phx.server"
|
||||||
|
|
||||||
|
rc_check() {
|
||||||
|
pgrep -q -U _pleroma -f "phx.server"
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_start() {
|
||||||
|
${rcexec} "cd pleroma; ${daemon} ${daemon_flags}"
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_stop() {
|
||||||
|
pkill -q -U _pleroma -f "phx.server"
|
||||||
|
}
|
||||||
|
|
||||||
|
rc_cmd $1
|
|
@ -0,0 +1,44 @@
|
||||||
|
#
|
||||||
|
# Default relayd.conf file for Pleroma on OpenBSD
|
||||||
|
# Simple installation instructions:
|
||||||
|
# 1. Place in /etc
|
||||||
|
# 2. Replace <ipaddr> with your public IPv4 address
|
||||||
|
# 3. If using IPv6i, uncomment IPv6 lines and replace <ip6addr> with your public IPv6 address
|
||||||
|
# 4. Check file using 'doas relayd -n'
|
||||||
|
# 5. Reload/start relayd
|
||||||
|
# # doas rcctl enable relayd
|
||||||
|
# # doas rcctl start relayd
|
||||||
|
#
|
||||||
|
|
||||||
|
ext_inet="<ipaddr>"
|
||||||
|
#ext_inet6="<ip6addr>"
|
||||||
|
|
||||||
|
table <pleroma_server> { 127.0.0.1 }
|
||||||
|
table <httpd_server> { 127.0.0.1 }
|
||||||
|
|
||||||
|
http protocol plerup { # Protocol for upstream pleroma server
|
||||||
|
#tcp { nodelay, sack, socket buffer 65536, backlog 128 } # Uncomment and adjust as you see fit
|
||||||
|
tls ciphers "ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA0-POLY1305"
|
||||||
|
tls ecdhe secp384r1
|
||||||
|
|
||||||
|
# Forward some paths to the local server (as pleroma won't respond to them as you might want)
|
||||||
|
pass request quick path "/robots.txt" forward to <httpd_server>
|
||||||
|
|
||||||
|
# Append a bunch of headers
|
||||||
|
match request header append "X-Forwarded-For" value "$REMOTE_ADDR" # This two header and the next one are not strictl required by pleroma but adding them won't hurt
|
||||||
|
match request header append "X-Forwarded-By" value "$SERVER_ADDR:$SERVER_PORT"
|
||||||
|
|
||||||
|
match request header append "Connection" value "upgrade"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
relay wwwtls {
|
||||||
|
listen on $ext_inet port https tls # Comment to disable listening on IPv4
|
||||||
|
# listen on $ext_inet6 port https tls # Comment to disable listening on IPv6
|
||||||
|
|
||||||
|
protocol plerup
|
||||||
|
|
||||||
|
forward to <pleroma_server> port 4000 check http "/" code 200
|
||||||
|
forward to <httpd_server> port 80 check http "/robots.txt" code 200
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# default Apache site config for Pleroma
|
# default Apache site config for Pleroma
|
||||||
#
|
#
|
||||||
# needed modules: define headers proxy proxy_http proxy_wstunnel rewrite ssl
|
# needed modules: define headers proxy proxy_http proxy_wstunnel rewrite ssl
|
||||||
|
# optional modules: cache cache_disk
|
||||||
#
|
#
|
||||||
# Simple installation instructions:
|
# Simple installation instructions:
|
||||||
# 1. Install your TLS certificate, possibly using Let's Encrypt.
|
# 1. Install your TLS certificate, possibly using Let's Encrypt.
|
||||||
|
@ -8,6 +9,14 @@
|
||||||
# 3. This assumes a Debian style Apache config. Copy this file to
|
# 3. This assumes a Debian style Apache config. Copy this file to
|
||||||
# /etc/apache2/sites-available/ and then add a symlink to it in
|
# /etc/apache2/sites-available/ and then add a symlink to it in
|
||||||
# /etc/apache2/sites-enabled/ by running 'a2ensite pleroma-apache.conf', then restart Apache.
|
# /etc/apache2/sites-enabled/ by running 'a2ensite pleroma-apache.conf', then restart Apache.
|
||||||
|
#
|
||||||
|
# Optional: enable disk-based caching for the media proxy
|
||||||
|
# For details, see https://git.pleroma.social/pleroma/pleroma/wikis/How%20to%20activate%20mediaproxy
|
||||||
|
#
|
||||||
|
# 1. Create the directory listed below as the CacheRoot, and make sure
|
||||||
|
# the Apache user can write to it.
|
||||||
|
# 2. Configure Apache's htcacheclean to clean the directory periodically.
|
||||||
|
# 3. Run 'a2enmod cache cache_disk' and restart Apache.
|
||||||
|
|
||||||
Define servername example.tld
|
Define servername example.tld
|
||||||
|
|
||||||
|
@ -34,6 +43,15 @@ CustomLog ${APACHE_LOG_DIR}/access.log combined
|
||||||
SSLCompression off
|
SSLCompression off
|
||||||
SSLSessionTickets off
|
SSLSessionTickets off
|
||||||
|
|
||||||
|
# uncomment the following to enable mediaproxy caching on disk
|
||||||
|
# <IfModule mod_cache_disk.c>
|
||||||
|
# CacheRoot /var/cache/apache2/mod_cache_disk
|
||||||
|
# CacheDirLevels 1
|
||||||
|
# CacheDirLength 2
|
||||||
|
# CacheEnable disk /proxy
|
||||||
|
# CacheLock on
|
||||||
|
# </IfModule>
|
||||||
|
|
||||||
RewriteEngine On
|
RewriteEngine On
|
||||||
RewriteCond %{HTTP:Connection} Upgrade [NC]
|
RewriteCond %{HTTP:Connection} Upgrade [NC]
|
||||||
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
RewriteCond %{HTTP:Upgrade} websocket [NC]
|
||||||
|
|
|
@ -11,16 +11,19 @@ proxy_cache_path /tmp/pleroma-media-cache levels=1:2 keys_zone=pleroma_media_cac
|
||||||
|
|
||||||
server {
|
server {
|
||||||
server_name example.tld;
|
server_name example.tld;
|
||||||
|
|
||||||
listen 80;
|
listen 80;
|
||||||
|
listen [::]:80;
|
||||||
return 301 https://$server_name$request_uri;
|
return 301 https://$server_name$request_uri;
|
||||||
|
|
||||||
# Uncomment this if you need to use the 'webroot' method with certbot. Make sure
|
# Uncomment this if you need to use the 'webroot' method with certbot. Make sure
|
||||||
# that you also create the .well-known/acme-challenge directory structure in pleroma/priv/static and
|
# that the directory exists and that it is accessible by the webserver. If you followed
|
||||||
# that is is accessible by the webserver. You may need to load this file with the ssl
|
# the guide, you already ran 'sudo mkdir -p /var/lib/letsencrypt' to create the folder.
|
||||||
# server block commented out, run certbot to get the certificate, and then uncomment it.
|
# You may need to load this file with the ssl server block commented out, run certbot
|
||||||
|
# to get the certificate, and then uncomment it.
|
||||||
#
|
#
|
||||||
# location ~ /\.well-known/acme-challenge {
|
# location ~ /\.well-known/acme-challenge {
|
||||||
# root <path to install>/pleroma/priv/static/;
|
# root /var/lib/letsencrypt/.well-known/acme-challenge;
|
||||||
# }
|
# }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,10 +31,13 @@ server {
|
||||||
ssl_session_cache shared:ssl_session_cache:10m;
|
ssl_session_cache shared:ssl_session_cache:10m;
|
||||||
|
|
||||||
server {
|
server {
|
||||||
|
server_name example.tld;
|
||||||
|
|
||||||
listen 443 ssl http2;
|
listen 443 ssl http2;
|
||||||
|
listen [::]:443 ssl http2;
|
||||||
ssl_session_timeout 5m;
|
ssl_session_timeout 5m;
|
||||||
|
|
||||||
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
ssl_trusted_certificate /etc/letsencrypt/live/example.tld/chain.pem;
|
||||||
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
ssl_certificate /etc/letsencrypt/live/example.tld/fullchain.pem;
|
||||||
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
|
ssl_certificate_key /etc/letsencrypt/live/example.tld/privkey.pem;
|
||||||
|
|
||||||
|
@ -47,8 +53,6 @@ server {
|
||||||
ssl_stapling on;
|
ssl_stapling on;
|
||||||
ssl_stapling_verify on;
|
ssl_stapling_verify on;
|
||||||
|
|
||||||
server_name example.tld;
|
|
||||||
|
|
||||||
gzip_vary on;
|
gzip_vary on;
|
||||||
gzip_proxied any;
|
gzip_proxied any;
|
||||||
gzip_comp_level 6;
|
gzip_comp_level 6;
|
||||||
|
@ -72,10 +76,17 @@ server {
|
||||||
|
|
||||||
location ~ ^/(media|proxy) {
|
location ~ ^/(media|proxy) {
|
||||||
proxy_cache pleroma_media_cache;
|
proxy_cache pleroma_media_cache;
|
||||||
|
slice 1m;
|
||||||
|
proxy_cache_key $host$uri$is_args$args$slice_range;
|
||||||
|
proxy_set_header Range $slice_range;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_cache_valid 200 206 301 304 1h;
|
||||||
proxy_cache_lock on;
|
proxy_cache_lock on;
|
||||||
proxy_ignore_client_abort on;
|
proxy_ignore_client_abort on;
|
||||||
proxy_buffering off;
|
proxy_buffering on;
|
||||||
chunked_transfer_encoding on;
|
chunked_transfer_encoding on;
|
||||||
|
proxy_ignore_headers Cache-Control;
|
||||||
|
proxy_hide_header Cache-Control;
|
||||||
proxy_pass http://localhost:4000;
|
proxy_pass http://localhost:4000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,24 +3,36 @@ Description=Pleroma social network
|
||||||
After=network.target postgresql.service
|
After=network.target postgresql.service
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
User=pleroma
|
|
||||||
WorkingDirectory=/home/pleroma/pleroma
|
|
||||||
Environment="HOME=/home/pleroma"
|
|
||||||
Environment="MIX_ENV=prod"
|
|
||||||
ExecStart=/usr/local/bin/mix phx.server
|
|
||||||
ExecReload=/bin/kill $MAINPID
|
ExecReload=/bin/kill $MAINPID
|
||||||
KillMode=process
|
KillMode=process
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
|
|
||||||
|
; Name of the user that runs the Pleroma service.
|
||||||
|
User=pleroma
|
||||||
|
; Declares that Pleroma runs in production mode.
|
||||||
|
Environment="MIX_ENV=prod"
|
||||||
|
|
||||||
|
; Make sure that all paths fit your installation.
|
||||||
|
; Path to the home directory of the user running the Pleroma service.
|
||||||
|
Environment="HOME=/var/lib/pleroma"
|
||||||
|
; Path to the folder containing the Pleroma installation.
|
||||||
|
WorkingDirectory=/opt/pleroma
|
||||||
|
; Path to the Mix binary.
|
||||||
|
ExecStart=/usr/bin/mix phx.server
|
||||||
|
|
||||||
; Some security directives.
|
; Some security directives.
|
||||||
; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.
|
; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.
|
||||||
PrivateTmp=true
|
PrivateTmp=true
|
||||||
|
; The /home, /root, and /run/user folders can not be accessed by this service anymore. If your Pleroma user has its home folder in one of the restricted places, or use one of these folders as its working directory, you have to set this to false.
|
||||||
|
ProtectHome=true
|
||||||
; Mount /usr, /boot, and /etc as read-only for processes invoked by this service.
|
; Mount /usr, /boot, and /etc as read-only for processes invoked by this service.
|
||||||
ProtectSystem=full
|
ProtectSystem=full
|
||||||
; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi.
|
; Sets up a new /dev mount for the process and only adds API pseudo devices like /dev/null, /dev/zero or /dev/random but not physical devices. Disabled by default because it may not work on devices like the Raspberry Pi.
|
||||||
PrivateDevices=false
|
PrivateDevices=false
|
||||||
; Ensures that the service process and all its children can never gain new privileges through execve().
|
; Ensures that the service process and all its children can never gain new privileges through execve().
|
||||||
NoNewPrivileges=true
|
NoNewPrivileges=true
|
||||||
|
; Drops the sysadmin capability from the daemon.
|
||||||
|
CapabilityBoundingSet=~CAP_SYS_ADMIN
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=multi-user.target
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
; Assumes pleroma is installed in /home/pleroma/pleroma and running as the pleroma user
|
||||||
|
; Also assumes mix is in /usr/bin, this might differ on BSDs or niche Linux distros
|
||||||
|
; Logs into /home/pleroma/logs
|
||||||
|
[program:pleroma]
|
||||||
|
command=/usr/bin/mix phx.server
|
||||||
|
directory=/home/pleroma/pleroma
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
user=pleroma
|
||||||
|
environment =
|
||||||
|
MIX_ENV=prod,
|
||||||
|
HOME=/home/pleroma,
|
||||||
|
USER=pleroma,
|
||||||
|
PATH="/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin:/home/pleroma/bin:%(ENV_PATH)s",
|
||||||
|
PWD=/home/pleroma/pleroma
|
||||||
|
stdout_logfile=/home/pleroma/logs/stdout.log
|
||||||
|
stdout_logfile_maxbytes=50MB
|
||||||
|
stdout_logfile_backups=10
|
||||||
|
stderr_logfile=/home/pleroma/logs/stderr.log
|
||||||
|
stderr_logfile_maxbytes=50MB
|
||||||
|
stderr_logfile_backups=10
|
|
@ -18,6 +18,11 @@ sub vcl_recv {
|
||||||
return (synth(750, ""));
|
return (synth(750, ""));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# CHUNKED SUPPORT
|
||||||
|
if (req.http.Range ~ "bytes=") {
|
||||||
|
set req.http.x-range = req.http.Range;
|
||||||
|
}
|
||||||
|
|
||||||
# Pipe if WebSockets request is coming through
|
# Pipe if WebSockets request is coming through
|
||||||
if (req.http.upgrade ~ "(?i)websocket") {
|
if (req.http.upgrade ~ "(?i)websocket") {
|
||||||
return (pipe);
|
return (pipe);
|
||||||
|
@ -42,15 +47,12 @@ sub vcl_recv {
|
||||||
# Strip headers that will affect caching from all other static content
|
# Strip headers that will affect caching from all other static content
|
||||||
# This also permits caching of individual toots and AP Activities
|
# This also permits caching of individual toots and AP Activities
|
||||||
if ((req.url ~ "^/(media|static)/") ||
|
if ((req.url ~ "^/(media|static)/") ||
|
||||||
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$"))
|
(req.url ~ "(?i)\.(html|js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|mp4|ogg|webm|svg|swf|ttf|pdf|woff|woff2)$"))
|
||||||
{
|
{
|
||||||
unset req.http.Cookie;
|
unset req.http.Cookie;
|
||||||
unset req.http.Authorization;
|
unset req.http.Authorization;
|
||||||
return (hash);
|
return (hash);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Everything else should just be piped to Pleroma
|
|
||||||
return (pipe);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sub vcl_backend_response {
|
sub vcl_backend_response {
|
||||||
|
@ -59,8 +61,11 @@ sub vcl_backend_response {
|
||||||
set beresp.do_gzip = true;
|
set beresp.do_gzip = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
# etags are bad
|
# CHUNKED SUPPORT
|
||||||
unset beresp.http.etag;
|
if (bereq.http.x-range ~ "bytes=" && beresp.status == 206) {
|
||||||
|
set beresp.ttl = 10m;
|
||||||
|
set beresp.http.CR = beresp.http.content-range;
|
||||||
|
}
|
||||||
|
|
||||||
# Don't cache objects that require authentication
|
# Don't cache objects that require authentication
|
||||||
if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") {
|
if (beresp.http.Authorization && !beresp.http.Cache-Control ~ "public") {
|
||||||
|
@ -92,14 +97,12 @@ sub vcl_backend_response {
|
||||||
}
|
}
|
||||||
|
|
||||||
# Strip cache-restricting headers from Pleroma on static content that we want to cache
|
# Strip cache-restricting headers from Pleroma on static content that we want to cache
|
||||||
# Also enable streaming of cached content to clients (no waiting for Varnish to complete backend fetch)
|
if (bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|mp4|ogg|webm|svg|swf|ttf|pdf|woff|woff2)$")
|
||||||
if (bereq.url ~ "(?i)\.(js|css|jpg|jpeg|png|gif|gz|tgz|bz2|tbz|mp3|ogg|svg|swf|ttf|pdf|woff|woff2)$")
|
|
||||||
{
|
{
|
||||||
unset beresp.http.set-cookie;
|
unset beresp.http.set-cookie;
|
||||||
unset beresp.http.Cache-Control;
|
unset beresp.http.Cache-Control;
|
||||||
unset beresp.http.x-request-id;
|
unset beresp.http.x-request-id;
|
||||||
set beresp.http.Cache-Control = "public, max-age=86400";
|
set beresp.http.Cache-Control = "public, max-age=86400";
|
||||||
set beresp.do_stream = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -119,3 +122,26 @@ sub vcl_pipe {
|
||||||
set bereq.http.connection = req.http.connection;
|
set bereq.http.connection = req.http.connection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub vcl_hash {
|
||||||
|
# CHUNKED SUPPORT
|
||||||
|
if (req.http.x-range ~ "bytes=") {
|
||||||
|
hash_data(req.http.x-range);
|
||||||
|
unset req.http.Range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_backend_fetch {
|
||||||
|
# CHUNKED SUPPORT
|
||||||
|
if (bereq.http.x-range) {
|
||||||
|
set bereq.http.Range = bereq.http.x-range;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub vcl_deliver {
|
||||||
|
# CHUNKED SUPPORT
|
||||||
|
if (resp.http.CR) {
|
||||||
|
set resp.http.Content-Range = resp.http.CR;
|
||||||
|
unset resp.http.CR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,9 +6,9 @@ defmodule Mix.Tasks.CompactDatabase do
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
import Mix.Ecto
|
|
||||||
import Ecto.Query
|
import Ecto.Query
|
||||||
alias Pleroma.{Repo, Object, Activity}
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
defp maybe_compact(%Activity{data: %{"object" => %{"id" => object_id}}} = activity) do
|
defp maybe_compact(%Activity{data: %{"object" => %{"id" => object_id}}} = activity) do
|
||||||
data =
|
data =
|
||||||
|
@ -33,7 +33,7 @@ defp activity_query(min_id, max_id) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def run(args) do
|
def run(_args) do
|
||||||
Application.ensure_all_started(:pleroma)
|
Application.ensure_all_started(:pleroma)
|
||||||
|
|
||||||
max = Repo.aggregate(Activity, :max, :id)
|
max = Repo.aggregate(Activity, :max, :id)
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
defmodule Mix.Tasks.DeactivateUser do
|
|
||||||
use Mix.Task
|
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Deactivates a user (local or remote)
|
|
||||||
|
|
||||||
Usage: ``mix deactivate_user <nickname>``
|
|
||||||
|
|
||||||
Example: ``mix deactivate_user lain``
|
|
||||||
"""
|
|
||||||
def run([nickname]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with user <- User.get_by_nickname(nickname) do
|
|
||||||
User.deactivate(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,47 +0,0 @@
|
||||||
defmodule Mix.Tasks.GenerateConfig do
|
|
||||||
use Mix.Task
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Generate a new config
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
``mix generate_config``
|
|
||||||
|
|
||||||
This mix task is interactive, and will overwrite the config present at ``config/generated_config.exs``.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def run(_) do
|
|
||||||
IO.puts("Answer a few questions to generate a new config\n")
|
|
||||||
IO.puts("--- THIS WILL OVERWRITE YOUR config/generated_config.exs! ---\n")
|
|
||||||
domain = IO.gets("What is your domain name? (e.g. pleroma.soykaf.com): ") |> String.trim()
|
|
||||||
name = IO.gets("What is the name of your instance? (e.g. Pleroma/Soykaf): ") |> String.trim()
|
|
||||||
email = IO.gets("What's your admin email address: ") |> String.trim()
|
|
||||||
|
|
||||||
secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
|
||||||
dbpass = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64)
|
|
||||||
|
|
||||||
resultSql = EEx.eval_file("lib/mix/tasks/sample_psql.eex", dbpass: dbpass)
|
|
||||||
|
|
||||||
result =
|
|
||||||
EEx.eval_file(
|
|
||||||
"lib/mix/tasks/sample_config.eex",
|
|
||||||
domain: domain,
|
|
||||||
email: email,
|
|
||||||
name: name,
|
|
||||||
secret: secret,
|
|
||||||
dbpass: dbpass
|
|
||||||
)
|
|
||||||
|
|
||||||
IO.puts(
|
|
||||||
"\nWriting config to config/generated_config.exs.\n\nCheck it and configure your database, then copy it to either config/dev.secret.exs or config/prod.secret.exs"
|
|
||||||
)
|
|
||||||
|
|
||||||
File.write("config/generated_config.exs", result)
|
|
||||||
|
|
||||||
IO.puts(
|
|
||||||
"\nWriting setup_db.psql, please run it as postgre superuser, i.e.: sudo su postgres -c 'psql -f config/setup_db.psql'"
|
|
||||||
)
|
|
||||||
|
|
||||||
File.write("config/setup_db.psql", resultSql)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,32 +0,0 @@
|
||||||
defmodule Mix.Tasks.GenerateInviteToken do
|
|
||||||
use Mix.Task
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Generates invite token
|
|
||||||
|
|
||||||
This is in the form of a URL to be used by the Invited user to register themselves.
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
``mix generate_invite_token``
|
|
||||||
"""
|
|
||||||
def run([]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with {:ok, token} <- Pleroma.UserInviteToken.create_token() do
|
|
||||||
IO.puts("Generated user invite token")
|
|
||||||
|
|
||||||
IO.puts(
|
|
||||||
"Url: #{
|
|
||||||
Pleroma.Web.Router.Helpers.redirect_url(
|
|
||||||
Pleroma.Web.Endpoint,
|
|
||||||
:registration_page,
|
|
||||||
token.token
|
|
||||||
)
|
|
||||||
}"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
IO.puts("Error creating token")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,33 +0,0 @@
|
||||||
defmodule Mix.Tasks.GeneratePasswordReset do
|
|
||||||
use Mix.Task
|
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Generate password reset link for user
|
|
||||||
|
|
||||||
Usage: ``mix generate_password_reset <nickname>``
|
|
||||||
|
|
||||||
Example: ``mix generate_password_reset lain``
|
|
||||||
"""
|
|
||||||
def run([nickname]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname),
|
|
||||||
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
|
|
||||||
IO.puts("Generated password reset token for #{user.nickname}")
|
|
||||||
|
|
||||||
IO.puts(
|
|
||||||
"Url: #{
|
|
||||||
Pleroma.Web.Router.Helpers.util_url(
|
|
||||||
Pleroma.Web.Endpoint,
|
|
||||||
:show_password_reset,
|
|
||||||
token.token
|
|
||||||
)
|
|
||||||
}"
|
|
||||||
)
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
IO.puts("No local user #{nickname}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,37 +0,0 @@
|
||||||
defmodule Mix.Tasks.SetModerator do
|
|
||||||
@moduledoc """
|
|
||||||
Set moderator to a local user
|
|
||||||
|
|
||||||
Usage: ``mix set_moderator <nickname>``
|
|
||||||
|
|
||||||
Example: ``mix set_moderator lain``
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Mix.Task
|
|
||||||
import Mix.Ecto
|
|
||||||
alias Pleroma.{Repo, User}
|
|
||||||
|
|
||||||
def run([nickname | rest]) do
|
|
||||||
Application.ensure_all_started(:pleroma)
|
|
||||||
|
|
||||||
moderator =
|
|
||||||
case rest do
|
|
||||||
[moderator] -> moderator == "true"
|
|
||||||
_ -> true
|
|
||||||
end
|
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
|
||||||
info =
|
|
||||||
user.info
|
|
||||||
|> Map.put("is_moderator", !!moderator)
|
|
||||||
|
|
||||||
cng = User.info_changeset(user, %{info: info})
|
|
||||||
{:ok, user} = User.update_and_set_cache(cng)
|
|
||||||
|
|
||||||
IO.puts("Moderator status of #{nickname}: #{user.info["is_moderator"]}")
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
IO.puts("No local user #{nickname}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.Common do
|
||||||
|
@doc "Common functions to be reused in mix tasks"
|
||||||
|
def start_pleroma do
|
||||||
|
Mix.Task.run("app.start")
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_option(options, opt, prompt, defval \\ nil, defname \\ nil) do
|
||||||
|
Keyword.get(options, opt) ||
|
||||||
|
case Mix.shell().prompt("#{prompt} [#{defname || defval}]") do
|
||||||
|
"\n" ->
|
||||||
|
case defval do
|
||||||
|
nil -> get_option(options, opt, prompt, defval)
|
||||||
|
defval -> defval
|
||||||
|
end
|
||||||
|
|
||||||
|
opt ->
|
||||||
|
opt |> String.trim()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def escape_sh_path(path) do
|
||||||
|
~S(') <> String.replace(path, ~S('), ~S(\')) <> ~S(')
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,213 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.Instance do
|
||||||
|
use Mix.Task
|
||||||
|
alias Mix.Tasks.Pleroma.Common
|
||||||
|
|
||||||
|
@shortdoc "Manages Pleroma instance"
|
||||||
|
@moduledoc """
|
||||||
|
Manages Pleroma instance.
|
||||||
|
|
||||||
|
## Generate a new instance config.
|
||||||
|
|
||||||
|
mix pleroma.instance gen [OPTION...]
|
||||||
|
|
||||||
|
If any 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 ADMIN_EMAIL` - the email address of the instance admin
|
||||||
|
- `--notify-email NOTIFY_EMAIL` - email address for notifications
|
||||||
|
- `--dbhost HOSTNAME` - the hostname of the PostgreSQL database to use
|
||||||
|
- `--dbname DBNAME` - the name of the database to use
|
||||||
|
- `--dbuser DBUSER` - the user (aka role) to use for the database connection
|
||||||
|
- `--dbpass DBPASS` - the password to use for the database connection
|
||||||
|
- `--indexable Y/N` - Allow/disallow indexing site by search engines
|
||||||
|
"""
|
||||||
|
|
||||||
|
def run(["gen" | rest]) do
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(
|
||||||
|
rest,
|
||||||
|
strict: [
|
||||||
|
force: :boolean,
|
||||||
|
output: :string,
|
||||||
|
output_psql: :string,
|
||||||
|
domain: :string,
|
||||||
|
instance_name: :string,
|
||||||
|
admin_email: :string,
|
||||||
|
notify_email: :string,
|
||||||
|
dbhost: :string,
|
||||||
|
dbname: :string,
|
||||||
|
dbuser: :string,
|
||||||
|
dbpass: :string,
|
||||||
|
indexable: :string
|
||||||
|
],
|
||||||
|
aliases: [
|
||||||
|
o: :output,
|
||||||
|
f: :force
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
paths =
|
||||||
|
[config_path, psql_path] = [
|
||||||
|
Keyword.get(options, :output, "config/generated_config.exs"),
|
||||||
|
Keyword.get(options, :output_psql, "config/setup_db.psql")
|
||||||
|
]
|
||||||
|
|
||||||
|
will_overwrite = Enum.filter(paths, &File.exists?/1)
|
||||||
|
proceed? = Enum.empty?(will_overwrite) or Keyword.get(options, :force, false)
|
||||||
|
|
||||||
|
if proceed? do
|
||||||
|
[domain, port | _] =
|
||||||
|
String.split(
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:domain,
|
||||||
|
"What domain will your instance use? (e.g pleroma.soykaf.com)"
|
||||||
|
),
|
||||||
|
":"
|
||||||
|
) ++ [443]
|
||||||
|
|
||||||
|
name =
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:instance_name,
|
||||||
|
"What is the name of your instance? (e.g. Pleroma/Soykaf)"
|
||||||
|
)
|
||||||
|
|
||||||
|
email = Common.get_option(options, :admin_email, "What is your admin email address?")
|
||||||
|
|
||||||
|
notify_email =
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:notify_email,
|
||||||
|
"What email address do you want to use for sending email notifications?",
|
||||||
|
email
|
||||||
|
)
|
||||||
|
|
||||||
|
indexable =
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:indexable,
|
||||||
|
"Do you want search engines to index your site? (y/n)",
|
||||||
|
"y"
|
||||||
|
) === "y"
|
||||||
|
|
||||||
|
dbhost =
|
||||||
|
Common.get_option(options, :dbhost, "What is the hostname of your database?", "localhost")
|
||||||
|
|
||||||
|
dbname =
|
||||||
|
Common.get_option(options, :dbname, "What is the name of your database?", "pleroma_dev")
|
||||||
|
|
||||||
|
dbuser =
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:dbuser,
|
||||||
|
"What is the user used to connect to your database?",
|
||||||
|
"pleroma"
|
||||||
|
)
|
||||||
|
|
||||||
|
dbpass =
|
||||||
|
Common.get_option(
|
||||||
|
options,
|
||||||
|
:dbpass,
|
||||||
|
"What is the password used to connect to your database?",
|
||||||
|
:crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64),
|
||||||
|
"autogenerated"
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
result_config =
|
||||||
|
EEx.eval_file(
|
||||||
|
"sample_config.eex" |> Path.expand(__DIR__),
|
||||||
|
domain: domain,
|
||||||
|
port: port,
|
||||||
|
email: email,
|
||||||
|
notify_email: notify_email,
|
||||||
|
name: name,
|
||||||
|
dbhost: dbhost,
|
||||||
|
dbname: dbname,
|
||||||
|
dbuser: dbuser,
|
||||||
|
dbpass: dbpass,
|
||||||
|
version: Pleroma.Mixfile.project() |> Keyword.get(:version),
|
||||||
|
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)
|
||||||
|
)
|
||||||
|
|
||||||
|
result_psql =
|
||||||
|
EEx.eval_file(
|
||||||
|
"sample_psql.eex" |> Path.expand(__DIR__),
|
||||||
|
dbname: dbname,
|
||||||
|
dbuser: dbuser,
|
||||||
|
dbpass: dbpass
|
||||||
|
)
|
||||||
|
|
||||||
|
Mix.shell().info(
|
||||||
|
"Writing config to #{config_path}. You should rename it to config/prod.secret.exs or config/dev.secret.exs."
|
||||||
|
)
|
||||||
|
|
||||||
|
File.write(config_path, result_config)
|
||||||
|
Mix.shell().info("Writing #{psql_path}.")
|
||||||
|
File.write(psql_path, result_psql)
|
||||||
|
|
||||||
|
write_robots_txt(indexable)
|
||||||
|
|
||||||
|
Mix.shell().info(
|
||||||
|
"\n" <>
|
||||||
|
"""
|
||||||
|
To get started:
|
||||||
|
1. Verify the contents of the generated files.
|
||||||
|
2. Run `sudo -u postgres psql -f #{Common.escape_sh_path(psql_path)}`.
|
||||||
|
""" <>
|
||||||
|
if config_path in ["config/dev.secret.exs", "config/prod.secret.exs"] do
|
||||||
|
""
|
||||||
|
else
|
||||||
|
"3. Run `mv #{Common.escape_sh_path(config_path)} 'config/prod.secret.exs'`."
|
||||||
|
end
|
||||||
|
)
|
||||||
|
else
|
||||||
|
Mix.shell().error(
|
||||||
|
"The task would have overwritten the following files:\n" <>
|
||||||
|
(Enum.map(paths, &"- #{&1}\n") |> Enum.join("")) <>
|
||||||
|
"Rerun with `--force` to overwrite them."
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp write_robots_txt(indexable) do
|
||||||
|
robots_txt =
|
||||||
|
EEx.eval_file(
|
||||||
|
Path.expand("robots_txt.eex", __DIR__),
|
||||||
|
indexable: indexable
|
||||||
|
)
|
||||||
|
|
||||||
|
static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
|
||||||
|
|
||||||
|
unless File.exists?(static_dir) do
|
||||||
|
File.mkdir_p!(static_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
robots_txt_path = Path.join(static_dir, "robots.txt")
|
||||||
|
|
||||||
|
if File.exists?(robots_txt_path) do
|
||||||
|
File.cp!(robots_txt_path, "#{robots_txt_path}.bak")
|
||||||
|
Mix.shell().info("Backing up existing robots.txt to #{robots_txt_path}.bak")
|
||||||
|
end
|
||||||
|
|
||||||
|
File.write(robots_txt_path, robots_txt)
|
||||||
|
Mix.shell().info("Writing #{robots_txt_path}.")
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,47 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.Relay do
|
||||||
|
use Mix.Task
|
||||||
|
alias Mix.Tasks.Pleroma.Common
|
||||||
|
alias Pleroma.Web.ActivityPub.Relay
|
||||||
|
|
||||||
|
@shortdoc "Manages remote relays"
|
||||||
|
@moduledoc """
|
||||||
|
Manages remote relays
|
||||||
|
|
||||||
|
## Follow a remote relay
|
||||||
|
|
||||||
|
``mix pleroma.relay follow <relay_url>``
|
||||||
|
|
||||||
|
Example: ``mix pleroma.relay follow https://example.org/relay``
|
||||||
|
|
||||||
|
## Unfollow a remote relay
|
||||||
|
|
||||||
|
``mix pleroma.relay unfollow <relay_url>``
|
||||||
|
|
||||||
|
Example: ``mix pleroma.relay unfollow https://example.org/relay``
|
||||||
|
"""
|
||||||
|
def run(["follow", target]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with {:ok, _activity} <- Relay.follow(target) do
|
||||||
|
# put this task to sleep to allow the genserver to push out the messages
|
||||||
|
:timer.sleep(500)
|
||||||
|
else
|
||||||
|
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["unfollow", target]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with {:ok, _activity} <- Relay.unfollow(target) do
|
||||||
|
# put this task to sleep to allow the genserver to push out the messages
|
||||||
|
:timer.sleep(500)
|
||||||
|
else
|
||||||
|
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,2 @@
|
||||||
|
User-Agent: *
|
||||||
|
Disallow: <%= if indexable, do: "", else: "/" %>
|
|
@ -0,0 +1,32 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.RobotsTxt do
|
||||||
|
use Mix.Task
|
||||||
|
|
||||||
|
@shortdoc "Generate robots.txt"
|
||||||
|
@moduledoc """
|
||||||
|
Generates robots.txt
|
||||||
|
|
||||||
|
## Overwrite robots.txt to disallow all
|
||||||
|
|
||||||
|
mix pleroma.robots_txt disallow_all
|
||||||
|
|
||||||
|
This will write a robots.txt that will hide all paths on your instance
|
||||||
|
from search engines and other robots that obey robots.txt
|
||||||
|
|
||||||
|
"""
|
||||||
|
def run(["disallow_all"]) do
|
||||||
|
static_dir = Pleroma.Config.get([:instance, :static_dir], "instance/static/")
|
||||||
|
|
||||||
|
if !File.exists?(static_dir) do
|
||||||
|
File.mkdir_p!(static_dir)
|
||||||
|
end
|
||||||
|
|
||||||
|
robots_txt_path = Path.join(static_dir, "robots.txt")
|
||||||
|
robots_txt_content = "User-Agent: *\nDisallow: /\n"
|
||||||
|
|
||||||
|
File.write!(robots_txt_path, robots_txt_content, [:write])
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,12 +1,19 @@
|
||||||
|
# Pleroma instance configuration
|
||||||
|
|
||||||
|
# NOTE: This file should not be committed to a repo or otherwise made public
|
||||||
|
# without removing sensitive information.
|
||||||
|
|
||||||
use Mix.Config
|
use Mix.Config
|
||||||
|
|
||||||
config :pleroma, Pleroma.Web.Endpoint,
|
config :pleroma, Pleroma.Web.Endpoint,
|
||||||
url: [host: "<%= domain %>", scheme: "https", port: 443],
|
url: [host: "<%= domain %>", scheme: "https", port: <%= port %>],
|
||||||
secret_key_base: "<%= secret %>"
|
secret_key_base: "<%= secret %>",
|
||||||
|
signing_salt: "<%= signing_salt %>"
|
||||||
|
|
||||||
config :pleroma, :instance,
|
config :pleroma, :instance,
|
||||||
name: "<%= name %>",
|
name: "<%= name %>",
|
||||||
email: "<%= email %>",
|
email: "<%= email %>",
|
||||||
|
notify_email: "<%= notify_email %>",
|
||||||
limit: 5000,
|
limit: 5000,
|
||||||
registrations_open: true,
|
registrations_open: true,
|
||||||
dedupe_media: false
|
dedupe_media: false
|
||||||
|
@ -16,15 +23,20 @@ config :pleroma, :media_proxy,
|
||||||
redirect_on_failure: true
|
redirect_on_failure: true
|
||||||
#base_url: "https://cache.pleroma.social"
|
#base_url: "https://cache.pleroma.social"
|
||||||
|
|
||||||
# Configure your database
|
|
||||||
config :pleroma, Pleroma.Repo,
|
config :pleroma, Pleroma.Repo,
|
||||||
adapter: Ecto.Adapters.Postgres,
|
adapter: Ecto.Adapters.Postgres,
|
||||||
username: "pleroma",
|
username: "<%= dbuser %>",
|
||||||
password: "<%= dbpass %>",
|
password: "<%= dbpass %>",
|
||||||
database: "pleroma_dev",
|
database: "<%= dbname %>",
|
||||||
hostname: "localhost",
|
hostname: "<%= dbhost %>",
|
||||||
pool_size: 10
|
pool_size: 10
|
||||||
|
|
||||||
|
# Configure web push notifications
|
||||||
|
config :web_push_encryption, :vapid_details,
|
||||||
|
subject: "mailto:<%= email %>",
|
||||||
|
public_key: "<%= web_push_public_key %>",
|
||||||
|
private_key: "<%= web_push_private_key %>"
|
||||||
|
|
||||||
# Enable Strict-Transport-Security once SSL is working:
|
# Enable Strict-Transport-Security once SSL is working:
|
||||||
# config :pleroma, :http_security,
|
# config :pleroma, :http_security,
|
||||||
# sts: true
|
# sts: true
|
||||||
|
@ -64,4 +76,3 @@ config :pleroma, Pleroma.Repo,
|
||||||
# storage_url: "https://swift-endpoint.prodider.com/v1/AUTH_<tenant>/<container>",
|
# storage_url: "https://swift-endpoint.prodider.com/v1/AUTH_<tenant>/<container>",
|
||||||
# object_url: "https://cdn-endpoint.provider.com/<container>"
|
# object_url: "https://cdn-endpoint.provider.com/<container>"
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
CREATE USER pleroma WITH ENCRYPTED PASSWORD '<%= dbpass %>';
|
CREATE USER <%= dbuser %> WITH ENCRYPTED PASSWORD '<%= dbpass %>';
|
||||||
CREATE DATABASE pleroma_dev OWNER pleroma;
|
CREATE DATABASE <%= dbname %> OWNER <%= dbuser %>;
|
||||||
\c pleroma_dev;
|
\c <%= dbname %>;
|
||||||
--Extensions made by ecto.migrate that need superuser access
|
--Extensions made by ecto.migrate that need superuser access
|
||||||
CREATE EXTENSION IF NOT EXISTS citext;
|
CREATE EXTENSION IF NOT EXISTS citext;
|
||||||
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
CREATE EXTENSION IF NOT EXISTS pg_trgm;
|
|
@ -1,16 +1,30 @@
|
||||||
defmodule Mix.Tasks.MigrateLocalUploads do
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.Uploads do
|
||||||
use Mix.Task
|
use Mix.Task
|
||||||
import Mix.Ecto
|
alias Mix.Tasks.Pleroma.Common
|
||||||
alias Pleroma.{Upload, Uploaders.Local, Uploaders.S3}
|
alias Pleroma.Upload
|
||||||
|
alias Pleroma.Uploaders.Local
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
@log_every 50
|
@log_every 50
|
||||||
@shortdoc "Migrate uploads from local to remote storage"
|
|
||||||
|
|
||||||
def run([target_uploader | args]) do
|
@shortdoc "Migrates uploads from local to remote storage"
|
||||||
|
@moduledoc """
|
||||||
|
Manages uploads
|
||||||
|
|
||||||
|
## Migrate uploads from local to remote storage
|
||||||
|
mix pleroma.uploads migrate_local TARGET_UPLOADER [OPTIONS...]
|
||||||
|
Options:
|
||||||
|
- `--delete` - delete local uploads after migrating them to the target uploader
|
||||||
|
|
||||||
|
A list of available uploaders can be seen in config.exs
|
||||||
|
"""
|
||||||
|
def run(["migrate_local", target_uploader | args]) do
|
||||||
delete? = Enum.member?(args, "--delete")
|
delete? = Enum.member?(args, "--delete")
|
||||||
Application.ensure_all_started(:pleroma)
|
Common.start_pleroma()
|
||||||
|
|
||||||
local_path = Pleroma.Config.get!([Local, :uploads])
|
local_path = Pleroma.Config.get!([Local, :uploads])
|
||||||
uploader = Module.concat(Pleroma.Uploaders, target_uploader)
|
uploader = Module.concat(Pleroma.Uploaders, target_uploader)
|
||||||
|
|
||||||
|
@ -24,10 +38,10 @@ def run([target_uploader | args]) do
|
||||||
Pleroma.Config.put([Upload, :uploader], uploader)
|
Pleroma.Config.put([Upload, :uploader], uploader)
|
||||||
end
|
end
|
||||||
|
|
||||||
Logger.info("Migrating files from local #{local_path} to #{to_string(uploader)}")
|
Mix.shell().info("Migrating files from local #{local_path} to #{to_string(uploader)}")
|
||||||
|
|
||||||
if delete? do
|
if delete? do
|
||||||
Logger.warn(
|
Mix.shell().info(
|
||||||
"Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)"
|
"Attention: uploaded files will be deleted, hope you have backups! (--delete ; cancel with ^C)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -54,7 +68,7 @@ def run([target_uploader | args]) do
|
||||||
|
|
||||||
File.exists?(root_path) ->
|
File.exists?(root_path) ->
|
||||||
file = Path.basename(id)
|
file = Path.basename(id)
|
||||||
[hash, ext] = String.split(id, ".")
|
hash = Path.rootname(id)
|
||||||
{%Pleroma.Upload{id: hash, name: file, path: file, tempfile: root_path}, root_path}
|
{%Pleroma.Upload{id: hash, name: file, path: file, tempfile: root_path}, root_path}
|
||||||
|
|
||||||
true ->
|
true ->
|
||||||
|
@ -64,7 +78,7 @@ def run([target_uploader | args]) do
|
||||||
|> Enum.filter(& &1)
|
|> Enum.filter(& &1)
|
||||||
|
|
||||||
total_count = length(uploads)
|
total_count = length(uploads)
|
||||||
Logger.info("Found #{total_count} uploads")
|
Mix.shell().info("Found #{total_count} uploads")
|
||||||
|
|
||||||
uploads
|
uploads
|
||||||
|> Task.async_stream(
|
|> Task.async_stream(
|
||||||
|
@ -76,22 +90,19 @@ def run([target_uploader | args]) do
|
||||||
:ok
|
:ok
|
||||||
|
|
||||||
error ->
|
error ->
|
||||||
Logger.error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")
|
Mix.shell().error("failed to upload #{inspect(upload.path)}: #{inspect(error)}")
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
timeout: 150_000
|
timeout: 150_000
|
||||||
)
|
)
|
||||||
|> Stream.chunk_every(@log_every)
|
|> Stream.chunk_every(@log_every)
|
||||||
|
# credo:disable-for-next-line Credo.Check.Warning.UnusedEnumOperation
|
||||||
|> Enum.reduce(0, fn done, count ->
|
|> Enum.reduce(0, fn done, count ->
|
||||||
count = count + length(done)
|
count = count + length(done)
|
||||||
Logger.info("Uploaded #{count}/#{total_count} files")
|
Mix.shell().info("Uploaded #{count}/#{total_count} files")
|
||||||
count
|
count
|
||||||
end)
|
end)
|
||||||
|
|
||||||
Logger.info("Done!")
|
Mix.shell().info("Done!")
|
||||||
end
|
|
||||||
|
|
||||||
def run(_) do
|
|
||||||
Logger.error("Usage: migrate_local_uploads S3|Swift [--delete]")
|
|
||||||
end
|
end
|
||||||
end
|
end
|
|
@ -0,0 +1,429 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2018 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Mix.Tasks.Pleroma.User do
|
||||||
|
use Mix.Task
|
||||||
|
import Ecto.Changeset
|
||||||
|
alias Mix.Tasks.Pleroma.Common
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.UserInviteToken
|
||||||
|
|
||||||
|
@shortdoc "Manages Pleroma users"
|
||||||
|
@moduledoc """
|
||||||
|
Manages Pleroma users.
|
||||||
|
|
||||||
|
## Create a new user.
|
||||||
|
|
||||||
|
mix pleroma.user new NICKNAME EMAIL [OPTION...]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `--name NAME` - the user's name (i.e., "Lain Iwakura")
|
||||||
|
- `--bio BIO` - the user's bio
|
||||||
|
- `--password PASSWORD` - the user's password
|
||||||
|
- `--moderator`/`--no-moderator` - whether the user is a moderator
|
||||||
|
- `--admin`/`--no-admin` - whether the user is an admin
|
||||||
|
- `-y`, `--assume-yes`/`--no-assume-yes` - whether to assume yes to all questions
|
||||||
|
|
||||||
|
## Generate an invite link.
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
mix pleroma.user invites
|
||||||
|
|
||||||
|
## Revoke invite
|
||||||
|
|
||||||
|
mix pleroma.user revoke_invite TOKEN OR TOKEN_ID
|
||||||
|
|
||||||
|
## Delete the user's account.
|
||||||
|
|
||||||
|
mix pleroma.user rm NICKNAME
|
||||||
|
|
||||||
|
## Delete the user's activities.
|
||||||
|
|
||||||
|
mix pleroma.user delete_activities NICKNAME
|
||||||
|
|
||||||
|
## Deactivate or activate the user's account.
|
||||||
|
|
||||||
|
mix pleroma.user toggle_activated NICKNAME
|
||||||
|
|
||||||
|
## Unsubscribe local users from user's account and deactivate it
|
||||||
|
|
||||||
|
mix pleroma.user unsubscribe NICKNAME
|
||||||
|
|
||||||
|
## Create a password reset link.
|
||||||
|
|
||||||
|
mix pleroma.user reset_password NICKNAME
|
||||||
|
|
||||||
|
## Set the value of the given user's settings.
|
||||||
|
|
||||||
|
mix pleroma.user set NICKNAME [OPTION...]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
- `--locked`/`--no-locked` - whether the user's account is locked
|
||||||
|
- `--moderator`/`--no-moderator` - whether the user is a moderator
|
||||||
|
- `--admin`/`--no-admin` - whether the user is an admin
|
||||||
|
|
||||||
|
## Add tags to a user.
|
||||||
|
|
||||||
|
mix pleroma.user tag NICKNAME TAGS
|
||||||
|
|
||||||
|
## Delete tags from a user.
|
||||||
|
|
||||||
|
mix pleroma.user untag NICKNAME TAGS
|
||||||
|
"""
|
||||||
|
def run(["new", nickname, email | rest]) do
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(
|
||||||
|
rest,
|
||||||
|
strict: [
|
||||||
|
name: :string,
|
||||||
|
bio: :string,
|
||||||
|
password: :string,
|
||||||
|
moderator: :boolean,
|
||||||
|
admin: :boolean,
|
||||||
|
assume_yes: :boolean
|
||||||
|
],
|
||||||
|
aliases: [
|
||||||
|
y: :assume_yes
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
name = Keyword.get(options, :name, nickname)
|
||||||
|
bio = Keyword.get(options, :bio, "")
|
||||||
|
|
||||||
|
{password, generated_password?} =
|
||||||
|
case Keyword.get(options, :password) do
|
||||||
|
nil ->
|
||||||
|
{:crypto.strong_rand_bytes(16) |> Base.encode64(), true}
|
||||||
|
|
||||||
|
password ->
|
||||||
|
{password, false}
|
||||||
|
end
|
||||||
|
|
||||||
|
moderator? = Keyword.get(options, :moderator, false)
|
||||||
|
admin? = Keyword.get(options, :admin, false)
|
||||||
|
assume_yes? = Keyword.get(options, :assume_yes, false)
|
||||||
|
|
||||||
|
Mix.shell().info("""
|
||||||
|
A user will be created with the following information:
|
||||||
|
- nickname: #{nickname}
|
||||||
|
- email: #{email}
|
||||||
|
- password: #{
|
||||||
|
if(generated_password?, do: "[generated; a reset link will be created]", else: password)
|
||||||
|
}
|
||||||
|
- name: #{name}
|
||||||
|
- bio: #{bio}
|
||||||
|
- moderator: #{if(moderator?, do: "true", else: "false")}
|
||||||
|
- admin: #{if(admin?, do: "true", else: "false")}
|
||||||
|
""")
|
||||||
|
|
||||||
|
proceed? = assume_yes? or Mix.shell().yes?("Continue?")
|
||||||
|
|
||||||
|
unless not proceed? do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
params = %{
|
||||||
|
nickname: nickname,
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
password_confirmation: password,
|
||||||
|
name: name,
|
||||||
|
bio: bio
|
||||||
|
}
|
||||||
|
|
||||||
|
changeset = User.register_changeset(%User{}, params, confirmed: true)
|
||||||
|
{:ok, _user} = User.register(changeset)
|
||||||
|
|
||||||
|
Mix.shell().info("User #{nickname} created")
|
||||||
|
|
||||||
|
if moderator? do
|
||||||
|
run(["set", nickname, "--moderator"])
|
||||||
|
end
|
||||||
|
|
||||||
|
if admin? do
|
||||||
|
run(["set", nickname, "--admin"])
|
||||||
|
end
|
||||||
|
|
||||||
|
if generated_password? do
|
||||||
|
run(["reset_password", nickname])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
Mix.shell().info("User will not be created.")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["rm", nickname]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||||
|
User.delete(user)
|
||||||
|
Mix.shell().info("User #{nickname} deleted.")
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("No local user #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["toggle_activated", nickname]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with %User{} = user <- User.get_by_nickname(nickname) do
|
||||||
|
{:ok, user} = User.deactivate(user, !user.info.deactivated)
|
||||||
|
|
||||||
|
Mix.shell().info(
|
||||||
|
"Activation status of #{nickname}: #{if(user.info.deactivated, do: "de", else: "")}activated"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("No user #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["reset_password", nickname]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with %User{local: true} = user <- User.get_by_nickname(nickname),
|
||||||
|
{:ok, token} <- Pleroma.PasswordResetToken.create_token(user) do
|
||||||
|
Mix.shell().info("Generated password reset token for #{user.nickname}")
|
||||||
|
|
||||||
|
IO.puts(
|
||||||
|
"URL: #{
|
||||||
|
Pleroma.Web.Router.Helpers.util_url(
|
||||||
|
Pleroma.Web.Endpoint,
|
||||||
|
:show_password_reset,
|
||||||
|
token.token
|
||||||
|
)
|
||||||
|
}"
|
||||||
|
)
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("No local user #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["unsubscribe", nickname]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with %User{} = user <- User.get_by_nickname(nickname) do
|
||||||
|
Mix.shell().info("Deactivating #{user.nickname}")
|
||||||
|
User.deactivate(user)
|
||||||
|
|
||||||
|
{:ok, friends} = User.get_friends(user)
|
||||||
|
|
||||||
|
Enum.each(friends, fn friend ->
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
|
||||||
|
Mix.shell().info("Unsubscribing #{friend.nickname} from #{user.nickname}")
|
||||||
|
User.unfollow(user, friend)
|
||||||
|
end)
|
||||||
|
|
||||||
|
:timer.sleep(500)
|
||||||
|
|
||||||
|
user = User.get_by_id(user.id)
|
||||||
|
|
||||||
|
if Enum.empty?(user.following) do
|
||||||
|
Mix.shell().info("Successfully unsubscribed all followers from #{user.nickname}")
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("No user #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["set", nickname | rest]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(
|
||||||
|
rest,
|
||||||
|
strict: [
|
||||||
|
moderator: :boolean,
|
||||||
|
admin: :boolean,
|
||||||
|
locked: :boolean
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||||
|
user =
|
||||||
|
case Keyword.get(options, :moderator) do
|
||||||
|
nil -> user
|
||||||
|
value -> set_moderator(user, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
user =
|
||||||
|
case Keyword.get(options, :locked) do
|
||||||
|
nil -> user
|
||||||
|
value -> set_locked(user, value)
|
||||||
|
end
|
||||||
|
|
||||||
|
_user =
|
||||||
|
case Keyword.get(options, :admin) do
|
||||||
|
nil -> user
|
||||||
|
value -> set_admin(user, value)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("No local user #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["tag", nickname | tags]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with %User{} = user <- User.get_by_nickname(nickname) do
|
||||||
|
user = user |> User.tag(tags)
|
||||||
|
|
||||||
|
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("Could not change user tags for #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["untag", nickname | tags]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with %User{} = user <- User.get_by_nickname(nickname) do
|
||||||
|
user = user |> User.untag(tags)
|
||||||
|
|
||||||
|
Mix.shell().info("Tags of #{user.nickname}: #{inspect(tags)}")
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("Could not change user tags for #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["invite" | rest]) do
|
||||||
|
{options, [], []} =
|
||||||
|
OptionParser.parse(rest,
|
||||||
|
strict: [
|
||||||
|
expires_at: :string,
|
||||||
|
max_use: :integer
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
options =
|
||||||
|
options
|
||||||
|
|> Keyword.update(:expires_at, {:ok, nil}, fn
|
||||||
|
nil -> {:ok, nil}
|
||||||
|
val -> Date.from_iso8601(val)
|
||||||
|
end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with {:ok, val} <- options[:expires_at],
|
||||||
|
options = Map.put(options, :expires_at, val),
|
||||||
|
{:ok, invite} <- UserInviteToken.create_invite(options) do
|
||||||
|
Mix.shell().info(
|
||||||
|
"Generated user invite token " <> String.replace(invite.invite_type, "_", " ")
|
||||||
|
)
|
||||||
|
|
||||||
|
url =
|
||||||
|
Pleroma.Web.Router.Helpers.redirect_url(
|
||||||
|
Pleroma.Web.Endpoint,
|
||||||
|
:registration_page,
|
||||||
|
invite.token
|
||||||
|
)
|
||||||
|
|
||||||
|
IO.puts(url)
|
||||||
|
else
|
||||||
|
error ->
|
||||||
|
Mix.shell().error("Could not create invite token: #{inspect(error)}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["invites"]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
Mix.shell().info("Invites list:")
|
||||||
|
|
||||||
|
UserInviteToken.list_invites()
|
||||||
|
|> Enum.each(fn invite ->
|
||||||
|
expire_info =
|
||||||
|
with expires_at when not is_nil(expires_at) <- invite.expires_at do
|
||||||
|
" | Expires at: #{Date.to_string(expires_at)}"
|
||||||
|
end
|
||||||
|
|
||||||
|
using_info =
|
||||||
|
with max_use when not is_nil(max_use) <- invite.max_use do
|
||||||
|
" | Max use: #{max_use} Left use: #{max_use - invite.uses}"
|
||||||
|
end
|
||||||
|
|
||||||
|
Mix.shell().info(
|
||||||
|
"ID: #{invite.id} | Token: #{invite.token} | Token type: #{invite.invite_type} | Used: #{
|
||||||
|
invite.used
|
||||||
|
}#{expire_info}#{using_info}"
|
||||||
|
)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["revoke_invite", token]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with {:ok, invite} <- UserInviteToken.find_by_token(token),
|
||||||
|
{:ok, _} <- UserInviteToken.update_invite(invite, %{used: true}) do
|
||||||
|
Mix.shell().info("Invite for token #{token} was revoked.")
|
||||||
|
else
|
||||||
|
_ -> Mix.shell().error("No invite found with token #{token}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def run(["delete_activities", nickname]) do
|
||||||
|
Common.start_pleroma()
|
||||||
|
|
||||||
|
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
||||||
|
User.delete_user_activities(user)
|
||||||
|
Mix.shell().info("User #{nickname} statuses deleted.")
|
||||||
|
else
|
||||||
|
_ ->
|
||||||
|
Mix.shell().error("No local user #{nickname}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_moderator(user, value) do
|
||||||
|
info_cng = User.Info.admin_api_update(user.info, %{is_moderator: value})
|
||||||
|
|
||||||
|
user_cng =
|
||||||
|
Ecto.Changeset.change(user)
|
||||||
|
|> put_embed(:info, info_cng)
|
||||||
|
|
||||||
|
{:ok, user} = User.update_and_set_cache(user_cng)
|
||||||
|
|
||||||
|
Mix.shell().info("Moderator status of #{user.nickname}: #{user.info.is_moderator}")
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_admin(user, value) do
|
||||||
|
info_cng = User.Info.admin_api_update(user.info, %{is_admin: value})
|
||||||
|
|
||||||
|
user_cng =
|
||||||
|
Ecto.Changeset.change(user)
|
||||||
|
|> put_embed(:info, info_cng)
|
||||||
|
|
||||||
|
{:ok, user} = User.update_and_set_cache(user_cng)
|
||||||
|
|
||||||
|
Mix.shell().info("Admin status of #{user.nickname}: #{user.info.is_admin}")
|
||||||
|
user
|
||||||
|
end
|
||||||
|
|
||||||
|
defp set_locked(user, value) do
|
||||||
|
info_cng = User.Info.user_upgrade(user.info, %{locked: value})
|
||||||
|
|
||||||
|
user_cng =
|
||||||
|
Ecto.Changeset.change(user)
|
||||||
|
|> put_embed(:info, info_cng)
|
||||||
|
|
||||||
|
{:ok, user} = User.update_and_set_cache(user_cng)
|
||||||
|
|
||||||
|
Mix.shell().info("Locked status of #{user.nickname}: #{user.info.locked}")
|
||||||
|
user
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,19 +0,0 @@
|
||||||
defmodule Mix.Tasks.ReactivateUser do
|
|
||||||
use Mix.Task
|
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Reactivate a user
|
|
||||||
|
|
||||||
Usage: ``mix reactivate_user <nickname>``
|
|
||||||
|
|
||||||
Example: ``mix reactivate_user lain``
|
|
||||||
"""
|
|
||||||
def run([nickname]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with user <- User.get_by_nickname(nickname) do
|
|
||||||
User.deactivate(user, false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,30 +0,0 @@
|
||||||
defmodule Mix.Tasks.RegisterUser do
|
|
||||||
@moduledoc """
|
|
||||||
Manually register a local user
|
|
||||||
|
|
||||||
Usage: ``mix register_user <name> <nickname> <email> <bio> <password>``
|
|
||||||
|
|
||||||
Example: ``mix register_user 仮面の告白 lain lain@example.org "blushy-crushy fediverse idol + pleroma dev" pleaseDontHeckLain``
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Mix.Task
|
|
||||||
alias Pleroma.{Repo, User}
|
|
||||||
|
|
||||||
@shortdoc "Register user"
|
|
||||||
def run([name, nickname, email, bio, password]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
params = %{
|
|
||||||
name: name,
|
|
||||||
nickname: nickname,
|
|
||||||
email: email,
|
|
||||||
password: password,
|
|
||||||
password_confirmation: password,
|
|
||||||
bio: bio
|
|
||||||
}
|
|
||||||
|
|
||||||
user = User.register_changeset(%User{}, params)
|
|
||||||
|
|
||||||
Repo.insert!(user)
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,24 +0,0 @@
|
||||||
defmodule Mix.Tasks.RelayFollow do
|
|
||||||
use Mix.Task
|
|
||||||
require Logger
|
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
|
||||||
|
|
||||||
@shortdoc "Follows a remote relay"
|
|
||||||
@moduledoc """
|
|
||||||
Follows a remote relay
|
|
||||||
|
|
||||||
Usage: ``mix relay_follow <relay_url>``
|
|
||||||
|
|
||||||
Example: ``mix relay_follow https://example.org/relay``
|
|
||||||
"""
|
|
||||||
def run([target]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with {:ok, activity} <- Relay.follow(target) do
|
|
||||||
# put this task to sleep to allow the genserver to push out the messages
|
|
||||||
:timer.sleep(500)
|
|
||||||
else
|
|
||||||
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,23 +0,0 @@
|
||||||
defmodule Mix.Tasks.RelayUnfollow do
|
|
||||||
use Mix.Task
|
|
||||||
require Logger
|
|
||||||
alias Pleroma.Web.ActivityPub.Relay
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Unfollows a remote relay
|
|
||||||
|
|
||||||
Usage: ``mix relay_follow <relay_url>``
|
|
||||||
|
|
||||||
Example: ``mix relay_follow https://example.org/relay``
|
|
||||||
"""
|
|
||||||
def run([target]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with {:ok, activity} <- Relay.follow(target) do
|
|
||||||
# put this task to sleep to allow the genserver to push out the messages
|
|
||||||
:timer.sleep(500)
|
|
||||||
else
|
|
||||||
{:error, e} -> Mix.shell().error("Error while following #{target}: #{inspect(e)}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,19 +0,0 @@
|
||||||
defmodule Mix.Tasks.RmUser do
|
|
||||||
use Mix.Task
|
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Permanently deletes a user
|
|
||||||
|
|
||||||
Usage: ``mix rm_user [nickname]``
|
|
||||||
|
|
||||||
Example: ``mix rm_user lain``
|
|
||||||
"""
|
|
||||||
def run([nickname]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
|
||||||
{:ok, _} = User.delete(user)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,32 +0,0 @@
|
||||||
defmodule Mix.Tasks.SetAdmin do
|
|
||||||
use Mix.Task
|
|
||||||
alias Pleroma.User
|
|
||||||
|
|
||||||
@doc """
|
|
||||||
Sets admin status
|
|
||||||
Usage: set_admin nickname [true|false]
|
|
||||||
"""
|
|
||||||
def run([nickname | rest]) do
|
|
||||||
Application.ensure_all_started(:pleroma)
|
|
||||||
|
|
||||||
status =
|
|
||||||
case rest do
|
|
||||||
[status] -> status == "true"
|
|
||||||
_ -> true
|
|
||||||
end
|
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
|
||||||
info =
|
|
||||||
user.info
|
|
||||||
|> Map.put("is_admin", !!status)
|
|
||||||
|
|
||||||
cng = User.info_changeset(user, %{info: info})
|
|
||||||
{:ok, user} = User.update_and_set_cache(cng)
|
|
||||||
|
|
||||||
IO.puts("Admin status of #{nickname}: #{user.info["is_admin"]}")
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
IO.puts("No local user #{nickname}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,39 +0,0 @@
|
||||||
defmodule Mix.Tasks.SetLocked do
|
|
||||||
@moduledoc """
|
|
||||||
Lock a local user
|
|
||||||
|
|
||||||
The local user will then have to manually accept/reject followers. This can also be done by the user into their settings.
|
|
||||||
|
|
||||||
Usage: ``mix set_locked <username>``
|
|
||||||
|
|
||||||
Example: ``mix set_locked lain``
|
|
||||||
"""
|
|
||||||
|
|
||||||
use Mix.Task
|
|
||||||
import Mix.Ecto
|
|
||||||
alias Pleroma.{Repo, User}
|
|
||||||
|
|
||||||
def run([nickname | rest]) do
|
|
||||||
ensure_started(Repo, [])
|
|
||||||
|
|
||||||
locked =
|
|
||||||
case rest do
|
|
||||||
[locked] -> locked == "true"
|
|
||||||
_ -> true
|
|
||||||
end
|
|
||||||
|
|
||||||
with %User{local: true} = user <- User.get_by_nickname(nickname) do
|
|
||||||
info =
|
|
||||||
user.info
|
|
||||||
|> Map.put("locked", !!locked)
|
|
||||||
|
|
||||||
cng = User.info_changeset(user, %{info: info})
|
|
||||||
user = Repo.update!(cng)
|
|
||||||
|
|
||||||
IO.puts("locked status of #{nickname}: #{user.info["locked"]}")
|
|
||||||
else
|
|
||||||
_ ->
|
|
||||||
IO.puts("No local user #{nickname}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,38 +0,0 @@
|
||||||
defmodule Mix.Tasks.UnsubscribeUser do
|
|
||||||
use Mix.Task
|
|
||||||
alias Pleroma.{User, Repo}
|
|
||||||
require Logger
|
|
||||||
|
|
||||||
@moduledoc """
|
|
||||||
Deactivate and Unsubscribe local users from a user
|
|
||||||
|
|
||||||
Usage: ``mix unsubscribe_user <nickname>``
|
|
||||||
|
|
||||||
Example: ``mix unsubscribe_user lain``
|
|
||||||
"""
|
|
||||||
def run([nickname]) do
|
|
||||||
Mix.Task.run("app.start")
|
|
||||||
|
|
||||||
with %User{} = user <- User.get_by_nickname(nickname) do
|
|
||||||
Logger.info("Deactivating #{user.nickname}")
|
|
||||||
User.deactivate(user)
|
|
||||||
|
|
||||||
{:ok, friends} = User.get_friends(user)
|
|
||||||
|
|
||||||
Enum.each(friends, fn friend ->
|
|
||||||
user = Repo.get(User, user.id)
|
|
||||||
|
|
||||||
Logger.info("Unsubscribing #{friend.nickname} from #{user.nickname}")
|
|
||||||
User.unfollow(user, friend)
|
|
||||||
end)
|
|
||||||
|
|
||||||
:timer.sleep(500)
|
|
||||||
|
|
||||||
user = Repo.get(User, user.id)
|
|
||||||
|
|
||||||
if length(user.following) == 0 do
|
|
||||||
Logger.info("Successfully unsubscribed all followers from #{user.nickname}")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -1,12 +1,18 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.PasswordResetToken do
|
defmodule Pleroma.PasswordResetToken do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|
||||||
import Ecto.Changeset
|
import Ecto.Changeset
|
||||||
|
|
||||||
alias Pleroma.{User, PasswordResetToken, Repo}
|
alias Pleroma.PasswordResetToken
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
schema "password_reset_tokens" do
|
schema "password_reset_tokens" do
|
||||||
belongs_to(:user, User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
field(:token, :string)
|
field(:token, :string)
|
||||||
field(:used, :boolean, default: false)
|
field(:used, :boolean, default: false)
|
||||||
|
|
||||||
|
@ -33,7 +39,7 @@ def used_changeset(struct) do
|
||||||
|
|
||||||
def reset_password(token, data) do
|
def reset_password(token, data) do
|
||||||
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
with %{used: false} = token <- Repo.get_by(PasswordResetToken, %{token: token}),
|
||||||
%User{} = user <- Repo.get(User, token.user_id),
|
%User{} = user <- User.get_by_id(token.user_id),
|
||||||
{:ok, _user} <- User.reset_password(user, data),
|
{:ok, _user} <- User.reset_password(user, data),
|
||||||
{:ok, token} <- Repo.update(used_changeset(token)) do
|
{:ok, token} <- Repo.update(used_changeset(token)) do
|
||||||
{:ok, token}
|
{:ok, token}
|
||||||
|
|
|
@ -1,18 +1,76 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Activity do
|
defmodule Pleroma.Activity do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
alias Pleroma.{Repo, Activity, Notification, Object}
|
|
||||||
import Ecto.{Query, Changeset}
|
alias Pleroma.Activity
|
||||||
|
alias Pleroma.Notification
|
||||||
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
@type t :: %__MODULE__{}
|
||||||
|
@primary_key {:id, Pleroma.FlakeId, autogenerate: true}
|
||||||
|
|
||||||
|
# https://github.com/tootsuite/mastodon/blob/master/app/models/notification.rb#L19
|
||||||
|
@mastodon_notification_types %{
|
||||||
|
"Create" => "mention",
|
||||||
|
"Follow" => "follow",
|
||||||
|
"Announce" => "reblog",
|
||||||
|
"Like" => "favourite"
|
||||||
|
}
|
||||||
|
|
||||||
|
@mastodon_to_ap_notification_types for {k, v} <- @mastodon_notification_types,
|
||||||
|
into: %{},
|
||||||
|
do: {v, k}
|
||||||
|
|
||||||
schema "activities" do
|
schema "activities" do
|
||||||
field(:data, :map)
|
field(:data, :map)
|
||||||
field(:local, :boolean, default: true)
|
field(:local, :boolean, default: true)
|
||||||
field(:actor, :string)
|
field(:actor, :string)
|
||||||
field(:recipients, {:array, :string})
|
field(:recipients, {:array, :string}, default: [])
|
||||||
has_many(:notifications, Notification, on_delete: :delete_all)
|
has_many(:notifications, Notification, on_delete: :delete_all)
|
||||||
|
|
||||||
|
# Attention: this is a fake relation, don't try to preload it blindly and expect it to work!
|
||||||
|
# The foreign key is embedded in a jsonb field.
|
||||||
|
#
|
||||||
|
# To use it, you probably want to do an inner join and a preload:
|
||||||
|
#
|
||||||
|
# ```
|
||||||
|
# |> join(:inner, [activity], o in Object,
|
||||||
|
# on: fragment("(?->>'id') = COALESCE((?)->'object'->> 'id', (?)->>'object')",
|
||||||
|
# o.data, activity.data, activity.data))
|
||||||
|
# |> preload([activity, object], [object: object])
|
||||||
|
# ```
|
||||||
|
#
|
||||||
|
# As a convenience, Activity.with_preloaded_object() sets up an inner join and preload for the
|
||||||
|
# typical case.
|
||||||
|
has_one(:object, Object, on_delete: :nothing, foreign_key: :id)
|
||||||
|
|
||||||
timestamps()
|
timestamps()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def with_preloaded_object(query) do
|
||||||
|
query
|
||||||
|
|> join(
|
||||||
|
:inner,
|
||||||
|
[activity],
|
||||||
|
o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
|
||||||
|
o.data,
|
||||||
|
activity.data,
|
||||||
|
activity.data
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> preload([activity, object], object: object)
|
||||||
|
end
|
||||||
|
|
||||||
def get_by_ap_id(ap_id) do
|
def get_by_ap_id(ap_id) do
|
||||||
Repo.one(
|
Repo.one(
|
||||||
from(
|
from(
|
||||||
|
@ -29,25 +87,45 @@ def change(struct, params \\ %{}) do
|
||||||
|> unique_constraint(:ap_id, name: :activities_unique_apid_index)
|
|> unique_constraint(:ap_id, name: :activities_unique_apid_index)
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO:
|
def get_by_ap_id_with_object(ap_id) do
|
||||||
# Go through these and fix them everywhere.
|
Repo.one(
|
||||||
# Wrong name, only returns create activities
|
|
||||||
def all_by_object_ap_id_q(ap_id) do
|
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
where:
|
where: fragment("(?)->>'id' = ?", activity.data, ^to_string(ap_id)),
|
||||||
|
left_join: o in Object,
|
||||||
|
on:
|
||||||
fragment(
|
fragment(
|
||||||
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
|
||||||
|
o.data,
|
||||||
activity.data,
|
activity.data,
|
||||||
activity.data,
|
activity.data
|
||||||
^to_string(ap_id)
|
|
||||||
),
|
),
|
||||||
where: fragment("(?)->>'type' = 'Create'", activity.data)
|
preload: [object: o]
|
||||||
|
)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Wrong name, returns all.
|
def get_by_id(id) do
|
||||||
def all_non_create_by_object_ap_id_q(ap_id) do
|
Repo.get(Activity, id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_by_id_with_object(id) do
|
||||||
|
from(activity in Activity,
|
||||||
|
where: activity.id == ^id,
|
||||||
|
inner_join: o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
|
||||||
|
o.data,
|
||||||
|
activity.data,
|
||||||
|
activity.data
|
||||||
|
),
|
||||||
|
preload: [object: o]
|
||||||
|
)
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
|
def by_object_ap_id(ap_id) do
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
where:
|
where:
|
||||||
|
@ -60,12 +138,7 @@ def all_non_create_by_object_ap_id_q(ap_id) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Wrong name plz fix thx
|
def create_by_object_ap_id(ap_ids) when is_list(ap_ids) do
|
||||||
def all_by_object_ap_id(ap_id) do
|
|
||||||
Repo.all(all_by_object_ap_id_q(ap_id))
|
|
||||||
end
|
|
||||||
|
|
||||||
def create_activity_by_object_id_query(ap_ids) do
|
|
||||||
from(
|
from(
|
||||||
activity in Activity,
|
activity in Activity,
|
||||||
where:
|
where:
|
||||||
|
@ -79,19 +152,66 @@ def create_activity_by_object_id_query(ap_ids) do
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_create_activity_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
def create_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||||
create_activity_by_object_id_query([ap_id])
|
from(
|
||||||
|
activity in Activity,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
|
activity.data,
|
||||||
|
activity.data,
|
||||||
|
^to_string(ap_id)
|
||||||
|
),
|
||||||
|
where: fragment("(?)->>'type' = 'Create'", activity.data)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_by_object_ap_id(_), do: nil
|
||||||
|
|
||||||
|
def get_all_create_by_object_ap_id(ap_id) do
|
||||||
|
Repo.all(create_by_object_ap_id(ap_id))
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_create_by_object_ap_id(ap_id) when is_binary(ap_id) do
|
||||||
|
create_by_object_ap_id(ap_id)
|
||||||
|> Repo.one()
|
|> Repo.one()
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_create_activity_by_object_ap_id(_), do: nil
|
def get_create_by_object_ap_id(_), do: nil
|
||||||
|
|
||||||
def normalize(obj) when is_map(obj), do: normalize(obj["id"])
|
def create_by_object_ap_id_with_object(ap_id) when is_binary(ap_id) do
|
||||||
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id(ap_id)
|
from(
|
||||||
def normalize(_), do: nil
|
activity in Activity,
|
||||||
|
where:
|
||||||
|
fragment(
|
||||||
|
"coalesce((?)->'object'->>'id', (?)->>'object') = ?",
|
||||||
|
activity.data,
|
||||||
|
activity.data,
|
||||||
|
^to_string(ap_id)
|
||||||
|
),
|
||||||
|
where: fragment("(?)->>'type' = 'Create'", activity.data),
|
||||||
|
inner_join: o in Object,
|
||||||
|
on:
|
||||||
|
fragment(
|
||||||
|
"(?->>'id') = COALESCE(?->'object'->>'id', ?->>'object')",
|
||||||
|
o.data,
|
||||||
|
activity.data,
|
||||||
|
activity.data
|
||||||
|
),
|
||||||
|
preload: [object: o]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_by_object_ap_id_with_object(_), do: nil
|
||||||
|
|
||||||
|
def get_create_by_object_ap_id_with_object(ap_id) do
|
||||||
|
ap_id
|
||||||
|
|> create_by_object_ap_id_with_object()
|
||||||
|
|> Repo.one()
|
||||||
|
end
|
||||||
|
|
||||||
defp get_in_reply_to_activity_from_object(%Object{data: %{"inReplyTo" => ap_id}}) do
|
defp get_in_reply_to_activity_from_object(%Object{data: %{"inReplyTo" => ap_id}}) do
|
||||||
get_create_activity_by_object_ap_id(ap_id)
|
get_create_by_object_ap_id_with_object(ap_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
defp get_in_reply_to_activity_from_object(_), do: nil
|
defp get_in_reply_to_activity_from_object(_), do: nil
|
||||||
|
@ -99,4 +219,92 @@ defp get_in_reply_to_activity_from_object(_), do: nil
|
||||||
def get_in_reply_to_activity(%Activity{data: %{"object" => object}}) do
|
def get_in_reply_to_activity(%Activity{data: %{"object" => object}}) do
|
||||||
get_in_reply_to_activity_from_object(Object.normalize(object))
|
get_in_reply_to_activity_from_object(Object.normalize(object))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def normalize(obj) when is_map(obj), do: get_by_ap_id_with_object(obj["id"])
|
||||||
|
def normalize(ap_id) when is_binary(ap_id), do: get_by_ap_id_with_object(ap_id)
|
||||||
|
def normalize(_), do: nil
|
||||||
|
|
||||||
|
def delete_by_ap_id(id) when is_binary(id) do
|
||||||
|
by_object_ap_id(id)
|
||||||
|
|> select([u], u)
|
||||||
|
|> Repo.delete_all()
|
||||||
|
|> elem(1)
|
||||||
|
|> Enum.find(fn
|
||||||
|
%{data: %{"type" => "Create", "object" => %{"id" => ap_id}}} -> ap_id == id
|
||||||
|
_ -> nil
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def delete_by_ap_id(_), do: nil
|
||||||
|
|
||||||
|
for {ap_type, type} <- @mastodon_notification_types do
|
||||||
|
def mastodon_notification_type(%Activity{data: %{"type" => unquote(ap_type)}}),
|
||||||
|
do: unquote(type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def mastodon_notification_type(%Activity{}), do: nil
|
||||||
|
|
||||||
|
def from_mastodon_notification_type(type) do
|
||||||
|
Map.get(@mastodon_to_ap_notification_types, type)
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_by_actor_and_id(actor, status_ids \\ [])
|
||||||
|
def all_by_actor_and_id(_actor, []), do: []
|
||||||
|
|
||||||
|
def all_by_actor_and_id(actor, status_ids) do
|
||||||
|
Activity
|
||||||
|
|> where([s], s.id in ^status_ids)
|
||||||
|
|> where([s], s.actor == ^actor)
|
||||||
|
|> Repo.all()
|
||||||
|
end
|
||||||
|
|
||||||
|
def increase_replies_count(nil), do: nil
|
||||||
|
|
||||||
|
def increase_replies_count(object_ap_id) do
|
||||||
|
from(a in create_by_object_ap_id(object_ap_id),
|
||||||
|
update: [
|
||||||
|
set: [
|
||||||
|
data:
|
||||||
|
fragment(
|
||||||
|
"""
|
||||||
|
jsonb_set(?, '{object, repliesCount}',
|
||||||
|
(coalesce((?->'object'->>'repliesCount')::int, 0) + 1)::varchar::jsonb, true)
|
||||||
|
""",
|
||||||
|
a.data,
|
||||||
|
a.data
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
|> case do
|
||||||
|
{1, [activity]} -> activity
|
||||||
|
_ -> {:error, "Not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def decrease_replies_count(nil), do: nil
|
||||||
|
|
||||||
|
def decrease_replies_count(object_ap_id) do
|
||||||
|
from(a in create_by_object_ap_id(object_ap_id),
|
||||||
|
update: [
|
||||||
|
set: [
|
||||||
|
data:
|
||||||
|
fragment(
|
||||||
|
"""
|
||||||
|
jsonb_set(?, '{object, repliesCount}',
|
||||||
|
(greatest(0, (?->'object'->>'repliesCount')::int - 1))::varchar::jsonb, true)
|
||||||
|
""",
|
||||||
|
a.data,
|
||||||
|
a.data
|
||||||
|
)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|> Repo.update_all([])
|
||||||
|
|> case do
|
||||||
|
{1, [activity]} -> activity
|
||||||
|
_ -> {:error, "Not found"}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,36 +1,55 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Application do
|
defmodule Pleroma.Application do
|
||||||
use Application
|
use Application
|
||||||
import Supervisor.Spec
|
import Supervisor.Spec
|
||||||
|
|
||||||
@name "Pleroma"
|
@name Mix.Project.config()[:name]
|
||||||
@version Mix.Project.config()[:version]
|
@version Mix.Project.config()[:version]
|
||||||
|
@repository Mix.Project.config()[:source_url]
|
||||||
def name, do: @name
|
def name, do: @name
|
||||||
def version, do: @version
|
def version, do: @version
|
||||||
def named_version(), do: @name <> " " <> @version
|
def named_version, do: @name <> " " <> @version
|
||||||
|
def repository, do: @repository
|
||||||
|
|
||||||
def user_agent() do
|
def user_agent do
|
||||||
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
info = "#{Pleroma.Web.base_url()} <#{Pleroma.Config.get([:instance, :email], "")}>"
|
||||||
named_version() <> "; " <> info
|
named_version() <> "; " <> info
|
||||||
end
|
end
|
||||||
|
|
||||||
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
# See http://elixir-lang.org/docs/stable/elixir/Application.html
|
||||||
# for more information on OTP Applications
|
# for more information on OTP Applications
|
||||||
@env Mix.env()
|
|
||||||
def start(_type, _args) do
|
def start(_type, _args) do
|
||||||
import Cachex.Spec
|
import Cachex.Spec
|
||||||
|
|
||||||
|
Pleroma.Config.DeprecationWarnings.warn()
|
||||||
|
setup_instrumenters()
|
||||||
|
|
||||||
# Define workers and child supervisors to be supervised
|
# Define workers and child supervisors to be supervised
|
||||||
children =
|
children =
|
||||||
[
|
[
|
||||||
# Start the Ecto repository
|
# Start the Ecto repository
|
||||||
supervisor(Pleroma.Repo, []),
|
supervisor(Pleroma.Repo, []),
|
||||||
worker(Pleroma.Emoji, []),
|
worker(Pleroma.Emoji, []),
|
||||||
|
worker(Pleroma.Captcha, []),
|
||||||
|
worker(
|
||||||
|
Cachex,
|
||||||
|
[
|
||||||
|
:used_captcha_cache,
|
||||||
|
[
|
||||||
|
ttl_interval: :timer.seconds(Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid]))
|
||||||
|
]
|
||||||
|
],
|
||||||
|
id: :cachex_used_captcha_cache
|
||||||
|
),
|
||||||
worker(
|
worker(
|
||||||
Cachex,
|
Cachex,
|
||||||
[
|
[
|
||||||
:user_cache,
|
:user_cache,
|
||||||
[
|
[
|
||||||
default_ttl: 25000,
|
default_ttl: 25_000,
|
||||||
ttl_interval: 1000,
|
ttl_interval: 1000,
|
||||||
limit: 2500
|
limit: 2500
|
||||||
]
|
]
|
||||||
|
@ -42,13 +61,34 @@ def start(_type, _args) do
|
||||||
[
|
[
|
||||||
:object_cache,
|
:object_cache,
|
||||||
[
|
[
|
||||||
default_ttl: 25000,
|
default_ttl: 25_000,
|
||||||
ttl_interval: 1000,
|
ttl_interval: 1000,
|
||||||
limit: 2500
|
limit: 2500
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
id: :cachex_object
|
id: :cachex_object
|
||||||
),
|
),
|
||||||
|
worker(
|
||||||
|
Cachex,
|
||||||
|
[
|
||||||
|
:rich_media_cache,
|
||||||
|
[
|
||||||
|
default_ttl: :timer.minutes(120),
|
||||||
|
limit: 5000
|
||||||
|
]
|
||||||
|
],
|
||||||
|
id: :cachex_rich_media
|
||||||
|
),
|
||||||
|
worker(
|
||||||
|
Cachex,
|
||||||
|
[
|
||||||
|
:scrubber_cache,
|
||||||
|
[
|
||||||
|
limit: 2500
|
||||||
|
]
|
||||||
|
],
|
||||||
|
id: :cachex_scrubber
|
||||||
|
),
|
||||||
worker(
|
worker(
|
||||||
Cachex,
|
Cachex,
|
||||||
[
|
[
|
||||||
|
@ -64,9 +104,15 @@ def start(_type, _args) do
|
||||||
],
|
],
|
||||||
id: :cachex_idem
|
id: :cachex_idem
|
||||||
),
|
),
|
||||||
|
worker(Pleroma.FlakeId, []),
|
||||||
|
worker(Pleroma.ScheduledActivityWorker, [])
|
||||||
|
] ++
|
||||||
|
hackney_pool_children() ++
|
||||||
|
[
|
||||||
worker(Pleroma.Web.Federator.RetryQueue, []),
|
worker(Pleroma.Web.Federator.RetryQueue, []),
|
||||||
worker(Pleroma.Web.Federator, []),
|
worker(Pleroma.Stats, []),
|
||||||
worker(Pleroma.Stats, [])
|
worker(Task, [&Pleroma.Web.Push.init/0], restart: :temporary, id: :web_push_init),
|
||||||
|
worker(Task, [&Pleroma.Web.Federator.init/0], restart: :temporary, id: :federator_init)
|
||||||
] ++
|
] ++
|
||||||
streamer_child() ++
|
streamer_child() ++
|
||||||
chat_child() ++
|
chat_child() ++
|
||||||
|
@ -82,15 +128,47 @@ def start(_type, _args) do
|
||||||
Supervisor.start_link(children, opts)
|
Supervisor.start_link(children, opts)
|
||||||
end
|
end
|
||||||
|
|
||||||
if Mix.env() == :test do
|
defp setup_instrumenters do
|
||||||
defp streamer_child(), do: []
|
require Prometheus.Registry
|
||||||
defp chat_child(), do: []
|
|
||||||
|
:ok =
|
||||||
|
:telemetry.attach(
|
||||||
|
"prometheus-ecto",
|
||||||
|
[:pleroma, :repo, :query],
|
||||||
|
&Pleroma.Repo.Instrumenter.handle_event/4,
|
||||||
|
%{}
|
||||||
|
)
|
||||||
|
|
||||||
|
Prometheus.Registry.register_collector(:prometheus_process_collector)
|
||||||
|
Pleroma.Web.Endpoint.MetricsExporter.setup()
|
||||||
|
Pleroma.Web.Endpoint.PipelineInstrumenter.setup()
|
||||||
|
Pleroma.Web.Endpoint.Instrumenter.setup()
|
||||||
|
Pleroma.Repo.Instrumenter.setup()
|
||||||
|
end
|
||||||
|
|
||||||
|
def enabled_hackney_pools do
|
||||||
|
[:media] ++
|
||||||
|
if Application.get_env(:tesla, :adapter) == Tesla.Adapter.Hackney do
|
||||||
|
[:federation]
|
||||||
else
|
else
|
||||||
defp streamer_child() do
|
[]
|
||||||
|
end ++
|
||||||
|
if Pleroma.Config.get([Pleroma.Uploader, :proxy_remote]) do
|
||||||
|
[:upload]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if Mix.env() == :test do
|
||||||
|
defp streamer_child, do: []
|
||||||
|
defp chat_child, do: []
|
||||||
|
else
|
||||||
|
defp streamer_child do
|
||||||
[worker(Pleroma.Web.Streamer, [])]
|
[worker(Pleroma.Web.Streamer, [])]
|
||||||
end
|
end
|
||||||
|
|
||||||
defp chat_child() do
|
defp chat_child do
|
||||||
if Pleroma.Config.get([:chat, :enabled]) do
|
if Pleroma.Config.get([:chat, :enabled]) do
|
||||||
[worker(Pleroma.Web.ChatChannel.ChatChannelState, [])]
|
[worker(Pleroma.Web.ChatChannel.ChatChannelState, [])]
|
||||||
else
|
else
|
||||||
|
@ -98,4 +176,11 @@ defp chat_child() do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
defp hackney_pool_children do
|
||||||
|
for pool <- enabled_hackney_pools() do
|
||||||
|
options = Pleroma.Config.get([:hackney_pools, pool])
|
||||||
|
:hackney_pool.child_spec(pool, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Captcha do
|
||||||
|
alias Calendar.DateTime
|
||||||
|
alias Plug.Crypto.KeyGenerator
|
||||||
|
alias Plug.Crypto.MessageEncryptor
|
||||||
|
|
||||||
|
use GenServer
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def start_link do
|
||||||
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def init(_) do
|
||||||
|
{:ok, nil}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Ask the configured captcha service for a new captcha
|
||||||
|
"""
|
||||||
|
def new do
|
||||||
|
GenServer.call(__MODULE__, :new)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Ask the configured captcha service to validate the captcha
|
||||||
|
"""
|
||||||
|
def validate(token, captcha, answer_data) do
|
||||||
|
GenServer.call(__MODULE__, {:validate, token, captcha, answer_data})
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_call(:new, _from, state) do
|
||||||
|
enabled = Pleroma.Config.get([__MODULE__, :enabled])
|
||||||
|
|
||||||
|
if !enabled do
|
||||||
|
{:reply, %{type: :none}, state}
|
||||||
|
else
|
||||||
|
new_captcha = method().new()
|
||||||
|
|
||||||
|
secret_key_base = Pleroma.Config.get!([Pleroma.Web.Endpoint, :secret_key_base])
|
||||||
|
|
||||||
|
# This make salt a little different for two keys
|
||||||
|
token = new_captcha[:token]
|
||||||
|
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
|
||||||
|
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
|
||||||
|
# Basicallty copy what Phoenix.Token does here, add the time to
|
||||||
|
# the actual data and make it a binary to then encrypt it
|
||||||
|
encrypted_captcha_answer =
|
||||||
|
%{
|
||||||
|
at: DateTime.now_utc(),
|
||||||
|
answer_data: new_captcha[:answer_data]
|
||||||
|
}
|
||||||
|
|> :erlang.term_to_binary()
|
||||||
|
|> MessageEncryptor.encrypt(secret, sign_secret)
|
||||||
|
|
||||||
|
{
|
||||||
|
:reply,
|
||||||
|
# Repalce the answer with the encrypted answer
|
||||||
|
%{new_captcha | answer_data: encrypted_captcha_answer},
|
||||||
|
state
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc false
|
||||||
|
def handle_call({:validate, token, captcha, answer_data}, _from, state) do
|
||||||
|
secret_key_base = Pleroma.Config.get!([Pleroma.Web.Endpoint, :secret_key_base])
|
||||||
|
secret = KeyGenerator.generate(secret_key_base, token <> "_encrypt")
|
||||||
|
sign_secret = KeyGenerator.generate(secret_key_base, token <> "_sign")
|
||||||
|
|
||||||
|
# If the time found is less than (current_time-seconds_valid) then the time has already passed
|
||||||
|
# Later we check that the time found is more than the presumed invalidatation time, that means
|
||||||
|
# that the data is still valid and the captcha can be checked
|
||||||
|
seconds_valid = Pleroma.Config.get!([Pleroma.Captcha, :seconds_valid])
|
||||||
|
valid_if_after = DateTime.subtract!(DateTime.now_utc(), seconds_valid)
|
||||||
|
|
||||||
|
result =
|
||||||
|
with {:ok, data} <- MessageEncryptor.decrypt(answer_data, secret, sign_secret),
|
||||||
|
%{at: at, answer_data: answer_md5} <- :erlang.binary_to_term(data) do
|
||||||
|
try do
|
||||||
|
if DateTime.before?(at, valid_if_after), do: throw({:error, "CAPTCHA expired"})
|
||||||
|
|
||||||
|
if not is_nil(Cachex.get!(:used_captcha_cache, token)),
|
||||||
|
do: throw({:error, "CAPTCHA already used"})
|
||||||
|
|
||||||
|
res = method().validate(token, captcha, answer_md5)
|
||||||
|
# Throw if an error occurs
|
||||||
|
if res != :ok, do: throw(res)
|
||||||
|
|
||||||
|
# Mark this captcha as used
|
||||||
|
{:ok, _} =
|
||||||
|
Cachex.put(:used_captcha_cache, token, true, ttl: :timer.seconds(seconds_valid))
|
||||||
|
|
||||||
|
:ok
|
||||||
|
catch
|
||||||
|
:throw, e -> e
|
||||||
|
end
|
||||||
|
else
|
||||||
|
_ -> {:error, "Invalid answer data"}
|
||||||
|
end
|
||||||
|
|
||||||
|
{:reply, result, state}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp method, do: Pleroma.Config.get!([__MODULE__, :method])
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Captcha.Service do
|
||||||
|
@doc """
|
||||||
|
Request new captcha from a captcha service.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
Type/Name of the service, the token to identify the captcha,
|
||||||
|
the data of the answer and service-specific data to use the newly created captcha
|
||||||
|
"""
|
||||||
|
@callback new() :: %{
|
||||||
|
type: atom(),
|
||||||
|
token: String.t(),
|
||||||
|
answer_data: any()
|
||||||
|
}
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Validated the provided captcha solution.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
* `token` the captcha is associated with
|
||||||
|
* `captcha` solution of the captcha to validate
|
||||||
|
* `answer_data` is the data needed to validate the answer (presumably encrypted)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
|
||||||
|
`true` if captcha is valid, `false` if not
|
||||||
|
"""
|
||||||
|
@callback validate(
|
||||||
|
token :: String.t(),
|
||||||
|
captcha :: String.t(),
|
||||||
|
answer_data :: any()
|
||||||
|
) :: :ok | {:error, String.t()}
|
||||||
|
end
|
|
@ -0,0 +1,37 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Captcha.Kocaptcha do
|
||||||
|
alias Pleroma.Captcha.Service
|
||||||
|
@behaviour Service
|
||||||
|
|
||||||
|
@impl Service
|
||||||
|
def new do
|
||||||
|
endpoint = Pleroma.Config.get!([__MODULE__, :endpoint])
|
||||||
|
|
||||||
|
case Tesla.get(endpoint <> "/new") do
|
||||||
|
{:error, _} ->
|
||||||
|
%{error: "Kocaptcha service unavailable"}
|
||||||
|
|
||||||
|
{:ok, res} ->
|
||||||
|
json_resp = Poison.decode!(res.body)
|
||||||
|
|
||||||
|
%{
|
||||||
|
type: :kocaptcha,
|
||||||
|
token: json_resp["token"],
|
||||||
|
url: endpoint <> json_resp["url"],
|
||||||
|
answer_data: json_resp["md5"]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Service
|
||||||
|
def validate(_token, captcha, answer_data) do
|
||||||
|
# Here the token is unsed, because the unencrypted captcha answer is just passed to method
|
||||||
|
if not is_nil(captcha) and
|
||||||
|
:crypto.hash(:md5, captcha) |> Base.encode16() == String.upcase(answer_data),
|
||||||
|
do: :ok,
|
||||||
|
else: {:error, "Invalid CAPTCHA"}
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,155 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Clippy do
|
||||||
|
@moduledoc false
|
||||||
|
# No software is complete until they have a Clippy implementation.
|
||||||
|
# A ballmer peak _may_ be required to change this module.
|
||||||
|
|
||||||
|
def tip do
|
||||||
|
tips()
|
||||||
|
|> Enum.random()
|
||||||
|
|> puts()
|
||||||
|
end
|
||||||
|
|
||||||
|
def tips do
|
||||||
|
host = Pleroma.Config.get([Pleroma.Web.Endpoint, :url, :host])
|
||||||
|
|
||||||
|
[
|
||||||
|
"“πλήρωμα” is “pleroma” in greek",
|
||||||
|
"For an extended Pleroma Clippy Experience, use the “Redmond” themes in Pleroma FE settings",
|
||||||
|
"Staff accounts and MRF policies of Pleroma instances are disclosed on the NodeInfo endpoints for easy transparency!\n
|
||||||
|
- https://catgirl.science/misc/nodeinfo.lua?#{host}
|
||||||
|
- https://fediverse.network/#{host}/federation",
|
||||||
|
"Pleroma can federate to the Dark Web!\n
|
||||||
|
- Tor: https://git.pleroma.social/pleroma/pleroma/wikis/Easy%20Onion%20Federation%20(Tor)
|
||||||
|
- i2p: https://git.pleroma.social/pleroma/pleroma/wikis/I2p%20federation",
|
||||||
|
"Lists of Pleroma instances:\n\n- http://distsn.org/pleroma-instances.html\n- https://fediverse.network/pleroma\n- https://the-federation.info/pleroma",
|
||||||
|
"Pleroma uses the LitePub protocol - https://litepub.social",
|
||||||
|
"To receive more federated posts, subscribe to relays!\n
|
||||||
|
- How-to: https://git.pleroma.social/pleroma/pleroma/wikis/Admin%20tasks#relay-managment
|
||||||
|
- Relays: https://fediverse.network/activityrelay"
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
@spec puts(String.t() | [[IO.ANSI.ansicode() | String.t(), ...], ...]) :: nil
|
||||||
|
def puts(text_or_lines) do
|
||||||
|
import IO.ANSI
|
||||||
|
|
||||||
|
lines =
|
||||||
|
if is_binary(text_or_lines) do
|
||||||
|
String.split(text_or_lines, ~r/\n/)
|
||||||
|
else
|
||||||
|
text_or_lines
|
||||||
|
end
|
||||||
|
|
||||||
|
longest_line_size =
|
||||||
|
lines
|
||||||
|
|> Enum.map(&charlist_count_text/1)
|
||||||
|
|> Enum.sort(&>=/2)
|
||||||
|
|> List.first()
|
||||||
|
|
||||||
|
pad_text = longest_line_size
|
||||||
|
|
||||||
|
pad =
|
||||||
|
for(_ <- 1..pad_text, do: "_")
|
||||||
|
|> Enum.join("")
|
||||||
|
|
||||||
|
pad_spaces =
|
||||||
|
for(_ <- 1..pad_text, do: " ")
|
||||||
|
|> Enum.join("")
|
||||||
|
|
||||||
|
spaces = " "
|
||||||
|
|
||||||
|
pre_lines = [
|
||||||
|
" / \\#{spaces} _#{pad}___",
|
||||||
|
" | |#{spaces} / #{pad_spaces} \\"
|
||||||
|
]
|
||||||
|
|
||||||
|
for l <- pre_lines do
|
||||||
|
IO.puts(l)
|
||||||
|
end
|
||||||
|
|
||||||
|
clippy_lines = [
|
||||||
|
" #{bright()}@ @#{reset()}#{spaces} ",
|
||||||
|
" || ||#{spaces}",
|
||||||
|
" || || <--",
|
||||||
|
" |\\_/| ",
|
||||||
|
" \\___/ "
|
||||||
|
]
|
||||||
|
|
||||||
|
noclippy_line = " "
|
||||||
|
|
||||||
|
env = %{
|
||||||
|
max_size: pad_text,
|
||||||
|
pad: pad,
|
||||||
|
pad_spaces: pad_spaces,
|
||||||
|
spaces: spaces,
|
||||||
|
pre_lines: pre_lines,
|
||||||
|
noclippy_line: noclippy_line
|
||||||
|
}
|
||||||
|
|
||||||
|
# surrond one/five line clippy with blank lines around to not fuck up the layout
|
||||||
|
#
|
||||||
|
# yes this fix sucks but it's good enough, have you ever seen a release of windows
|
||||||
|
# without some butched features anyway?
|
||||||
|
lines =
|
||||||
|
if length(lines) == 1 or length(lines) == 5 do
|
||||||
|
[""] ++ lines ++ [""]
|
||||||
|
else
|
||||||
|
lines
|
||||||
|
end
|
||||||
|
|
||||||
|
clippy_line(lines, clippy_lines, env)
|
||||||
|
rescue
|
||||||
|
e ->
|
||||||
|
IO.puts("(Clippy crashed, sorry: #{inspect(e)})")
|
||||||
|
IO.puts(text_or_lines)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp clippy_line([line | lines], [prefix | clippy_lines], env) do
|
||||||
|
IO.puts([prefix <> "| ", rpad_line(line, env.max_size)])
|
||||||
|
clippy_line(lines, clippy_lines, env)
|
||||||
|
end
|
||||||
|
|
||||||
|
# more text lines but clippy's complete
|
||||||
|
defp clippy_line([line | lines], [], env) do
|
||||||
|
IO.puts([env.noclippy_line, "| ", rpad_line(line, env.max_size)])
|
||||||
|
|
||||||
|
if lines == [] do
|
||||||
|
IO.puts(env.noclippy_line <> "\\_#{env.pad}___/")
|
||||||
|
end
|
||||||
|
|
||||||
|
clippy_line(lines, [], env)
|
||||||
|
end
|
||||||
|
|
||||||
|
# no more text lines but clippy's not complete
|
||||||
|
defp clippy_line([], [clippy | clippy_lines], env) do
|
||||||
|
if env.pad do
|
||||||
|
IO.puts(clippy <> "\\_#{env.pad}___/")
|
||||||
|
clippy_line([], clippy_lines, %{env | pad: nil})
|
||||||
|
else
|
||||||
|
IO.puts(clippy)
|
||||||
|
clippy_line([], clippy_lines, env)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp clippy_line(_, _, _) do
|
||||||
|
end
|
||||||
|
|
||||||
|
defp rpad_line(line, max) do
|
||||||
|
pad = max - (charlist_count_text(line) - 2)
|
||||||
|
pads = Enum.join(for(_ <- 1..pad, do: " "))
|
||||||
|
[IO.ANSI.format(line), pads <> " |"]
|
||||||
|
end
|
||||||
|
|
||||||
|
defp charlist_count_text(line) do
|
||||||
|
if is_list(line) do
|
||||||
|
text = Enum.join(Enum.filter(line, &is_binary/1))
|
||||||
|
String.length(text)
|
||||||
|
else
|
||||||
|
String.length(line)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Config do
|
defmodule Pleroma.Config do
|
||||||
defmodule Error do
|
defmodule Error do
|
||||||
defexception [:message]
|
defexception [:message]
|
||||||
|
@ -53,4 +57,8 @@ def delete([parent_key | keys]) do
|
||||||
def delete(key) do
|
def delete(key) do
|
||||||
Application.delete_env(:pleroma, key)
|
Application.delete_env(:pleroma, key)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def oauth_consumer_strategies, do: get([:auth, :oauth_consumer_strategies], [])
|
||||||
|
|
||||||
|
def oauth_consumer_enabled?, do: oauth_consumer_strategies() != []
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Config.DeprecationWarnings do
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
def check_frontend_config_mechanism do
|
||||||
|
if Pleroma.Config.get(:fe) do
|
||||||
|
Logger.warn("""
|
||||||
|
!!!DEPRECATION WARNING!!!
|
||||||
|
You are using the old configuration mechanism for the frontend. Please check config.md.
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_hellthread_threshold do
|
||||||
|
if Pleroma.Config.get([:mrf_hellthread, :threshold]) do
|
||||||
|
Logger.warn("""
|
||||||
|
!!!DEPRECATION WARNING!!!
|
||||||
|
You are using the old configuration mechanism for the hellthread filter. Please check config.md.
|
||||||
|
""")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def warn do
|
||||||
|
check_frontend_config_mechanism()
|
||||||
|
check_hellthread_threshold()
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Emails.AdminEmail do
|
||||||
|
@moduledoc "Admin emails"
|
||||||
|
|
||||||
|
import Swoosh.Email
|
||||||
|
|
||||||
|
alias Pleroma.Web.Router.Helpers
|
||||||
|
|
||||||
|
defp instance_config, do: Pleroma.Config.get(:instance)
|
||||||
|
defp instance_name, do: instance_config()[:name]
|
||||||
|
|
||||||
|
defp instance_notify_email do
|
||||||
|
Keyword.get(instance_config(), :notify_email, instance_config()[:email])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp user_url(user) do
|
||||||
|
Helpers.o_status_url(Pleroma.Web.Endpoint, :feed_redirect, user.nickname)
|
||||||
|
end
|
||||||
|
|
||||||
|
def report(to, reporter, account, statuses, comment) do
|
||||||
|
comment_html =
|
||||||
|
if comment do
|
||||||
|
"<p>Comment: #{comment}"
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
statuses_html =
|
||||||
|
if length(statuses) > 0 do
|
||||||
|
statuses_list_html =
|
||||||
|
statuses
|
||||||
|
|> Enum.map(fn
|
||||||
|
%{id: id} ->
|
||||||
|
status_url = Helpers.o_status_url(Pleroma.Web.Endpoint, :notice, id)
|
||||||
|
"<li><a href=\"#{status_url}\">#{status_url}</li>"
|
||||||
|
|
||||||
|
id when is_binary(id) ->
|
||||||
|
"<li><a href=\"#{id}\">#{id}</li>"
|
||||||
|
end)
|
||||||
|
|> Enum.join("\n")
|
||||||
|
|
||||||
|
"""
|
||||||
|
<p> Statuses:
|
||||||
|
<ul>
|
||||||
|
#{statuses_list_html}
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
"""
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
html_body = """
|
||||||
|
<p>Reported by: <a href="#{user_url(reporter)}">#{reporter.nickname}</a></p>
|
||||||
|
<p>Reported Account: <a href="#{user_url(account)}">#{account.nickname}</a></p>
|
||||||
|
#{comment_html}
|
||||||
|
#{statuses_html}
|
||||||
|
"""
|
||||||
|
|
||||||
|
new()
|
||||||
|
|> to({to.name, to.email})
|
||||||
|
|> from({instance_name(), instance_notify_email()})
|
||||||
|
|> reply_to({reporter.name, reporter.email})
|
||||||
|
|> subject("#{instance_name()} Report")
|
||||||
|
|> html_body(html_body)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,13 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Emails.Mailer do
|
||||||
|
use Swoosh.Mailer, otp_app: :pleroma
|
||||||
|
|
||||||
|
def deliver_async(email, config \\ []) do
|
||||||
|
PleromaJobQueue.enqueue(:mailer, __MODULE__, [:deliver_async, email, config])
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(:deliver_async, email, config), do: deliver(email, config)
|
||||||
|
end
|
|
@ -0,0 +1,95 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.Emails.UserEmail do
|
||||||
|
@moduledoc "User emails"
|
||||||
|
|
||||||
|
import Swoosh.Email
|
||||||
|
|
||||||
|
alias Pleroma.Web.Endpoint
|
||||||
|
alias Pleroma.Web.Router
|
||||||
|
|
||||||
|
defp instance_config, do: Pleroma.Config.get(:instance)
|
||||||
|
|
||||||
|
defp instance_name, do: instance_config()[:name]
|
||||||
|
|
||||||
|
defp sender do
|
||||||
|
email = Keyword.get(instance_config(), :notify_email, instance_config()[:email])
|
||||||
|
{instance_name(), email}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp recipient(email, nil), do: email
|
||||||
|
defp recipient(email, name), do: {name, email}
|
||||||
|
defp recipient(%Pleroma.User{} = user), do: recipient(user.email, user.name)
|
||||||
|
|
||||||
|
def password_reset_email(user, password_reset_token) when is_binary(password_reset_token) do
|
||||||
|
password_reset_url =
|
||||||
|
Router.Helpers.util_url(
|
||||||
|
Endpoint,
|
||||||
|
:show_password_reset,
|
||||||
|
password_reset_token
|
||||||
|
)
|
||||||
|
|
||||||
|
html_body = """
|
||||||
|
<h3>Reset your password at #{instance_name()}</h3>
|
||||||
|
<p>Someone has requested password change for your account at #{instance_name()}.</p>
|
||||||
|
<p>If it was you, visit the following link to proceed: <a href="#{password_reset_url}">reset password</a>.</p>
|
||||||
|
<p>If it was someone else, nothing to worry about: your data is secure and your password has not been changed.</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
new()
|
||||||
|
|> to(recipient(user))
|
||||||
|
|> from(sender())
|
||||||
|
|> subject("Password reset")
|
||||||
|
|> html_body(html_body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def user_invitation_email(
|
||||||
|
user,
|
||||||
|
%Pleroma.UserInviteToken{} = user_invite_token,
|
||||||
|
to_email,
|
||||||
|
to_name \\ nil
|
||||||
|
) do
|
||||||
|
registration_url =
|
||||||
|
Router.Helpers.redirect_url(
|
||||||
|
Endpoint,
|
||||||
|
:registration_page,
|
||||||
|
user_invite_token.token
|
||||||
|
)
|
||||||
|
|
||||||
|
html_body = """
|
||||||
|
<h3>You are invited to #{instance_name()}</h3>
|
||||||
|
<p>#{user.name} invites you to join #{instance_name()}, an instance of Pleroma federated social networking platform.</p>
|
||||||
|
<p>Click the following link to register: <a href="#{registration_url}">accept invitation</a>.</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
new()
|
||||||
|
|> to(recipient(to_email, to_name))
|
||||||
|
|> from(sender())
|
||||||
|
|> subject("Invitation to #{instance_name()}")
|
||||||
|
|> html_body(html_body)
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_confirmation_email(user) do
|
||||||
|
confirmation_url =
|
||||||
|
Router.Helpers.confirm_email_url(
|
||||||
|
Endpoint,
|
||||||
|
:confirm_email,
|
||||||
|
user.id,
|
||||||
|
to_string(user.info.confirmation_token)
|
||||||
|
)
|
||||||
|
|
||||||
|
html_body = """
|
||||||
|
<h3>Welcome to #{instance_name()}!</h3>
|
||||||
|
<p>Email confirmation is required to activate the account.</p>
|
||||||
|
<p>Click the following link to proceed: <a href="#{confirmation_url}">activate your account</a>.</p>
|
||||||
|
"""
|
||||||
|
|
||||||
|
new()
|
||||||
|
|> to(recipient(user))
|
||||||
|
|> from(sender())
|
||||||
|
|> subject("#{instance_name()} account confirmation")
|
||||||
|
|> html_body(html_body)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,25 +1,35 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Emoji do
|
defmodule Pleroma.Emoji do
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
The emojis are loaded from:
|
The emojis are loaded from:
|
||||||
|
|
||||||
* the built-in Finmojis (if enabled in configuration),
|
* the built-in Finmojis (if enabled in configuration),
|
||||||
* the files: `config/emoji.txt` and `config/custom_emoji.txt`
|
* the files: `config/emoji.txt` and `config/custom_emoji.txt`
|
||||||
* glob paths
|
* glob paths, nested folder is used as tag name for grouping e.g. priv/static/emoji/custom/nested_folder
|
||||||
|
|
||||||
This GenServer stores in an ETS table the list of the loaded emojis, and also allows to reload the list at runtime.
|
This GenServer stores in an ETS table the list of the loaded emojis, and also allows to reload the list at runtime.
|
||||||
"""
|
"""
|
||||||
use GenServer
|
use GenServer
|
||||||
|
|
||||||
|
@type pattern :: Regex.t() | module() | String.t()
|
||||||
|
@type patterns :: pattern() | [pattern()]
|
||||||
|
@type group_patterns :: keyword(patterns())
|
||||||
|
|
||||||
@ets __MODULE__.Ets
|
@ets __MODULE__.Ets
|
||||||
@ets_options [:set, :protected, :named_table, {:read_concurrency, true}]
|
@ets_options [:ordered_set, :protected, :named_table, {:read_concurrency, true}]
|
||||||
|
@groups Application.get_env(:pleroma, :emoji)[:groups]
|
||||||
|
|
||||||
@doc false
|
@doc false
|
||||||
def start_link() do
|
def start_link do
|
||||||
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
GenServer.start_link(__MODULE__, [], name: __MODULE__)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "Reloads the emojis from disk."
|
@doc "Reloads the emojis from disk."
|
||||||
@spec reload() :: :ok
|
@spec reload() :: :ok
|
||||||
def reload() do
|
def reload do
|
||||||
GenServer.call(__MODULE__, :reload)
|
GenServer.call(__MODULE__, :reload)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,7 +44,7 @@ def get(name) do
|
||||||
|
|
||||||
@doc "Returns all the emojos!!"
|
@doc "Returns all the emojos!!"
|
||||||
@spec get_all() :: [{String.t(), String.t()}, ...]
|
@spec get_all() :: [{String.t(), String.t()}, ...]
|
||||||
def get_all() do
|
def get_all do
|
||||||
:ets.tab2list(@ets)
|
:ets.tab2list(@ets)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -68,14 +78,15 @@ def code_change(_old_vsn, state, _extra) do
|
||||||
{:ok, state}
|
{:ok, state}
|
||||||
end
|
end
|
||||||
|
|
||||||
defp load() do
|
defp load do
|
||||||
|
finmoji_enabled = Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)
|
||||||
|
shortcode_globs = Application.get_env(:pleroma, :emoji)[:shortcode_globs] || []
|
||||||
|
|
||||||
emojis =
|
emojis =
|
||||||
(load_finmoji(Keyword.get(Application.get_env(:pleroma, :instance), :finmoji_enabled)) ++
|
(load_finmoji(finmoji_enabled) ++
|
||||||
load_from_file("config/emoji.txt") ++
|
load_from_file("config/emoji.txt") ++
|
||||||
load_from_file("config/custom_emoji.txt") ++
|
load_from_file("config/custom_emoji.txt") ++
|
||||||
load_from_globs(
|
load_from_globs(shortcode_globs))
|
||||||
Keyword.get(Application.get_env(:pleroma, :emoji, []), :shortcode_globs, [])
|
|
||||||
))
|
|
||||||
|> Enum.reject(fn value -> value == nil end)
|
|> Enum.reject(fn value -> value == nil end)
|
||||||
|
|
||||||
true = :ets.insert(@ets, emojis)
|
true = :ets.insert(@ets, emojis)
|
||||||
|
@ -147,9 +158,12 @@ defp load() do
|
||||||
"white_nights",
|
"white_nights",
|
||||||
"woollysocks"
|
"woollysocks"
|
||||||
]
|
]
|
||||||
|
|
||||||
defp load_finmoji(true) do
|
defp load_finmoji(true) do
|
||||||
Enum.map(@finmoji, fn finmoji ->
|
Enum.map(@finmoji, fn finmoji ->
|
||||||
{finmoji, "/finmoji/128px/#{finmoji}-128.png"}
|
file_name = "/finmoji/128px/#{finmoji}-128.png"
|
||||||
|
group = match_extra(@groups, file_name)
|
||||||
|
{finmoji, file_name, to_string(group)}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -165,11 +179,17 @@ defp load_from_file(file) do
|
||||||
|
|
||||||
defp load_from_file_stream(stream) do
|
defp load_from_file_stream(stream) do
|
||||||
stream
|
stream
|
||||||
|> Stream.map(&String.strip/1)
|
|> Stream.map(&String.trim/1)
|
||||||
|> Stream.map(fn line ->
|
|> Stream.map(fn line ->
|
||||||
case String.split(line, ~r/,\s*/) do
|
case String.split(line, ~r/,\s*/) do
|
||||||
[name, file] -> {name, file}
|
[name, file, tags] ->
|
||||||
_ -> nil
|
{name, file, tags}
|
||||||
|
|
||||||
|
[name, file] ->
|
||||||
|
{name, file, to_string(match_extra(@groups, file))}
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|> Enum.to_list()
|
|> Enum.to_list()
|
||||||
|
@ -186,9 +206,40 @@ defp load_from_globs(globs) do
|
||||||
|> Enum.concat()
|
|> Enum.concat()
|
||||||
|
|
||||||
Enum.map(paths, fn path ->
|
Enum.map(paths, fn path ->
|
||||||
|
tag = match_extra(@groups, Path.join("/", Path.relative_to(path, static_path)))
|
||||||
shortcode = Path.basename(path, Path.extname(path))
|
shortcode = Path.basename(path, Path.extname(path))
|
||||||
external_path = Path.join("/", Path.relative_to(path, static_path))
|
external_path = Path.join("/", Path.relative_to(path, static_path))
|
||||||
{shortcode, external_path}
|
{shortcode, external_path, to_string(tag)}
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Finds a matching group for the given emoji filename
|
||||||
|
"""
|
||||||
|
@spec match_extra(group_patterns(), String.t()) :: atom() | nil
|
||||||
|
def match_extra(group_patterns, filename) do
|
||||||
|
match_group_patterns(group_patterns, fn pattern ->
|
||||||
|
case pattern do
|
||||||
|
%Regex{} = regex -> Regex.match?(regex, filename)
|
||||||
|
string when is_binary(string) -> filename == string
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp match_group_patterns(group_patterns, matcher) do
|
||||||
|
Enum.find_value(group_patterns, fn {group, patterns} ->
|
||||||
|
patterns =
|
||||||
|
patterns
|
||||||
|
|> List.wrap()
|
||||||
|
|> Enum.map(fn pattern ->
|
||||||
|
if String.contains?(pattern, "*") do
|
||||||
|
~r(#{String.replace(pattern, "*", ".*")})
|
||||||
|
else
|
||||||
|
pattern
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
Enum.any?(patterns, matcher) && group
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,10 +1,18 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Filter do
|
defmodule Pleroma.Filter do
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
import Ecto.{Changeset, Query}
|
|
||||||
alias Pleroma.{User, Repo, Activity}
|
import Ecto.Changeset
|
||||||
|
import Ecto.Query
|
||||||
|
|
||||||
|
alias Pleroma.Repo
|
||||||
|
alias Pleroma.User
|
||||||
|
|
||||||
schema "filters" do
|
schema "filters" do
|
||||||
belongs_to(:user, Pleroma.User)
|
belongs_to(:user, User, type: Pleroma.FlakeId)
|
||||||
field(:filter_id, :integer)
|
field(:filter_id, :integer)
|
||||||
field(:hide, :boolean, default: false)
|
field(:hide, :boolean, default: false)
|
||||||
field(:whole_word, :boolean, default: true)
|
field(:whole_word, :boolean, default: true)
|
||||||
|
@ -26,7 +34,7 @@ def get(id, %{id: user_id} = _user) do
|
||||||
Repo.one(query)
|
Repo.one(query)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_filters(%Pleroma.User{id: user_id} = user) do
|
def get_filters(%User{id: user_id} = _user) do
|
||||||
query =
|
query =
|
||||||
from(
|
from(
|
||||||
f in Pleroma.Filter,
|
f in Pleroma.Filter,
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.FlakeId do
|
||||||
|
@moduledoc """
|
||||||
|
Flake is a decentralized, k-ordered id generation service.
|
||||||
|
|
||||||
|
Adapted from:
|
||||||
|
|
||||||
|
* [flaky](https://github.com/nirvana/flaky), released under the terms of the Truly Free License,
|
||||||
|
* [Flake](https://github.com/boundary/flake), Copyright 2012, Boundary, Apache License, Version 2.0
|
||||||
|
"""
|
||||||
|
|
||||||
|
@type t :: binary
|
||||||
|
|
||||||
|
@behaviour Ecto.Type
|
||||||
|
use GenServer
|
||||||
|
require Logger
|
||||||
|
alias __MODULE__
|
||||||
|
import Kernel, except: [to_string: 1]
|
||||||
|
|
||||||
|
defstruct node: nil, time: 0, sq: 0
|
||||||
|
|
||||||
|
@doc "Converts a binary Flake to a String"
|
||||||
|
def to_string(<<0::integer-size(64), id::integer-size(64)>>) do
|
||||||
|
Kernel.to_string(id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_string(<<_::integer-size(64), _::integer-size(48), _::integer-size(16)>> = flake) do
|
||||||
|
encode_base62(flake)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_string(s), do: s
|
||||||
|
|
||||||
|
def from_string(int) when is_integer(int) do
|
||||||
|
from_string(Kernel.to_string(int))
|
||||||
|
end
|
||||||
|
|
||||||
|
for i <- [-1, 0] do
|
||||||
|
def from_string(unquote(i)), do: <<0::integer-size(128)>>
|
||||||
|
def from_string(unquote(Kernel.to_string(i))), do: <<0::integer-size(128)>>
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_string(<<_::integer-size(128)>> = flake), do: flake
|
||||||
|
|
||||||
|
def from_string(string) when is_binary(string) and byte_size(string) < 18 do
|
||||||
|
case Integer.parse(string) do
|
||||||
|
{id, ""} -> <<0::integer-size(64), id::integer-size(64)>>
|
||||||
|
_ -> nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_string(string) do
|
||||||
|
string |> decode_base62 |> from_integer
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_integer(<<integer::integer-size(128)>>), do: integer
|
||||||
|
|
||||||
|
def from_integer(integer) do
|
||||||
|
<<_time::integer-size(64), _node::integer-size(48), _seq::integer-size(16)>> =
|
||||||
|
<<integer::integer-size(128)>>
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc "Generates a Flake"
|
||||||
|
@spec get :: binary
|
||||||
|
def get, do: to_string(:gen_server.call(:flake, :get))
|
||||||
|
|
||||||
|
# -- Ecto.Type API
|
||||||
|
@impl Ecto.Type
|
||||||
|
def type, do: :uuid
|
||||||
|
|
||||||
|
@impl Ecto.Type
|
||||||
|
def cast(value) do
|
||||||
|
{:ok, FlakeId.to_string(value)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Ecto.Type
|
||||||
|
def load(value) do
|
||||||
|
{:ok, FlakeId.to_string(value)}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl Ecto.Type
|
||||||
|
def dump(value) do
|
||||||
|
{:ok, FlakeId.from_string(value)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def autogenerate, do: get()
|
||||||
|
|
||||||
|
# -- GenServer API
|
||||||
|
def start_link do
|
||||||
|
:gen_server.start_link({:local, :flake}, __MODULE__, [], [])
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
|
def init([]) do
|
||||||
|
{:ok, %FlakeId{node: worker_id(), time: time()}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@impl GenServer
|
||||||
|
def handle_call(:get, _from, state) do
|
||||||
|
{flake, new_state} = get(time(), state)
|
||||||
|
{:reply, flake, new_state}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Matches when the calling time is the same as the state time. Incr. sq
|
||||||
|
defp get(time, %FlakeId{time: time, node: node, sq: seq}) do
|
||||||
|
new_state = %FlakeId{time: time, node: node, sq: seq + 1}
|
||||||
|
{gen_flake(new_state), new_state}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Matches when the times are different, reset sq
|
||||||
|
defp get(newtime, %FlakeId{time: time, node: node}) when newtime > time do
|
||||||
|
new_state = %FlakeId{time: newtime, node: node, sq: 0}
|
||||||
|
{gen_flake(new_state), new_state}
|
||||||
|
end
|
||||||
|
|
||||||
|
# Error when clock is running backwards
|
||||||
|
defp get(newtime, %FlakeId{time: time}) when newtime < time do
|
||||||
|
{:error, :clock_running_backwards}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp gen_flake(%FlakeId{time: time, node: node, sq: seq}) do
|
||||||
|
<<time::integer-size(64), node::integer-size(48), seq::integer-size(16)>>
|
||||||
|
end
|
||||||
|
|
||||||
|
defp nthchar_base62(n) when n <= 9, do: ?0 + n
|
||||||
|
defp nthchar_base62(n) when n <= 35, do: ?A + n - 10
|
||||||
|
defp nthchar_base62(n), do: ?a + n - 36
|
||||||
|
|
||||||
|
defp encode_base62(<<integer::integer-size(128)>>) do
|
||||||
|
integer
|
||||||
|
|> encode_base62([])
|
||||||
|
|> List.to_string()
|
||||||
|
end
|
||||||
|
|
||||||
|
defp encode_base62(int, acc) when int < 0, do: encode_base62(-int, acc)
|
||||||
|
defp encode_base62(int, []) when int == 0, do: '0'
|
||||||
|
defp encode_base62(int, acc) when int == 0, do: acc
|
||||||
|
|
||||||
|
defp encode_base62(int, acc) do
|
||||||
|
r = rem(int, 62)
|
||||||
|
id = div(int, 62)
|
||||||
|
acc = [nthchar_base62(r) | acc]
|
||||||
|
encode_base62(id, acc)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode_base62(s) do
|
||||||
|
decode_base62(String.to_charlist(s), 0)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp decode_base62([c | cs], acc) when c >= ?0 and c <= ?9,
|
||||||
|
do: decode_base62(cs, 62 * acc + (c - ?0))
|
||||||
|
|
||||||
|
defp decode_base62([c | cs], acc) when c >= ?A and c <= ?Z,
|
||||||
|
do: decode_base62(cs, 62 * acc + (c - ?A + 10))
|
||||||
|
|
||||||
|
defp decode_base62([c | cs], acc) when c >= ?a and c <= ?z,
|
||||||
|
do: decode_base62(cs, 62 * acc + (c - ?a + 36))
|
||||||
|
|
||||||
|
defp decode_base62([], acc), do: acc
|
||||||
|
|
||||||
|
defp time do
|
||||||
|
{mega_seconds, seconds, micro_seconds} = :erlang.timestamp()
|
||||||
|
1_000_000_000 * mega_seconds + seconds * 1000 + :erlang.trunc(micro_seconds / 1000)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp worker_id do
|
||||||
|
<<worker::integer-size(48)>> = :crypto.strong_rand_bytes(6)
|
||||||
|
worker
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,32 +1,103 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Formatter do
|
defmodule Pleroma.Formatter do
|
||||||
|
alias Pleroma.Emoji
|
||||||
|
alias Pleroma.HTML
|
||||||
alias Pleroma.User
|
alias Pleroma.User
|
||||||
alias Pleroma.Web.MediaProxy
|
alias Pleroma.Web.MediaProxy
|
||||||
alias Pleroma.HTML
|
|
||||||
alias Pleroma.Emoji
|
|
||||||
|
|
||||||
@tag_regex ~r/\#\w+/u
|
@safe_mention_regex ~r/^(\s*(?<mentions>@.+?\s+)+)(?<rest>.*)/
|
||||||
def parse_tags(text, data \\ %{}) do
|
@link_regex ~r"((?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~%:/?#[\]@!\$&'\(\)\*\+,;=.]+)|[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+"ui
|
||||||
Regex.scan(@tag_regex, text)
|
@markdown_characters_regex ~r/(`|\*|_|{|}|[|]|\(|\)|#|\+|-|\.|!)/
|
||||||
|> Enum.map(fn ["#" <> tag = full_tag] -> {full_tag, String.downcase(tag)} end)
|
|
||||||
|> (fn map ->
|
@auto_linker_config hashtag: true,
|
||||||
if data["sensitive"] in [true, "True", "true", "1"],
|
hashtag_handler: &Pleroma.Formatter.hashtag_handler/4,
|
||||||
do: [{"#nsfw", "nsfw"}] ++ map,
|
mention: true,
|
||||||
else: map
|
mention_handler: &Pleroma.Formatter.mention_handler/4
|
||||||
end).()
|
|
||||||
|
def escape_mention_handler("@" <> nickname = mention, buffer, _, _) do
|
||||||
|
case User.get_cached_by_nickname(nickname) do
|
||||||
|
%User{} ->
|
||||||
|
# escape markdown characters with `\\`
|
||||||
|
# (we don't want something like @user__name to be parsed by markdown)
|
||||||
|
String.replace(mention, @markdown_characters_regex, "\\\\\\1")
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
buffer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_mentions(text) do
|
def mention_handler("@" <> nickname, buffer, opts, acc) do
|
||||||
# Modified from https://www.w3.org/TR/html5/forms.html#valid-e-mail-address
|
case User.get_cached_by_nickname(nickname) do
|
||||||
regex =
|
%User{id: id} = user ->
|
||||||
~r/@[a-zA-Z0-9.!#$%&'*+\/=?^_`{|}~-]*@?[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*/u
|
ap_id = get_ap_id(user)
|
||||||
|
nickname_text = get_nickname_text(nickname, opts)
|
||||||
|
|
||||||
Regex.scan(regex, text)
|
link =
|
||||||
|> List.flatten()
|
"<span class='h-card'><a data-user='#{id}' class='u-url mention' href='#{ap_id}'>@<span>#{
|
||||||
|> Enum.uniq()
|
nickname_text
|
||||||
|> Enum.map(fn "@" <> match = full_match ->
|
}</span></a></span>"
|
||||||
{full_match, User.get_cached_by_nickname(match)}
|
|
||||||
end)
|
{link, %{acc | mentions: MapSet.put(acc.mentions, {"@" <> nickname, user})}}
|
||||||
|> Enum.filter(fn {_match, user} -> user end)
|
|
||||||
|
_ ->
|
||||||
|
{buffer, acc}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def hashtag_handler("#" <> tag = tag_text, _buffer, _opts, acc) do
|
||||||
|
tag = String.downcase(tag)
|
||||||
|
url = "#{Pleroma.Web.base_url()}/tag/#{tag}"
|
||||||
|
link = "<a class='hashtag' data-tag='#{tag}' href='#{url}' rel='tag'>#{tag_text}</a>"
|
||||||
|
|
||||||
|
{link, %{acc | tags: MapSet.put(acc.tags, {tag_text, tag})}}
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Parses a text and replace plain text links with HTML. Returns a tuple with a result text, mentions, and hashtags.
|
||||||
|
|
||||||
|
If the 'safe_mention' option is given, only consecutive mentions at the start the post are actually mentioned.
|
||||||
|
"""
|
||||||
|
@spec linkify(String.t(), keyword()) ::
|
||||||
|
{String.t(), [{String.t(), User.t()}], [{String.t(), String.t()}]}
|
||||||
|
def linkify(text, options \\ []) do
|
||||||
|
options = options ++ @auto_linker_config
|
||||||
|
|
||||||
|
if options[:safe_mention] && Regex.named_captures(@safe_mention_regex, text) do
|
||||||
|
%{"mentions" => mentions, "rest" => rest} = Regex.named_captures(@safe_mention_regex, text)
|
||||||
|
acc = %{mentions: MapSet.new(), tags: MapSet.new()}
|
||||||
|
|
||||||
|
{text_mentions, %{mentions: mentions}} = AutoLinker.link_map(mentions, acc, options)
|
||||||
|
{text_rest, %{tags: tags}} = AutoLinker.link_map(rest, acc, options)
|
||||||
|
|
||||||
|
{text_mentions <> text_rest, MapSet.to_list(mentions), MapSet.to_list(tags)}
|
||||||
|
else
|
||||||
|
acc = %{mentions: MapSet.new(), tags: MapSet.new()}
|
||||||
|
{text, %{mentions: mentions, tags: tags}} = AutoLinker.link_map(text, acc, options)
|
||||||
|
|
||||||
|
{text, MapSet.to_list(mentions), MapSet.to_list(tags)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Escapes a special characters in mention names.
|
||||||
|
"""
|
||||||
|
def mentions_escape(text, options \\ []) do
|
||||||
|
options =
|
||||||
|
Keyword.merge(options,
|
||||||
|
mention: true,
|
||||||
|
url: false,
|
||||||
|
mention_handler: &Pleroma.Formatter.escape_mention_handler/4
|
||||||
|
)
|
||||||
|
|
||||||
|
if options[:safe_mention] && Regex.named_captures(@safe_mention_regex, text) do
|
||||||
|
%{"mentions" => mentions, "rest" => rest} = Regex.named_captures(@safe_mention_regex, text)
|
||||||
|
AutoLinker.link(mentions, options) <> AutoLinker.link(rest, options)
|
||||||
|
else
|
||||||
|
AutoLinker.link(text, options)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def emojify(text) do
|
def emojify(text) do
|
||||||
|
@ -35,34 +106,40 @@ def emojify(text) do
|
||||||
|
|
||||||
def emojify(text, nil), do: text
|
def emojify(text, nil), do: text
|
||||||
|
|
||||||
def emojify(text, emoji) do
|
def emojify(text, emoji, strip \\ false) do
|
||||||
Enum.reduce(emoji, text, fn {emoji, file}, text ->
|
Enum.reduce(emoji, text, fn emoji_data, text ->
|
||||||
emoji = HTML.strip_tags(emoji)
|
emoji = HTML.strip_tags(elem(emoji_data, 0))
|
||||||
file = HTML.strip_tags(file)
|
file = HTML.strip_tags(elem(emoji_data, 1))
|
||||||
|
|
||||||
String.replace(
|
html =
|
||||||
text,
|
if not strip do
|
||||||
":#{emoji}:",
|
|
||||||
"<img height='32px' width='32px' alt='#{emoji}' title='#{emoji}' src='#{
|
"<img height='32px' width='32px' alt='#{emoji}' title='#{emoji}' src='#{
|
||||||
MediaProxy.url(file)
|
MediaProxy.url(file)
|
||||||
}' />"
|
}' />"
|
||||||
)
|
else
|
||||||
|> HTML.filter_tags()
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
String.replace(text, ":#{emoji}:", html) |> HTML.filter_tags()
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def demojify(text) do
|
||||||
|
emojify(text, Emoji.get_all(), true)
|
||||||
|
end
|
||||||
|
|
||||||
|
def demojify(text, nil), do: text
|
||||||
|
|
||||||
def get_emoji(text) when is_binary(text) do
|
def get_emoji(text) when is_binary(text) do
|
||||||
Enum.filter(Emoji.get_all(), fn {emoji, _} -> String.contains?(text, ":#{emoji}:") end)
|
Enum.filter(Emoji.get_all(), fn {emoji, _, _} -> String.contains?(text, ":#{emoji}:") end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def get_emoji(_), do: []
|
def get_emoji(_), do: []
|
||||||
|
|
||||||
@link_regex ~r/[0-9a-z+\-\.]+:[0-9a-z$-_.+!*'(),]+/ui
|
def html_escape({text, mentions, hashtags}, type) do
|
||||||
|
{html_escape(text, type), mentions, hashtags}
|
||||||
|
end
|
||||||
|
|
||||||
@uri_schemes Application.get_env(:pleroma, :uri_schemes, [])
|
|
||||||
@valid_schemes Keyword.get(@uri_schemes, :valid_schemes, [])
|
|
||||||
|
|
||||||
# TODO: make it use something other than @link_regex
|
|
||||||
def html_escape(text, "text/html") do
|
def html_escape(text, "text/html") do
|
||||||
HTML.filter_tags(text)
|
HTML.filter_tags(text)
|
||||||
end
|
end
|
||||||
|
@ -76,87 +153,21 @@ def html_escape(text, "text/plain") do
|
||||||
|> Enum.join("")
|
|> Enum.join("")
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc "changes scheme:... urls to html links"
|
def truncate(text, max_length \\ 200, omission \\ "...") do
|
||||||
def add_links({subs, text}) do
|
# Remove trailing whitespace
|
||||||
links =
|
text = Regex.replace(~r/([^ \t\r\n])([ \t]+$)/u, text, "\\g{1}")
|
||||||
|
|
||||||
|
if String.length(text) < max_length do
|
||||||
text
|
text
|
||||||
|> String.split([" ", "\t", "<br>"])
|
|
||||||
|> Enum.filter(fn word -> String.starts_with?(word, @valid_schemes) end)
|
|
||||||
|> Enum.filter(fn word -> Regex.match?(@link_regex, word) end)
|
|
||||||
|> Enum.map(fn url -> {Ecto.UUID.generate(), url} end)
|
|
||||||
|> Enum.sort_by(fn {_, url} -> -String.length(url) end)
|
|
||||||
|
|
||||||
uuid_text =
|
|
||||||
links
|
|
||||||
|> Enum.reduce(text, fn {uuid, url}, acc -> String.replace(acc, url, uuid) end)
|
|
||||||
|
|
||||||
subs =
|
|
||||||
subs ++
|
|
||||||
Enum.map(links, fn {uuid, url} ->
|
|
||||||
{uuid, "<a href=\"#{url}\">#{url}</a>"}
|
|
||||||
end)
|
|
||||||
|
|
||||||
{subs, uuid_text}
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Adds the links to mentioned users"
|
|
||||||
def add_user_links({subs, text}, mentions) do
|
|
||||||
mentions =
|
|
||||||
mentions
|
|
||||||
|> Enum.sort_by(fn {name, _} -> -String.length(name) end)
|
|
||||||
|> Enum.map(fn {name, user} -> {name, user, Ecto.UUID.generate()} end)
|
|
||||||
|
|
||||||
uuid_text =
|
|
||||||
mentions
|
|
||||||
|> Enum.reduce(text, fn {match, _user, uuid}, text ->
|
|
||||||
String.replace(text, match, uuid)
|
|
||||||
end)
|
|
||||||
|
|
||||||
subs =
|
|
||||||
subs ++
|
|
||||||
Enum.map(mentions, fn {match, %User{ap_id: ap_id, info: info}, uuid} ->
|
|
||||||
ap_id =
|
|
||||||
if is_binary(info.source_data["url"]) do
|
|
||||||
info.source_data["url"]
|
|
||||||
else
|
else
|
||||||
ap_id
|
length_with_omission = max_length - String.length(omission)
|
||||||
|
String.slice(text, 0, length_with_omission) <> omission
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
short_match = String.split(match, "@") |> tl() |> hd()
|
defp get_ap_id(%User{info: %{source_data: %{"url" => url}}}) when is_binary(url), do: url
|
||||||
|
defp get_ap_id(%User{ap_id: ap_id}), do: ap_id
|
||||||
|
|
||||||
{uuid,
|
defp get_nickname_text(nickname, %{mentions_format: :full}), do: User.full_nickname(nickname)
|
||||||
"<span><a class='mention' href='#{ap_id}'>@<span>#{short_match}</span></a></span>"}
|
defp get_nickname_text(nickname, _), do: User.local_nickname(nickname)
|
||||||
end)
|
|
||||||
|
|
||||||
{subs, uuid_text}
|
|
||||||
end
|
|
||||||
|
|
||||||
@doc "Adds the hashtag links"
|
|
||||||
def add_hashtag_links({subs, text}, tags) do
|
|
||||||
tags =
|
|
||||||
tags
|
|
||||||
|> Enum.sort_by(fn {name, _} -> -String.length(name) end)
|
|
||||||
|> Enum.map(fn {name, short} -> {name, short, Ecto.UUID.generate()} end)
|
|
||||||
|
|
||||||
uuid_text =
|
|
||||||
tags
|
|
||||||
|> Enum.reduce(text, fn {match, _short, uuid}, text ->
|
|
||||||
String.replace(text, match, uuid)
|
|
||||||
end)
|
|
||||||
|
|
||||||
subs =
|
|
||||||
subs ++
|
|
||||||
Enum.map(tags, fn {tag_text, tag, uuid} ->
|
|
||||||
url = "<a href='#{Pleroma.Web.base_url()}/tag/#{tag}' rel='tag'>#{tag_text}</a>"
|
|
||||||
{uuid, url}
|
|
||||||
end)
|
|
||||||
|
|
||||||
{subs, uuid_text}
|
|
||||||
end
|
|
||||||
|
|
||||||
def finalize({subs, text}) do
|
|
||||||
Enum.reduce(subs, text, fn {uuid, replacement}, result_text ->
|
|
||||||
String.replace(result_text, uuid, replacement)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.Gopher.Server do
|
defmodule Pleroma.Gopher.Server do
|
||||||
use GenServer
|
use GenServer
|
||||||
require Logger
|
require Logger
|
||||||
|
|
||||||
def start_link() do
|
def start_link do
|
||||||
config = Pleroma.Config.get(:gopher, [])
|
config = Pleroma.Config.get(:gopher, [])
|
||||||
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
|
ip = Keyword.get(config, :ip, {0, 0, 0, 0})
|
||||||
port = Keyword.get(config, :port, 1234)
|
port = Keyword.get(config, :port, 1234)
|
||||||
|
@ -22,7 +26,7 @@ def init([ip, port]) do
|
||||||
:gopher,
|
:gopher,
|
||||||
100,
|
100,
|
||||||
:ranch_tcp,
|
:ranch_tcp,
|
||||||
[port: port],
|
[ip: ip, port: port],
|
||||||
__MODULE__.ProtocolHandler,
|
__MODULE__.ProtocolHandler,
|
||||||
[]
|
[]
|
||||||
)
|
)
|
||||||
|
@ -32,19 +36,19 @@ def init([ip, port]) do
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
defmodule Pleroma.Gopher.Server.ProtocolHandler do
|
||||||
alias Pleroma.Web.ActivityPub.ActivityPub
|
|
||||||
alias Pleroma.User
|
|
||||||
alias Pleroma.Activity
|
alias Pleroma.Activity
|
||||||
alias Pleroma.Repo
|
|
||||||
alias Pleroma.HTML
|
alias Pleroma.HTML
|
||||||
alias Pleroma.Object
|
alias Pleroma.Object
|
||||||
|
alias Pleroma.User
|
||||||
|
alias Pleroma.Web.ActivityPub.ActivityPub
|
||||||
|
alias Pleroma.Web.ActivityPub.Visibility
|
||||||
|
|
||||||
def start_link(ref, socket, transport, opts) do
|
def start_link(ref, socket, transport, opts) do
|
||||||
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
|
pid = spawn_link(__MODULE__, :init, [ref, socket, transport, opts])
|
||||||
{:ok, pid}
|
{:ok, pid}
|
||||||
end
|
end
|
||||||
|
|
||||||
def init(ref, socket, transport, _Opts = []) do
|
def init(ref, socket, transport, [] = _Opts) do
|
||||||
:ok = :ranch.accept_ack(ref)
|
:ok = :ranch.accept_ack(ref)
|
||||||
loop(socket, transport)
|
loop(socket, transport)
|
||||||
end
|
end
|
||||||
|
@ -62,7 +66,8 @@ def info(text) do
|
||||||
def link(name, selector, type \\ 1) do
|
def link(name, selector, type \\ 1) do
|
||||||
address = Pleroma.Web.Endpoint.host()
|
address = Pleroma.Web.Endpoint.host()
|
||||||
port = Pleroma.Config.get([:gopher, :port], 1234)
|
port = Pleroma.Config.get([:gopher, :port], 1234)
|
||||||
"#{type}#{name}\t#{selector}\t#{address}\t#{port}\r\n"
|
dstport = Pleroma.Config.get([:gopher, :dstport], port)
|
||||||
|
"#{type}#{name}\t#{selector}\t#{address}\t#{dstport}\r\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
def render_activities(activities) do
|
def render_activities(activities) do
|
||||||
|
@ -106,8 +111,8 @@ def response("/main/all") do
|
||||||
end
|
end
|
||||||
|
|
||||||
def response("/notices/" <> id) do
|
def response("/notices/" <> id) do
|
||||||
with %Activity{} = activity <- Repo.get(Activity, id),
|
with %Activity{} = activity <- Activity.get_by_id(id),
|
||||||
true <- ActivityPub.is_public?(activity) do
|
true <- Visibility.is_public?(activity) do
|
||||||
activities =
|
activities =
|
||||||
ActivityPub.fetch_activities_for_context(activity.data["context"])
|
ActivityPub.fetch_activities_for_context(activity.data["context"])
|
||||||
|> render_activities
|
|> render_activities
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
defmodule Pleroma.HTML do
|
defmodule Pleroma.HTML do
|
||||||
alias HtmlSanitizeEx.Scrubber
|
alias HtmlSanitizeEx.Scrubber
|
||||||
|
|
||||||
|
@ -5,26 +9,83 @@ defp get_scrubbers(scrubber) when is_atom(scrubber), do: [scrubber]
|
||||||
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
|
defp get_scrubbers(scrubbers) when is_list(scrubbers), do: scrubbers
|
||||||
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
|
defp get_scrubbers(_), do: [Pleroma.HTML.Scrubber.Default]
|
||||||
|
|
||||||
def get_scrubbers() do
|
def get_scrubbers do
|
||||||
Pleroma.Config.get([:markup, :scrub_policy])
|
Pleroma.Config.get([:markup, :scrub_policy])
|
||||||
|> get_scrubbers
|
|> get_scrubbers
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_tags(html, nil) do
|
def filter_tags(html, nil) do
|
||||||
get_scrubbers()
|
filter_tags(html, get_scrubbers())
|
||||||
|> Enum.reduce(html, fn scrubber, html ->
|
end
|
||||||
|
|
||||||
|
def filter_tags(html, scrubbers) when is_list(scrubbers) do
|
||||||
|
Enum.reduce(scrubbers, html, fn scrubber, html ->
|
||||||
filter_tags(html, scrubber)
|
filter_tags(html, scrubber)
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_tags(html, scrubber) do
|
def filter_tags(html, scrubber), do: Scrubber.scrub(html, scrubber)
|
||||||
html |> Scrubber.scrub(scrubber)
|
def filter_tags(html), do: filter_tags(html, nil)
|
||||||
|
def strip_tags(html), do: Scrubber.scrub(html, Scrubber.StripTags)
|
||||||
|
|
||||||
|
def get_cached_scrubbed_html_for_activity(content, scrubbers, activity, key \\ "") do
|
||||||
|
key = "#{key}#{generate_scrubber_signature(scrubbers)}|#{activity.id}"
|
||||||
|
|
||||||
|
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||||
|
object = Pleroma.Object.normalize(activity)
|
||||||
|
ensure_scrubbed_html(content, scrubbers, object.data["fake"] || false)
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_tags(html), do: filter_tags(html, nil)
|
def get_cached_stripped_html_for_activity(content, activity, key) do
|
||||||
|
get_cached_scrubbed_html_for_activity(
|
||||||
|
content,
|
||||||
|
HtmlSanitizeEx.Scrubber.StripTags,
|
||||||
|
activity,
|
||||||
|
key
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
def strip_tags(html) do
|
def ensure_scrubbed_html(
|
||||||
html |> Scrubber.scrub(Scrubber.StripTags)
|
content,
|
||||||
|
scrubbers,
|
||||||
|
false = _fake
|
||||||
|
) do
|
||||||
|
{:commit, filter_tags(content, scrubbers)}
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_scrubbed_html(
|
||||||
|
content,
|
||||||
|
scrubbers,
|
||||||
|
true = _fake
|
||||||
|
) do
|
||||||
|
{:ignore, filter_tags(content, scrubbers)}
|
||||||
|
end
|
||||||
|
|
||||||
|
defp generate_scrubber_signature(scrubber) when is_atom(scrubber) do
|
||||||
|
generate_scrubber_signature([scrubber])
|
||||||
|
end
|
||||||
|
|
||||||
|
defp generate_scrubber_signature(scrubbers) do
|
||||||
|
Enum.reduce(scrubbers, "", fn scrubber, signature ->
|
||||||
|
"#{signature}#{to_string(scrubber)}"
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_first_external_url(_, nil), do: {:error, "No content"}
|
||||||
|
|
||||||
|
def extract_first_external_url(object, content) do
|
||||||
|
key = "URL|#{object.id}"
|
||||||
|
|
||||||
|
Cachex.fetch!(:scrubber_cache, key, fn _key ->
|
||||||
|
result =
|
||||||
|
content
|
||||||
|
|> Floki.filter_out("a.mention")
|
||||||
|
|> Floki.attribute("a", "href")
|
||||||
|
|> Enum.at(0)
|
||||||
|
|
||||||
|
{:commit, {:ok, result}}
|
||||||
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -35,8 +96,7 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@markup Application.get_env(:pleroma, :markup)
|
@markup Application.get_env(:pleroma, :markup)
|
||||||
@uri_schemes Application.get_env(:pleroma, :uri_schemes, [])
|
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||||
@valid_schemes Keyword.get(@uri_schemes, :valid_schemes, [])
|
|
||||||
|
|
||||||
require HtmlSanitizeEx.Scrubber.Meta
|
require HtmlSanitizeEx.Scrubber.Meta
|
||||||
alias HtmlSanitizeEx.Scrubber.Meta
|
alias HtmlSanitizeEx.Scrubber.Meta
|
||||||
|
@ -45,15 +105,22 @@ defmodule Pleroma.HTML.Scrubber.TwitterText do
|
||||||
Meta.strip_comments()
|
Meta.strip_comments()
|
||||||
|
|
||||||
# links
|
# links
|
||||||
Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
|
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||||
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
||||||
|
|
||||||
|
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
||||||
|
"tag",
|
||||||
|
"nofollow",
|
||||||
|
"noopener",
|
||||||
|
"noreferrer"
|
||||||
|
])
|
||||||
|
|
||||||
# paragraphs and linebreaks
|
# paragraphs and linebreaks
|
||||||
Meta.allow_tag_with_these_attributes("br", [])
|
Meta.allow_tag_with_these_attributes("br", [])
|
||||||
Meta.allow_tag_with_these_attributes("p", [])
|
Meta.allow_tag_with_these_attributes("p", [])
|
||||||
|
|
||||||
# microformats
|
# microformats
|
||||||
Meta.allow_tag_with_these_attributes("span", [])
|
Meta.allow_tag_with_these_attributes("span", ["class"])
|
||||||
|
|
||||||
# allow inline images for custom emoji
|
# allow inline images for custom emoji
|
||||||
@allow_inline_images Keyword.get(@markup, :allow_inline_images)
|
@allow_inline_images Keyword.get(@markup, :allow_inline_images)
|
||||||
|
@ -78,16 +145,24 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
|
|
||||||
require HtmlSanitizeEx.Scrubber.Meta
|
require HtmlSanitizeEx.Scrubber.Meta
|
||||||
alias HtmlSanitizeEx.Scrubber.Meta
|
alias HtmlSanitizeEx.Scrubber.Meta
|
||||||
|
# credo:disable-for-previous-line
|
||||||
|
# No idea how to fix this one…
|
||||||
|
|
||||||
@markup Application.get_env(:pleroma, :markup)
|
@markup Application.get_env(:pleroma, :markup)
|
||||||
@uri_schemes Application.get_env(:pleroma, :uri_schemes, [])
|
@valid_schemes Pleroma.Config.get([:uri_schemes, :valid_schemes], [])
|
||||||
@valid_schemes Keyword.get(@uri_schemes, :valid_schemes, [])
|
|
||||||
|
|
||||||
Meta.remove_cdata_sections_before_scrub()
|
Meta.remove_cdata_sections_before_scrub()
|
||||||
Meta.strip_comments()
|
Meta.strip_comments()
|
||||||
|
|
||||||
Meta.allow_tag_with_uri_attributes("a", ["href"], @valid_schemes)
|
Meta.allow_tag_with_uri_attributes("a", ["href", "data-user", "data-tag"], @valid_schemes)
|
||||||
Meta.allow_tag_with_these_attributes("a", ["name", "title"])
|
Meta.allow_tag_with_these_attributes("a", ["name", "title", "class"])
|
||||||
|
|
||||||
|
Meta.allow_tag_with_this_attribute_values("a", "rel", [
|
||||||
|
"tag",
|
||||||
|
"nofollow",
|
||||||
|
"noopener",
|
||||||
|
"noreferrer"
|
||||||
|
])
|
||||||
|
|
||||||
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
Meta.allow_tag_with_these_attributes("abbr", ["title"])
|
||||||
|
|
||||||
|
@ -102,7 +177,7 @@ defmodule Pleroma.HTML.Scrubber.Default do
|
||||||
Meta.allow_tag_with_these_attributes("ol", [])
|
Meta.allow_tag_with_these_attributes("ol", [])
|
||||||
Meta.allow_tag_with_these_attributes("p", [])
|
Meta.allow_tag_with_these_attributes("p", [])
|
||||||
Meta.allow_tag_with_these_attributes("pre", [])
|
Meta.allow_tag_with_these_attributes("pre", [])
|
||||||
Meta.allow_tag_with_these_attributes("span", [])
|
Meta.allow_tag_with_these_attributes("span", ["class"])
|
||||||
Meta.allow_tag_with_these_attributes("strong", [])
|
Meta.allow_tag_with_these_attributes("strong", [])
|
||||||
Meta.allow_tag_with_these_attributes("u", [])
|
Meta.allow_tag_with_these_attributes("u", [])
|
||||||
Meta.allow_tag_with_these_attributes("ul", [])
|
Meta.allow_tag_with_these_attributes("ul", [])
|
||||||
|
@ -166,7 +241,7 @@ def scrub_attribute("img", {"src", "http" <> target}) do
|
||||||
{"src", media_url}
|
{"src", media_url}
|
||||||
end
|
end
|
||||||
|
|
||||||
def scrub_attribute(tag, attribute), do: attribute
|
def scrub_attribute(_tag, attribute), do: attribute
|
||||||
|
|
||||||
def scrub({"img", attributes, children}) do
|
def scrub({"img", attributes, children}) do
|
||||||
attributes =
|
attributes =
|
||||||
|
@ -177,9 +252,9 @@ def scrub({"img", attributes, children}) do
|
||||||
{"img", attributes, children}
|
{"img", attributes, children}
|
||||||
end
|
end
|
||||||
|
|
||||||
def scrub({:comment, children}), do: ""
|
def scrub({:comment, _children}), do: ""
|
||||||
|
|
||||||
def scrub({tag, attributes, children}), do: {tag, attributes, children}
|
def scrub({tag, attributes, children}), do: {tag, attributes, children}
|
||||||
def scrub({tag, children}), do: children
|
def scrub({_tag, children}), do: children
|
||||||
def scrub(text), do: text
|
def scrub(text), do: text
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.HTTP.Connection do
|
||||||
|
@moduledoc """
|
||||||
|
Connection for http-requests.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@hackney_options [
|
||||||
|
connect_timeout: 2_000,
|
||||||
|
recv_timeout: 20_000,
|
||||||
|
follow_redirect: true,
|
||||||
|
pool: :federation
|
||||||
|
]
|
||||||
|
@adapter Application.get_env(:tesla, :adapter)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Configure a client connection
|
||||||
|
|
||||||
|
# Returns
|
||||||
|
|
||||||
|
Tesla.Env.client
|
||||||
|
"""
|
||||||
|
@spec new(Keyword.t()) :: Tesla.Env.client()
|
||||||
|
def new(opts \\ []) do
|
||||||
|
Tesla.client([], {@adapter, hackney_options(opts)})
|
||||||
|
end
|
||||||
|
|
||||||
|
# fetch Hackney options
|
||||||
|
#
|
||||||
|
defp hackney_options(opts) do
|
||||||
|
options = Keyword.get(opts, :adapter, [])
|
||||||
|
adapter_options = Pleroma.Config.get([:http, :adapter], [])
|
||||||
|
|
||||||
|
@hackney_options
|
||||||
|
|> Keyword.merge(adapter_options)
|
||||||
|
|> Keyword.merge(options)
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,13 +1,58 @@
|
||||||
defmodule Pleroma.HTTP do
|
# Pleroma: A lightweight social networking server
|
||||||
require HTTPoison
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.HTTP do
|
||||||
|
@moduledoc """
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
alias Pleroma.HTTP.Connection
|
||||||
|
alias Pleroma.HTTP.RequestBuilder, as: Builder
|
||||||
|
|
||||||
|
@type t :: __MODULE__
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Builds and perform http request.
|
||||||
|
|
||||||
|
# Arguments:
|
||||||
|
`method` - :get, :post, :put, :delete
|
||||||
|
`url`
|
||||||
|
`body`
|
||||||
|
`headers` - a keyworld list of headers, e.g. `[{"content-type", "text/plain"}]`
|
||||||
|
`options` - custom, per-request middleware or adapter options
|
||||||
|
|
||||||
|
# Returns:
|
||||||
|
`{:ok, %Tesla.Env{}}` or `{:error, error}`
|
||||||
|
|
||||||
|
"""
|
||||||
def request(method, url, body \\ "", headers \\ [], options \\ []) do
|
def request(method, url, body \\ "", headers \\ [], options \\ []) do
|
||||||
|
try do
|
||||||
options =
|
options =
|
||||||
process_request_options(options)
|
process_request_options(options)
|
||||||
|> process_sni_options(url)
|
|> process_sni_options(url)
|
||||||
|
|
||||||
HTTPoison.request(method, url, body, headers, options)
|
params = Keyword.get(options, :params, [])
|
||||||
|
|
||||||
|
%{}
|
||||||
|
|> Builder.method(method)
|
||||||
|
|> Builder.headers(headers)
|
||||||
|
|> Builder.opts(options)
|
||||||
|
|> Builder.url(url)
|
||||||
|
|> Builder.add_param(:body, :body, body)
|
||||||
|
|> Builder.add_param(:query, :query, params)
|
||||||
|
|> Enum.into([])
|
||||||
|
|> (&Tesla.request(Connection.new(options), &1)).()
|
||||||
|
rescue
|
||||||
|
e ->
|
||||||
|
{:error, e}
|
||||||
|
catch
|
||||||
|
:exit, e ->
|
||||||
|
{:error, e}
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
defp process_sni_options(options, nil), do: options
|
||||||
|
|
||||||
defp process_sni_options(options, url) do
|
defp process_sni_options(options, url) do
|
||||||
uri = URI.parse(url)
|
uri = URI.parse(url)
|
||||||
|
@ -22,7 +67,6 @@ defp process_sni_options(options, url) do
|
||||||
def process_request_options(options) do
|
def process_request_options(options) do
|
||||||
config = Application.get_env(:pleroma, :http, [])
|
config = Application.get_env(:pleroma, :http, [])
|
||||||
proxy = Keyword.get(config, :proxy_url, nil)
|
proxy = Keyword.get(config, :proxy_url, nil)
|
||||||
options = options ++ [hackney: [pool: :default]]
|
|
||||||
|
|
||||||
case proxy do
|
case proxy do
|
||||||
nil -> options
|
nil -> options
|
||||||
|
@ -30,8 +74,19 @@ def process_request_options(options) do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def get(url, headers \\ [], options \\ []), do: request(:get, url, "", headers, options)
|
@doc """
|
||||||
|
Performs GET request.
|
||||||
|
|
||||||
|
See `Pleroma.HTTP.request/5`
|
||||||
|
"""
|
||||||
|
def get(url, headers \\ [], options \\ []),
|
||||||
|
do: request(:get, url, "", headers, options)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Performs POST request.
|
||||||
|
|
||||||
|
See `Pleroma.HTTP.request/5`
|
||||||
|
"""
|
||||||
def post(url, body, headers \\ [], options \\ []),
|
def post(url, body, headers \\ [], options \\ []),
|
||||||
do: request(:post, url, body, headers, options)
|
do: request(:post, url, body, headers, options)
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
# Pleroma: A lightweight social networking server
|
||||||
|
# Copyright © 2017-2019 Pleroma Authors <https://pleroma.social/>
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
defmodule Pleroma.HTTP.RequestBuilder do
|
||||||
|
@moduledoc """
|
||||||
|
Helper functions for building Tesla requests
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Specify the request method when building a request
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
- request (Map) - Collected request options
|
||||||
|
- m (atom) - Request method
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
|
||||||
|
Map
|
||||||
|
"""
|
||||||
|
@spec method(map(), atom) :: map()
|
||||||
|
def method(request, m) do
|
||||||
|
Map.put_new(request, :method, m)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Specify the request method when building a request
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
- request (Map) - Collected request options
|
||||||
|
- u (String) - Request URL
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
|
||||||
|
Map
|
||||||
|
"""
|
||||||
|
@spec url(map(), String.t()) :: map()
|
||||||
|
def url(request, u) do
|
||||||
|
Map.put_new(request, :url, u)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Add headers to the request
|
||||||
|
"""
|
||||||
|
@spec headers(map(), list(tuple)) :: map()
|
||||||
|
def headers(request, h) do
|
||||||
|
Map.put_new(request, :headers, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Add custom, per-request middleware or adapter options to the request
|
||||||
|
"""
|
||||||
|
@spec opts(map(), Keyword.t()) :: map()
|
||||||
|
def opts(request, options) do
|
||||||
|
Map.put_new(request, :opts, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Add optional parameters to the request
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
- request (Map) - Collected request options
|
||||||
|
- definitions (Map) - Map of parameter name to parameter location.
|
||||||
|
- options (KeywordList) - The provided optional parameters
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
|
||||||
|
Map
|
||||||
|
"""
|
||||||
|
@spec add_optional_params(map(), %{optional(atom) => atom}, keyword()) :: map()
|
||||||
|
def add_optional_params(request, _, []), do: request
|
||||||
|
|
||||||
|
def add_optional_params(request, definitions, [{key, value} | tail]) do
|
||||||
|
case definitions do
|
||||||
|
%{^key => location} ->
|
||||||
|
request
|
||||||
|
|> add_param(location, key, value)
|
||||||
|
|> add_optional_params(definitions, tail)
|
||||||
|
|
||||||
|
_ ->
|
||||||
|
add_optional_params(request, definitions, tail)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Add optional parameters to the request
|
||||||
|
|
||||||
|
## Parameters
|
||||||
|
|
||||||
|
- request (Map) - Collected request options
|
||||||
|
- location (atom) - Where to put the parameter
|
||||||
|
- key (atom) - The name of the parameter
|
||||||
|
- value (any) - The value of the parameter
|
||||||
|
|
||||||
|
## Returns
|
||||||
|
|
||||||
|
Map
|
||||||
|
"""
|
||||||
|
@spec add_param(map(), atom, atom, any()) :: map()
|
||||||
|
def add_param(request, :query, :query, values), do: Map.put(request, :query, values)
|
||||||
|
|
||||||
|
def add_param(request, :body, :body, value), do: Map.put(request, :body, value)
|
||||||
|
|
||||||
|
def add_param(request, :body, key, value) do
|
||||||
|
request
|
||||||
|
|> Map.put_new_lazy(:body, &Tesla.Multipart.new/0)
|
||||||
|
|> Map.update!(
|
||||||
|
:body,
|
||||||
|
&Tesla.Multipart.add_field(
|
||||||
|
&1,
|
||||||
|
key,
|
||||||
|
Jason.encode!(value),
|
||||||
|
headers: [{:"Content-Type", "application/json"}]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_param(request, :file, name, path) do
|
||||||
|
request
|
||||||
|
|> Map.put_new_lazy(:body, &Tesla.Multipart.new/0)
|
||||||
|
|> Map.update!(:body, &Tesla.Multipart.add_file(&1, path, name: name))
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_param(request, :form, name, value) do
|
||||||
|
request
|
||||||
|
|> Map.update(:body, %{name => value}, &Map.put(&1, name, value))
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_param(request, location, key, value) do
|
||||||
|
Map.update(request, location, [{key, value}], &(&1 ++ [{key, value}]))
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,36 @@
|
||||||
|
defmodule Pleroma.Instances do
|
||||||
|
@moduledoc "Instances context."
|
||||||
|
|
||||||
|
@adapter Pleroma.Instances.Instance
|
||||||
|
|
||||||
|
defdelegate filter_reachable(urls_or_hosts), to: @adapter
|
||||||
|
defdelegate reachable?(url_or_host), to: @adapter
|
||||||
|
defdelegate set_reachable(url_or_host), to: @adapter
|
||||||
|
defdelegate set_unreachable(url_or_host, unreachable_since \\ nil), to: @adapter
|
||||||
|
|
||||||
|
def set_consistently_unreachable(url_or_host),
|
||||||
|
do: set_unreachable(url_or_host, reachability_datetime_threshold())
|
||||||
|
|
||||||
|
def reachability_datetime_threshold do
|
||||||
|
federation_reachability_timeout_days =
|
||||||
|
Pleroma.Config.get(:instance)[:federation_reachability_timeout_days] || 0
|
||||||
|
|
||||||
|
if federation_reachability_timeout_days > 0 do
|
||||||
|
NaiveDateTime.add(
|
||||||
|
NaiveDateTime.utc_now(),
|
||||||
|
-federation_reachability_timeout_days * 24 * 3600,
|
||||||
|
:second
|
||||||
|
)
|
||||||
|
else
|
||||||
|
~N[0000-01-01 00:00:00]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def host(url_or_host) when is_binary(url_or_host) do
|
||||||
|
if url_or_host =~ ~r/^http/i do
|
||||||
|
URI.parse(url_or_host).host
|
||||||
|
else
|
||||||
|
url_or_host
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,113 @@
|
||||||
|
defmodule Pleroma.Instances.Instance do
|
||||||
|
@moduledoc "Instance."
|
||||||
|
|
||||||
|
alias Pleroma.Instances
|
||||||
|
alias Pleroma.Instances.Instance
|
||||||
|
alias Pleroma.Repo
|
||||||
|
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
import Ecto.Query
|
||||||
|
import Ecto.Changeset
|
||||||
|
|
||||||
|
schema "instances" do
|
||||||
|
field(:host, :string)
|
||||||
|
field(:unreachable_since, :naive_datetime_usec)
|
||||||
|
|
||||||
|
timestamps()
|
||||||
|
end
|
||||||
|
|
||||||
|
defdelegate host(url_or_host), to: Instances
|
||||||
|
|
||||||
|
def changeset(struct, params \\ %{}) do
|
||||||
|
struct
|
||||||
|
|> cast(params, [:host, :unreachable_since])
|
||||||
|
|> validate_required([:host])
|
||||||
|
|> unique_constraint(:host)
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_reachable([]), do: %{}
|
||||||
|
|
||||||
|
def filter_reachable(urls_or_hosts) when is_list(urls_or_hosts) do
|
||||||
|
hosts =
|
||||||
|
urls_or_hosts
|
||||||
|
|> Enum.map(&(&1 && host(&1)))
|
||||||
|
|> Enum.filter(&(to_string(&1) != ""))
|
||||||
|
|
||||||
|
unreachable_since_by_host =
|
||||||
|
Repo.all(
|
||||||
|
from(i in Instance,
|
||||||
|
where: i.host in ^hosts,
|
||||||
|
select: {i.host, i.unreachable_since}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Map.new(& &1)
|
||||||
|
|
||||||
|
reachability_datetime_threshold = Instances.reachability_datetime_threshold()
|
||||||
|
|
||||||
|
for entry <- Enum.filter(urls_or_hosts, &is_binary/1) do
|
||||||
|
host = host(entry)
|
||||||
|
unreachable_since = unreachable_since_by_host[host]
|
||||||
|
|
||||||
|
if !unreachable_since ||
|
||||||
|
NaiveDateTime.compare(unreachable_since, reachability_datetime_threshold) == :gt do
|
||||||
|
{entry, unreachable_since}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|> Enum.filter(& &1)
|
||||||
|
|> Map.new(& &1)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reachable?(url_or_host) when is_binary(url_or_host) do
|
||||||
|
!Repo.one(
|
||||||
|
from(i in Instance,
|
||||||
|
where:
|
||||||
|
i.host == ^host(url_or_host) and
|
||||||
|
i.unreachable_since <= ^Instances.reachability_datetime_threshold(),
|
||||||
|
select: true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def reachable?(_), do: true
|
||||||
|
|
||||||
|
def set_reachable(url_or_host) when is_binary(url_or_host) do
|
||||||
|
with host <- host(url_or_host),
|
||||||
|
%Instance{} = existing_record <- Repo.get_by(Instance, %{host: host}) do
|
||||||
|
{:ok, _instance} =
|
||||||
|
existing_record
|
||||||
|
|> changeset(%{unreachable_since: nil})
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_reachable(_), do: {:error, nil}
|
||||||
|
|
||||||
|
def set_unreachable(url_or_host, unreachable_since \\ nil)
|
||||||
|
|
||||||
|
def set_unreachable(url_or_host, unreachable_since) when is_binary(url_or_host) do
|
||||||
|
unreachable_since = unreachable_since || DateTime.utc_now()
|
||||||
|
host = host(url_or_host)
|
||||||
|
existing_record = Repo.get_by(Instance, %{host: host})
|
||||||
|
|
||||||
|
changes = %{unreachable_since: unreachable_since}
|
||||||
|
|
||||||
|
cond do
|
||||||
|
is_nil(existing_record) ->
|
||||||
|
%Instance{}
|
||||||
|
|> changeset(Map.put(changes, :host, host))
|
||||||
|
|> Repo.insert()
|
||||||
|
|
||||||
|
existing_record.unreachable_since &&
|
||||||
|
NaiveDateTime.compare(existing_record.unreachable_since, unreachable_since) != :gt ->
|
||||||
|
{:ok, existing_record}
|
||||||
|
|
||||||
|
true ->
|
||||||
|
existing_record
|
||||||
|
|> changeset(changes)
|
||||||
|
|> Repo.update()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_unreachable(_, _), do: {:error, nil}
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue