From 9e3df495dc61c090d0e758f9e18bf7f4b00630af Mon Sep 17 00:00:00 2001 From: Thomas Citharel Date: Thu, 30 Jan 2020 20:27:25 +0100 Subject: [PATCH] Move configuration to traditional way MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ⭐️ This is the way. Signed-off-by: Thomas Citharel --- .env.sample | 22 ----- .gitignore | 7 +- .gitlab-ci.yml | 4 +- CHANGELOG.md | 15 +++ Makefile | 2 +- config/config.exs | 85 ++++++++++------- config/dev.exs | 39 ++++++-- config/prod.exs | 92 ++++--------------- config/test.exs | 14 ++- docker-compose.yml | 1 + .../CLI tasks/maintenance_ tasks.md | 33 +------ .../administration/CLI tasks/manage_actors.md | 2 +- docs/administration/CLI tasks/manage_users.md | 2 +- docs/administration/CLI tasks/relay.md | 2 +- docs/administration/configure/email.md | 31 +++++++ docs/administration/configure/geocoders.md | 74 +++++++++++++-- docs/administration/docker.md | 15 --- docs/administration/index.md | 46 ++++++++-- docs/administration/upgrading.md | 6 +- docs/contribute/development.md | 25 +++-- docs/contribute/styleguide.md | 2 +- js/.gitignore | 4 - lib/federation/activity_pub/activity_pub.ex | 11 ++- lib/federation/activity_pub/transmogrifier.ex | 4 +- lib/graphql/api/follows.ex | 9 +- lib/mix/tasks/mobilizon/instance.ex | 39 +++++--- lib/mobilizon/addresses/addresses.ex | 2 +- mix.exs | 1 + mkdocs.yml | 2 + priv/templates/config.template.eex | 31 +++++++ .../templates/setup_db.eex | 0 support/systemd/mobilizon.service | 2 +- 32 files changed, 368 insertions(+), 256 deletions(-) delete mode 100644 .env.sample create mode 100644 docs/administration/configure/email.md delete mode 100644 docs/administration/docker.md create mode 100644 priv/templates/config.template.eex rename support/postgresql/setup_db.psql => priv/templates/setup_db.eex (100%) diff --git a/.env.sample b/.env.sample deleted file mode 100644 index 91aab29c3..000000000 --- a/.env.sample +++ /dev/null @@ -1,22 +0,0 @@ -# Settings -MOBILIZON_INSTANCE_NAME="<%= instance_name %>" -MOBILIZON_INSTANCE_HOST="<%= instance_domain %>" -MOBILIZON_INSTANCE_PORT=4002 -MOBILIZON_INSTANCE_EMAIL="<%= instance_email %>" -MOBILIZON_INSTANCE_REGISTRATIONS_OPEN=true - -# API -GRAPHQL_API_ENDPOINT="https://<%= instance_domain %>" -GRAPHQL_API_FULL_PATH="" - -# APP -MIX_ENV=prod -MOBILIZON_LOGLEVEL="info" -MOBILIZON_SECRET="<%= instance_secret %>" - -# Database -MOBILIZON_DATABASE_USERNAME="<%= database_username %>" -MOBILIZON_DATABASE_PASSWORD="<%= database_password %>" -MOBILIZON_DATABASE_DBNAME="<%= database_name %>" -MOBILIZON_DATABASE_HOST="<%= database_host %>" -MOBILIZON_DATABASE_PORT=<%= database_port %> diff --git a/.gitignore b/.gitignore index 4c08c9957..4d3a805ed 100644 --- a/.gitignore +++ b/.gitignore @@ -15,12 +15,6 @@ erl_crash.dump # variables. /config/*.secret.exs -.env.production -.env.test -/.env -.env.2 -.env.1 - /setup_db.psql .elixir_ls @@ -35,6 +29,7 @@ site/ test/fixtures/image_tmp.jpg test/uploads/ uploads/* +release/ !uploads/.gitkeep .idea *.mo diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 929abbf14..e4422047d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -24,7 +24,7 @@ cache: key: ${CI_COMMIT_REF_SLUG} paths: - ~/.cache/Cypress - - build/ + - _build/ - deps/ - js/node_modules - cache/Cypress @@ -34,7 +34,7 @@ lint: script: - export EXITVALUE=0 - mix deps.get - - mix credo -a || export EXITVALUE=1 + - mix credo --strict -a || export EXITVALUE=1 - mix format --check-formatted --dry-run || export EXITVALUE=1 - cd js - yarn install diff --git a/CHANGELOG.md b/CHANGELOG.md index 013599eeb..7ae55ec7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,21 @@ 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/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Special operations +Config has moved from `.env` files to a more traditional way to handle things in the Elixir world, with `.exs` files. + +To migrate existing configuration, you can simply run `mix mobilizon.instance gen` and fill in the adequate values previously in `.env` files (you don't need to perform the operations to create the database). + +A minimal file template [is available](https://framagit.org/framasoft/mobilizon/blob/master/priv/templates/config.template.eex) to check for missing configuration. + +Also make sure to remove the `EnvironmentFile=` line from the systemd service and set `Environment=MIX_ENV=prod` instead. See [the updated file](https://framagit.org/framasoft/mobilizon/blob/master/support/systemd/mobilizon.service). + +### Added +- Possibility to participate anonymously to an event +- Possibility to participate to a remote event (being redirected by providing federated identity) + ## [1.0.0-beta.2] - 2019-12-18 ### Special operations diff --git a/Makefile b/Makefile index 7f7cda3c9..c939bfdc6 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ init: start: stop @bash docker/message.sh "starting Mobilizon with docker" docker-compose up -d api - @bash docker/message.sh "started" + @bash docker/message.sh "Docker server started." stop: @bash docker/message.sh "stopping Mobilizon" docker-compose down diff --git a/config/config.exs b/config/config.exs index f12d6dc61..1065721ec 100644 --- a/config/config.exs +++ b/config/config.exs @@ -10,15 +10,15 @@ config :mobilizon, ecto_repos: [Mobilizon.Storage.Repo], env: Mix.env() +config :mobilizon, Mobilizon.Storage.Repo, types: Mobilizon.Storage.PostgresTypes + config :mobilizon, :instance, - name: System.get_env("MOBILIZON_INSTANCE_NAME") || "My Mobilizon Instance", - description: - System.get_env("MOBILIZON_INSTANCE_DESCRIPTION") || - "Change this to a proper description of your instance", - hostname: System.get_env("MOBILIZON_INSTANCE_HOST") || "localhost", - registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN") || false, + name: "My Mobilizon Instance", + description: "Change this to a proper description of your instance", + hostname: "localhost", + registrations_open: false, registration_email_whitelist: [], - demo: System.get_env("MOBILIZON_INSTANCE_DEMO_MODE") || false, + demo: false, repository: Mix.Project.config()[:source_url], allow_relay: true, # Federation is to be activated with Mobilizon 1.0.0-beta.2 @@ -27,8 +27,8 @@ config :mobilizon, :instance, upload_limit: 10_000_000, avatar_upload_limit: 2_000_000, banner_upload_limit: 4_000_000, - email_from: System.get_env("MOBILIZON_INSTANCE_EMAIL") || "noreply@localhost", - email_reply_to: System.get_env("MOBILIZON_INSTANCE_EMAIL") || "noreply@localhost" + email_from: "noreply@localhost", + email_reply_to: "noreply@localhost" config :mime, :types, %{ "application/activity+json" => ["activity-json"], @@ -37,10 +37,17 @@ config :mime, :types, %{ # Configures the endpoint config :mobilizon, Mobilizon.Web.Endpoint, - url: [host: "localhost"], + http: [ + transport_options: [socket_opts: [:inet6]] + ], + url: [ + host: "mobilizon.local", + scheme: "https" + ], secret_key_base: "1yOazsoE0Wqu4kXk3uC5gu3jDbShOimTCzyFL3OjCdBmOXMyHX87Qmf3+Tu9s0iM", render_errors: [view: Mobilizon.Web.ErrorView, accepts: ~w(html json)], - pubsub: [name: Mobilizon.PubSub, adapter: Phoenix.PubSub.PG2] + pubsub: [name: Mobilizon.PubSub, adapter: Phoenix.PubSub.PG2], + cache_static_manifest: "priv/static/manifest.json" # Upload configuration config :mobilizon, Mobilizon.Web.Upload, @@ -73,14 +80,31 @@ config :mobilizon, :media_proxy, ] ] +config :mobilizon, Mobilizon.Web.Email.Mailer, + adapter: Bamboo.SMTPAdapter, + server: "localhost", + hostname: "localhost", + port: 25, + # or {:system, "SMTP_USERNAME"} + username: nil, + # or {:system, "SMTP_PASSWORD"} + password: nil, + # can be `:always` or `:never` + tls: :if_available, + # or {":system", ALLOWED_TLS_VERSIONS"} w/ comma seprated values (e.g. "tlsv1.1,tlsv1.2") + allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], + # can be `true` + ssl: false, + retries: 1, + # can be `true` + no_mx_lookups: false + # Configures Elixir's Logger config :logger, :console, format: "$time $metadata[$level] $message\n", metadata: [:request_id] -config :mobilizon, Mobilizon.Web.Auth.Guardian, - issuer: "mobilizon", - secret_key: "ty0WM7YBE3ojvxoUQxo8AERrNpfbXnIJ82ovkPdqbUFw31T5LcK8wGjaOiReVQjo" +config :mobilizon, Mobilizon.Web.Auth.Guardian, issuer: "mobilizon" config :guardian, Guardian.DB, repo: Mobilizon.Storage.Repo, @@ -96,7 +120,7 @@ config :geolix, %{ id: :city, adapter: Geolix.Adapter.MMDB2, - source: System.get_env("GEOLITE_CITIES_PATH") || "priv/data/GeoLite2-City.mmdb" + source: "priv/data/GeoLite2-City.mmdb" } ] @@ -124,36 +148,31 @@ config :http_signatures, config :mobilizon, :activitypub, sign_object_fetches: true +config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Nominatim + config :mobilizon, Mobilizon.Service.Geospatial.Nominatim, - endpoint: - System.get_env("GEOSPATIAL_NOMINATIM_ENDPOINT") || "https://nominatim.openstreetmap.org", - api_key: System.get_env("GEOSPATIAL_NOMINATIM_API_KEY") || nil + endpoint: "https://nominatim.openstreetmap.org", + api_key: nil config :mobilizon, Mobilizon.Service.Geospatial.Addok, - endpoint: System.get_env("GEOSPATIAL_ADDOK_ENDPOINT") || "https://api-adresse.data.gouv.fr" + endpoint: "https://api-adresse.data.gouv.fr" -config :mobilizon, Mobilizon.Service.Geospatial.Photon, - endpoint: System.get_env("GEOSPATIAL_PHOTON_ENDPOINT") || "https://photon.komoot.de" +config :mobilizon, Mobilizon.Service.Geospatial.Photon, endpoint: "https://photon.komoot.de" config :mobilizon, Mobilizon.Service.Geospatial.GoogleMaps, - api_key: System.get_env("GEOSPATIAL_GOOGLE_MAPS_API_KEY") || nil, - fetch_place_details: System.get_env("GEOSPATIAL_GOOGLE_MAPS_FETCH_PLACE_DETAILS") || true + api_key: nil, + fetch_place_details: true -config :mobilizon, Mobilizon.Service.Geospatial.MapQuest, - api_key: System.get_env("GEOSPATIAL_MAP_QUEST_API_KEY") || nil +config :mobilizon, Mobilizon.Service.Geospatial.MapQuest, api_key: nil -config :mobilizon, Mobilizon.Service.Geospatial.Mimirsbrunn, - endpoint: System.get_env("GEOSPATIAL_MIMIRSBRUNN_ENDPOINT") || nil +config :mobilizon, Mobilizon.Service.Geospatial.Mimirsbrunn, endpoint: nil -config :mobilizon, Mobilizon.Service.Geospatial.Pelias, - endpoint: System.get_env("GEOSPATIAL_PELIAS_ENDPOINT") || nil +config :mobilizon, Mobilizon.Service.Geospatial.Pelias, endpoint: nil config :mobilizon, :maps, tiles: [ - endpoint: - System.get_env("MAPS_TILES_ENDPOINT") || - "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - attribution: System.get_env("MAPS_TILES_ATTRIBUTION") + endpoint: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + attribution: "© The OpenStreetMap Contributors" ] config :mobilizon, :anonymous, diff --git a/config/dev.exs b/config/dev.exs index c52310e73..7a68ef14a 100644 --- a/config/dev.exs +++ b/config/dev.exs @@ -8,10 +8,10 @@ import Config # with brunch.io to recompile .js and .css sources. config :mobilizon, Mobilizon.Web.Endpoint, http: [ - port: System.get_env("MOBILIZON_INSTANCE_PORT") || 4000 + port: 4000 ], url: [ - host: System.get_env("MOBILIZON_INSTANCE_HOST") || "mobilizon.local", + host: System.get_env("MOBILIZON_INSTANCE_HOST", "mobilizon.local"), port: 80, scheme: "http" ], @@ -65,13 +65,36 @@ config :mobilizon, Mobilizon.Web.Email.Mailer, adapter: Bamboo.LocalAdapter # Configure your database config :mobilizon, Mobilizon.Storage.Repo, - types: Mobilizon.Storage.PostgresTypes, - username: System.get_env("MOBILIZON_DATABASE_USERNAME") || "mobilizon", - password: System.get_env("MOBILIZON_DATABASE_PASSWORD") || "mobilizon", - database: System.get_env("MOBILIZON_DATABASE_DBNAME") || "mobilizon_dev", - hostname: System.get_env("MOBILIZON_DATABASE_HOST") || "localhost", - port: System.get_env("MOBILIZON_DATABASE_PORT") || "5432", + username: System.get_env("MOBILIZON_DATABASE_USERNAME", "mobilizon"), + password: System.get_env("MOBILIZON_DATABASE_PASSWORD", "mobilizon"), + database: System.get_env("MOBILIZON_DATABASE_DBNAME", "mobilizon_dev"), + hostname: System.get_env("MOBILIZON_DATABASE_HOST", "localhost"), + port: "5432", pool_size: 10, show_sensitive_data_on_connection_error: true +config :mobilizon, :instance, + name: System.get_env("MOBILIZON_INSTANCE_NAME", "Mobilizon"), + hostname: System.get_env("MOBILIZON_INSTANCE_HOST", "Mobilizon"), + email_from: System.get_env("MOBILIZON_INSTANCE_EMAIL"), + email_reply_to: System.get_env("MOBILIZON_INSTANCE_EMAIL"), + registrations_open: System.get_env("MOBILIZON_INSTANCE_REGISTRATIONS_OPEN") == "true" + config :mobilizon, :activitypub, sign_object_fetches: false + +require Logger + +cond do + System.get_env("INSTANCE_CONFIG") && + File.exists?("./config/#{System.get_env("INSTANCE_CONFIG")}") -> + import_config System.get_env("INSTANCE_CONFIG") + + System.get_env("DOCKER", "false") == "false" && File.exists?("./config/dev.secret.exs") -> + import_config "dev.secret.exs" + + System.get_env("DOCKER", "false") == "true" -> + Logger.info("Using environment configuration for Docker") + + true -> + Logger.error("No configuration file found") +end diff --git a/config/prod.exs b/config/prod.exs index 4ff43c098..f37b2ac52 100644 --- a/config/prod.exs +++ b/config/prod.exs @@ -2,86 +2,26 @@ import Config config :mobilizon, Mobilizon.Web.Endpoint, http: [ - port: System.get_env("MOBILIZON_INSTANCE_PORT") || 4000, - transport_options: [socket_opts: [:inet6]] + port: 4000 ], url: [ - host: System.get_env("MOBILIZON_INSTANCE_HOST") || "mobilizon.me", - port: 443, - scheme: "https" - ], - secret_key_base: - System.get_env("MOBILIZON_SECRET") || "ThisShouldBeAVeryStrongStringPleaseReplaceMe", - cache_static_manifest: "priv/static/manifest.json" - -# Configure your database -config :mobilizon, Mobilizon.Storage.Repo, - types: Mobilizon.Storage.PostgresTypes, - username: System.get_env("MOBILIZON_DATABASE_USERNAME") || "mobilizon", - password: System.get_env("MOBILIZON_DATABASE_PASSWORD") || "mobilizon", - database: System.get_env("MOBILIZON_DATABASE_DBNAME") || "mobilizon_prod", - hostname: System.get_env("MOBILIZON_DATABASE_HOST") || "localhost", - port: System.get_env("MOBILIZON_DATABASE_PORT") || "5432", - pool_size: 15 - -config :mobilizon, Mobilizon.Web.Email.Mailer, - adapter: Bamboo.SMTPAdapter, - server: "localhost", - hostname: "localhost", - port: 25, - # or {:system, "SMTP_USERNAME"} - username: nil, - # or {:system, "SMTP_PASSWORD"} - password: nil, - # can be `:always` or `:never` - tls: :if_available, - # or {":system", ALLOWED_TLS_VERSIONS"} w/ comma seprated values (e.g. "tlsv1.1,tlsv1.2") - allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], - # can be `true` - ssl: false, - retries: 1, - # can be `true` - no_mx_lookups: false + host: "mobilizon.local", + scheme: "https", + port: 443 + ] # Do not print debug messages in production -config :logger, level: System.get_env("MOBILIZON_LOGLEVEL") |> String.to_atom() || :info +config :logger, level: :info -config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Nominatim +cond do + System.get_env("INSTANCE_CONFIG") && + File.exists?("./config/#{System.get_env("INSTANCE_CONFIG")}") -> + import_config System.get_env("INSTANCE_CONFIG") -# ## SSL Support -# -# To get SSL working, you will need to add the `https` key -# to the previous section and set your `:url` port to 443: -# -# config :mobilizon, Mobilizon.Web.Endpoint, -# ... -# url: [host: "example.com", port: 443], -# https: [:inet6, -# port: 443, -# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"), -# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")] -# -# Where those two env variables return an absolute path to -# the key and cert in disk or a relative path inside priv, -# for example "priv/ssl/server.key". -# -# We also recommend setting `force_ssl`, ensuring no data is -# ever sent via http, always redirecting to https: -# -# config :mobilizon, Mobilizon.Web.Endpoint, -# force_ssl: [hsts: true] -# -# Check `Plug.SSL` for all available options in `force_ssl`. + File.exists?("./config/dev.secret.exs") -> + import_config "dev.secret.exs" -# ## Using releases -# -# If you are doing OTP releases, you need to instruct Phoenix -# to start the server for all endpoints: -# -# config :phoenix, :serve_endpoints, true -# -# Alternatively, you can configure exactly which server to -# start per endpoint: -# -# config :mobilizon, Mobilizon.Web.Endpoint, server: true -# + true -> + require Logger + Logger.error("No configuration file found") +end diff --git a/config/test.exs b/config/test.exs index 5049ce143..a8ba75e47 100644 --- a/config/test.exs +++ b/config/test.exs @@ -8,11 +8,13 @@ config :mobilizon, :instance, # you can enable the server option below. config :mobilizon, Mobilizon.Web.Endpoint, http: [ - port: System.get_env("MOBILIZON_INSTANCE_PORT") || 80 + port: 80 ], url: [ - host: System.get_env("MOBILIZON_INSTANCE_HOST") || "mobilizon.test" + host: "mobilizon.test", + scheme: "http" ], + secret_key_base: "some secret", server: false # Print only warnings and errors during test @@ -26,7 +28,7 @@ config :logger, # Configure your database config :mobilizon, Mobilizon.Storage.Repo, types: Mobilizon.Storage.PostgresTypes, - username: System.get_env("MOBILIZON_DATABASE_USERNAME") || "mobilizon", + username: System.get_env("MOBILIZON_DATABASE_USERNAME") || "mobilizon_test", password: System.get_env("MOBILIZON_DATABASE_PASSWORD") || "mobilizon", database: System.get_env("MOBILIZON_DATABASE_DBNAME") || "mobilizon_test", hostname: System.get_env("MOBILIZON_DATABASE_HOST") || "localhost", @@ -44,3 +46,9 @@ config :exvcr, config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Mock config :mobilizon, Oban, queues: false, prune: :disabled + +config :mobilizon, Mobilizon.Web.Auth.Guardian, secret_key: "some secret" + +if System.get_env("DOCKER", "false") == "false" && File.exists?("./config/test.secret.exs") do + import_config "test.secret.exs" +end diff --git a/docker-compose.yml b/docker-compose.yml index caa767380..3df763f40 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -22,6 +22,7 @@ services: - postgres environment: MIX_ENV: "dev" + DOCKER: "true" MOBILIZON_INSTANCE_NAME: My Mobilizon Instance MOBILIZON_INSTANCE_HOST: mobilizon.me MOBILIZON_INSTANCE_EMAIL: noreply@mobilizon.me diff --git a/docs/administration/CLI tasks/maintenance_ tasks.md b/docs/administration/CLI tasks/maintenance_ tasks.md index 241b77f11..938e86350 100644 --- a/docs/administration/CLI tasks/maintenance_ tasks.md +++ b/docs/administration/CLI tasks/maintenance_ tasks.md @@ -15,7 +15,7 @@ mix mobilizon.instance gen [] ### Options * `-f`, `--force` Whether to erase existing files -* `-o`, `--output PATH` The path to output the `.env` file. Defaults to `.env.production`. +* `-o`, `--output PATH` The path to output the `prod.secret.exs` file. Defaults to `config/prod.secret.exs`. * `--output_psql PATH` The path to output the SQL script. Defaults to `setup_db.psql`. * `--domain DOMAIN` The instance's domain * `--instance_name INSTANCE_NAME` The instance's name @@ -25,34 +25,3 @@ mix mobilizon.instance gen [] * `--dbuser DBUSER` The database user (aka role) to use for the database connection * `--dbpass DBPASS` The database user's password to use for the database connection * `--dbport DBPORT` The database port - -## Depreciated commands - -### move_participant_stats - -!!! tip "Environment" - You need to run these commands with the appropriate environment loaded - -Task to move participant stats directly on the `event` table (so there's no need to count event participants each time). -This task should **only be run once** when migrating from `v1.0.0-beta.1` to `v1.0.0-beta.2`. - -This task will be removed in version `v1.0.0-beta.3`. - -```bash -mix mobilizon.move_participant_stats -``` - -### setup_search - -!!! tip "Environment" - You need to run these commands with the appropriate environment loaded - -Task to setup search for existing events. - -This task should **only be run once** when migrating from `v1.0.0-beta.1` to `v1.0.0-beta.2`. - -This task will be removed in version `v1.0.0-beta.3`. - -```bash -mix mobilizon.setup_search -``` diff --git a/docs/administration/CLI tasks/manage_actors.md b/docs/administration/CLI tasks/manage_actors.md index 2ef74e03b..1d2ee4d92 100644 --- a/docs/administration/CLI tasks/manage_actors.md +++ b/docs/administration/CLI tasks/manage_actors.md @@ -1,7 +1,7 @@ # Manage actors !!! tip "Environment" - You need to run these commands with the appropriate environment loaded + You need to run these commands with the appropriate environment loaded, so probably prefix with `MIX_ENV=prod`. ## List all available commands ```bash diff --git a/docs/administration/CLI tasks/manage_users.md b/docs/administration/CLI tasks/manage_users.md index c8b83f52c..9a9e07dab 100644 --- a/docs/administration/CLI tasks/manage_users.md +++ b/docs/administration/CLI tasks/manage_users.md @@ -1,7 +1,7 @@ # Manage users !!! tip "Environment" - You need to run these commands with the appropriate environment loaded + You need to run these commands with the appropriate environment loaded, so probably prefix with `MIX_ENV=prod`. ## List all available commands diff --git a/docs/administration/CLI tasks/relay.md b/docs/administration/CLI tasks/relay.md index 68ef8b804..e8854e71b 100644 --- a/docs/administration/CLI tasks/relay.md +++ b/docs/administration/CLI tasks/relay.md @@ -3,7 +3,7 @@ Manages remote relays !!! tip "Environment" - You need to run these commands with the appropriate environment loaded + You need to run these commands with the appropriate environment loaded, so probably prefix with `MIX_ENV=prod`. ## Make your instance follow a mobilizon instance diff --git a/docs/administration/configure/email.md b/docs/administration/configure/email.md new file mode 100644 index 000000000..8cdf17b9a --- /dev/null +++ b/docs/administration/configure/email.md @@ -0,0 +1,31 @@ +# Email + +Mobilizon requires a SMTP server to deliver emails. Using 3rd-party mail providers (Mandrill, SendGrid, Mailjet, …) will be possible in the future. + +## SMTP configuration + +Mobilizon default settings assumes a SMTP server listens on `localhost`, port `25`. To specify a specific server and credentials, you can add the following section in your `prod.secret.exs` file and modify credentials to your needs. +```elixir +config :mobilizon, Mobilizon.Web.Email.Mailer, + adapter: Bamboo.SMTPAdapter, + server: "localhost", + hostname: "localhost", + port: 25, + username: nil, + password: nil, + # can be `:always` or `:never` + tls: :if_available, + allowed_tls_versions: [:tlsv1, :"tlsv1.1", :"tlsv1.2"], + # can be `true` + ssl: false, + retries: 1, + # can be `true` + no_mx_lookups: false, + # can be `:always`. If your smtp relay requires authentication set it to `:always`. + auth: :if_available +``` + +!!! tip + The hostname option sets the FQDN to the header of your emails, its optional, but if you don't set it, the underlying `gen_smtp` module will use the hostname of your machine, like `localhost`. + +You'll need to restart Mobilizon to recompile the app and apply the new settings. \ No newline at end of file diff --git a/docs/administration/configure/geocoders.md b/docs/administration/configure/geocoders.md index 3f636ed84..1a0141c26 100644 --- a/docs/administration/configure/geocoders.md +++ b/docs/administration/configure/geocoders.md @@ -10,18 +10,28 @@ However, providing a geocoding service is quite expensive, especially if you wan !!! note "Hardware setup" To give an idea of what hardware is required to self-host a geocoding service, we successfully installed and used [Addok](#addok), [Pelias](#pelias) and [Mimirsbrunn](#mimirsbrunn) on a 8 cores/16GB RAM machine without any issues **importing only French addresses and data**. +## Change geocoder + +To change geocoder backend, you need to add the following line in `prod.secret.exs`: +```elixir +config :mobilizon, Mobilizon.Service.Geospatial, + service: Mobilizon.Service.Geospatial.Nominatim +``` +And change `Nominatim` to one of the supported geocoders. Depending on the provider, you'll also need to add some special config to specify eventual endpoints or API keys. + +For instance, when using `Mimirsbrunn`, you'll need the following configuration: +```elixir +config :mobilizon, Mobilizon.Service.Geospatial, + service: Mobilizon.Service.Geospatial.Mimirsbrunn + +config :mobilizon, Mobilizon.Service.Geospatial.Mimirsbrunn, + endpoint: "https://my-mimir-instance.tld" +``` + ## List of supported geocoders This is the list of all geocoders supported by Mobilizon. The current default one is [Nominatim](#nominatim) and uses the official OpenStreetMap instance. -!!! bug - Changing geocoder through `.env` configuration isn't currently supported by Mobilizon. - Instead you need to edit the following line in `config.prod.exs`: - ```elixir - config :mobilizon, Mobilizon.Service.Geospatial, service: Mobilizon.Service.Geospatial.Nominatim - ``` - And change `Nominatim` to one of the supported geocoders. This change might be overwritten when updating Mobilizon. - ### Nominatim [Nominatim](https://wiki.openstreetmap.org/wiki/Nominatim) is a GPL-2.0 licenced tool to search data by name and address. It's written in C and PHP and uses PostgreSQL. @@ -35,6 +45,13 @@ It's the current default search tool on the [OpenStreetMap homepage](https://www Several companies provide hosted instances of Nominatim that you can query via an API, for example see [MapQuest Open Initiative](https://developer.mapquest.com/documentation/open/nominatim-search). +** Default configuration ** +```elixir +config :mobilizon, Mobilizon.Service.Geospatial.Nominatim, + endpoint: "https://nominatim.openstreetmap.org", + api_key: nil +``` + ### Addok [Addok](https://github.com/addok/addok) is a WTFPL licenced search engine for address (and only address). It's written in Python and uses Redis. @@ -42,6 +59,12 @@ It's used by French government for [adresse.data.gouv.fr](https://adresse.data.g !!! warning "Terms" When using France's Addok instance at `api-adresse.data.gouv.fr` (default endpoint for this geocoder if not configured otherwise), you need to read and accept the [GCU](https://adresse.data.gouv.fr/cgu) (in French). + +** Default configuration ** +```elixir +config :mobilizon, Mobilizon.Service.Geospatial.Addok, + endpoint: "https://api-adresse.data.gouv.fr" +``` ### Photon @@ -50,6 +73,12 @@ It's used by French government for [adresse.data.gouv.fr](https://adresse.data.g !!! warning "Terms" The terms of use for the official instance (default endpoint for this geocoder if not configured otherwise) are simply the following: > You can use the API for your project, but please be fair - extensive usage will be throttled. We do not guarantee for the availability and usage might be subject of change in the future. + +** Default configuration ** +```elixir +config :mobilizon, Mobilizon.Service.Geospatial.Photon, + endpoint: "https://photon.komoot.de" +``` ### Pelias @@ -58,24 +87,53 @@ It's used by French government for [adresse.data.gouv.fr](https://adresse.data.g There's [Geocode Earth](https://geocode.earth/) SAAS that provides a Pelias API. They offer discounts for Open-Source projects. [See the pricing](https://geocode.earth/). +**Configuration example** +```elixir +config :mobilizon, Mobilizon.Service.Geospatial.Pelias, + endpoint: nil +``` + ### Mimirsbrunn [Mimirsbrunn](https://github.com/CanalTP/mimirsbrunn) is an AGPL-3.0 licensed geocoding written in Rust and powered by ElasticSearch. Mimirsbrunn is used by [Qwant Maps](https://www.qwant.com/maps) and [Navitia](https://www.navitia.io). +** Default configuration ** +```elixir +config :mobilizon, Mobilizon.Service.Geospatial.Mimirsbrunn, + endpoint: nil +``` + ### Google Maps [Google Maps](https://developers.google.com/maps/documentation/geocoding/intro) is a proprietary service that provides APIs for geocoding. They don't have a free plan, but offer credit when creating a new account. [See the pricing](https://cloud.google.com/maps-platform/pricing/). +** Default configuration ** + +!!! note + `fetch_place_details` tells GoogleMaps to also fetch some details on a place when geocoding. It can be more expensive, since you're doing two requests to Google instead of one. + +```elixir +config :mobilizon, Mobilizon.Service.Geospatial.GoogleMaps, + api_key: nil, + fetch_place_details: true +``` + ### MapQuest [MapQuest](https://developer.mapquest.com/documentation/open/geocoding-api/) is a proprietary service that provides APIs for geocoding. They offer a free plan. [See the pricing](https://developer.mapquest.com/plans). +** Default configuration ** +```elixir +config :mobilizon, Mobilizon.Service.Geospatial.MapQuest, + api_key: nil +``` + ### More geocoding services Geocoding implementations are simple modules that need to implement the [`Mobilizon.Service.Geospatial.Provider` behaviour](https://framasoft.frama.io/mobilizon/backend/Mobilizon.Service.Geospatial.Provider.html), so feel free to write your own! diff --git a/docs/administration/docker.md b/docs/administration/docker.md deleted file mode 100644 index 421ac3af0..000000000 --- a/docs/administration/docker.md +++ /dev/null @@ -1,15 +0,0 @@ -# Docker - -You can quickly get a server running using Docker. You'll need both [Docker](https://www.docker.com/community-edition) and [Docker-Compose](https://docs.docker.com/compose/install/). - -Start by cloning the repo -```bash -git clone https://framagit.org/framasoft/mobilizon && cd mobilizon -``` - -Then, just run `make` to build containers. -```bash -make -``` - -This will start a database container, an API container also containing the front-end running on `localhost`. diff --git a/docs/administration/index.md b/docs/administration/index.md index 6c606e822..165b4c49a 100644 --- a/docs/administration/index.md +++ b/docs/administration/index.md @@ -1,13 +1,14 @@ # Install +!!! info "Docker" + + Docker production installation is not yet supported. See [issue #352](https://framagit.org/framasoft/mobilizon/issues/352). + ## Pre-requisites * A Linux machine with **root access** * A **domain name** (or subdomain) for the Mobilizon server, e.g. `example.net` -* An **SMTP server** to deliver emails - -!!! tip - You can also install Mobilizon [with Docker](docker.md). +* An **SMTP server** to deliver emails ## Dependencies @@ -88,12 +89,33 @@ Mobilizon provides a command line tool to generate configuration mix mobilizon.instance gen ``` -This will ask you questions about your instance and generate a `.env.prod` file. +This will ask you questions about your setup and your instance to generate a `prod.secret.exs` file in the `config/` folder, and a `setup_db.psql` file to setup the database. +### Database setup -### Migration +The `setup_db.psql` file contains SQL instructions to create a PostgreSQL user and database with the chosen credentials and add the required extensions to the Mobilizon database. -Run database migrations: `mix ecto.migrate`. You will have to do this again after most updates. +Execute +```bash +sudo -u postgres psql -f setup_db.psql +``` + +!!! warning + + When it's done, don't forget to remove the `setup_db.psql` file. + +### Database Migration + +Run database migrations: +```bash +MIX_ENV=prod mix ecto.migrate +``` + +!!! note + + Note the `MIX_ENV=prod` environment variable prefix in front of the command. You will have to use it for each `mix` command from now on. + +You will have to do this again after most updates. !!! tip If some migrations fail, it probably means you're not using a recent enough version of PostgreSQL, or that you haven't installed the required extensions. @@ -147,3 +169,13 @@ sudo ln -s /etc/nginx/sites-available/mobilizon.conf /etc/nginx/sites-enabled/ Edit the file `/etc/nginx/sites-available` and adapt it to your own configuration. Test the configuration with `sudo nginx -t` and reload nginx with `systemctl reload nginx`. + +## Optional tasks + +### Geolocation databases + +Mobilizon can use geolocation from MMDB format data from sources like [MaxMind GeoIP](https://dev.maxmind.com/geoip/geoip2/geolite2/) databases or [db-ip.com](https://db-ip.com/db/download/ip-to-city-lite) databases. This allows showing events happening near the user's location. + +You will need to download the City database and put it into `priv/data/GeoLite2-City.mmdb`. + +Mobilizon will only show a warning at startup if the database is missing, but it isn't required. \ No newline at end of file diff --git a/docs/administration/upgrading.md b/docs/administration/upgrading.md index bf20d45e1..18b7debdb 100644 --- a/docs/administration/upgrading.md +++ b/docs/administration/upgrading.md @@ -10,7 +10,7 @@ Some tasks (like database migrations) can take a while, so we advise you to run # Backup -Always make sure your database and `.env.production` file are properly backuped before performing upgrades. +Always make sure your database and `config` folder are properly backuped before performing upgrades. Unless stated otherwise in the release notes, the following steps are enough to upgrade Mobilizon. @@ -57,7 +57,7 @@ cd ../ ### Recompile Mobilizon ```bash -mix compile +MIX_ENV=prod mix compile ``` Let's switch back to your regular user. @@ -72,7 +72,7 @@ Go back to the `mobilizon` user. ```bash sudo -i -u mobilizon cd live -mix ecto.migrate +MIX_ENV=prod mix ecto.migrate ``` ### Restart Mobilizon Let's switch back one last time to your regular user. diff --git a/docs/contribute/development.md b/docs/contribute/development.md index a72b7d5cb..b8de4c2ac 100644 --- a/docs/contribute/development.md +++ b/docs/contribute/development.md @@ -1,11 +1,12 @@ # Development Clone the repository: -```bash -# With HTTPS -git clone https://framagit.org/framasoft/mobilizon && cd mobilizon -# With SSH +```bash tab="HTTPS" +git clone https://framagit.org/framasoft/mobilizon && cd mobilizon +``` + +```bash tab="SSH" git clone git@framagit.org:framasoft/mobilizon.git && cd mobilizon ``` @@ -14,19 +15,19 @@ Run Mobilizon: * with Docker and Docker-Compose (**Recommended**) * without Docker and Docker-Compose (This involves more work on your part, use Docker and Docker-Compose if you can) -## With Docker and Docker-Compose +## With Docker * Install [Docker](https://docs.docker.com/install/#supported-platforms) and [Docker-Compose](https://docs.docker.com/compose/install/) for your system. * Run `make start` to build, then launch a database container and an API container. * Follow the progress of the build with `docker-compose logs -f`. * Access `localhost:4000` in your browser once the containers are fully built and launched. -## Without Docker and Docker-Compose +## Without Docker * Install dependencies: - * Elixir (and Erlang) by following the instructions at [https://elixir-lang.github.io/install.html](https://elixir-lang.github.io/install.html) - * [PostgreSQL]() with PostGIS - * Install NodeJS (we guarantee support for the latest LTS and later) ![](https://img.shields.io/badge/node-%3E%3D%2012.0+-brightgreen.svg) + * [Elixir (and Erlang)](https://elixir-lang.org/install.html) + * PostgreSQL >= 9.6 with PostGIS + * [Install NodeJS](https://nodejs.org/en/download/) (we guarantee support for the latest LTS and later) ![](https://img.shields.io/badge/node-%3E%3D%2012.0+-brightgreen.svg) * Start services: * Start postgres * Setup services: @@ -48,3 +49,9 @@ Run Mobilizon: Now you can visit [`localhost:4000`](http://localhost:4000) in your browser and see the website (server *and* client) in action. + +## FAQ + +### Issues with argon2 when creating users. + +This is because you installed deps through Docker and are now using Mobilizon without it, or the other way around. Just `rm -r deps/argon2_elixir` and trigger `mix deps.get` again. \ No newline at end of file diff --git a/docs/contribute/styleguide.md b/docs/contribute/styleguide.md index 9a70cc48f..a843253a2 100644 --- a/docs/contribute/styleguide.md +++ b/docs/contribute/styleguide.md @@ -7,7 +7,7 @@ We format our code with the Elixir Formatter and check for issues with [Credo](h Please run these two commands before pushing code: * `mix format` - * `mix credo` + * `mix credo --strict -a` These two commands must not return an error code, since they are required to pass inside CI. diff --git a/js/.gitignore b/js/.gitignore index db755fec3..c06650462 100644 --- a/js/.gitignore +++ b/js/.gitignore @@ -9,10 +9,6 @@ selenium-debug.log styleguide/ -# local env files -.env.local -.env.*.local - # Log files npm-debug.log* yarn-debug.log* diff --git a/lib/federation/activity_pub/activity_pub.ex b/lib/federation/activity_pub/activity_pub.ex index 42e2922c5..a3f07d1e5 100644 --- a/lib/federation/activity_pub/activity_pub.ex +++ b/lib/federation/activity_pub/activity_pub.ex @@ -707,7 +707,7 @@ defmodule Mobilizon.Federation.ActivityPub do # Get recipients for an activity or object @spec get_recipients(map()) :: list() defp get_recipients(data) do - (data["to"] || []) ++ (data["cc"] || []) + Map.get(data, "to", []) ++ Map.get(data, "cc", []) end @spec create_event(map(), map()) :: {:ok, map()} @@ -870,17 +870,18 @@ defmodule Mobilizon.Federation.ActivityPub do audience <- follower.actor |> Audience.calculate_to_and_cc_from_mentions() |> Map.merge(additional), reject_data <- %{ - "to" => follower.actor.url, + "to" => [follower.actor.url], "type" => "Reject", - "actor" => follower.actor.url, + "actor" => follower.target_actor.url, "object" => follower_as_data }, update_data <- - reject_data - |> Map.merge(audience) + audience + |> Map.merge(reject_data) |> Map.merge(%{ "id" => "#{Endpoint.url()}/reject/follow/#{follower.id}" }) do + Logger.error(inspect(update_data)) {:ok, follower, update_data} else err -> diff --git a/lib/federation/activity_pub/transmogrifier.ex b/lib/federation/activity_pub/transmogrifier.ex index 34e57b8ba..0efbd73e2 100644 --- a/lib/federation/activity_pub/transmogrifier.ex +++ b/lib/federation/activity_pub/transmogrifier.ex @@ -405,14 +405,14 @@ defmodule Mobilizon.Federation.ActivityPub.Transmogrifier do Handle incoming `Reject` activities wrapping a `Follow` activity """ def do_handle_incoming_reject_following(follow_object, %Actor{} = actor) do - with {:follow, {:ok, %Follower{approved: false, target_actor: followed} = follow}} <- + with {:follow, {:ok, %Follower{target_actor: followed} = follow}} <- {:follow, get_follow(follow_object)}, {:same_actor, true} <- {:same_actor, actor.id == followed.id}, {:ok, activity, _} <- ActivityPub.reject(:follow, follow) do {:ok, activity, follow} else - {:follow, _} -> + {:follow, _err} -> Logger.debug( "Tried to handle a Reject activity but it's not containing a Follow activity" ) diff --git a/lib/graphql/api/follows.ex b/lib/graphql/api/follows.ex index 1796c80d8..11b0eef7d 100644 --- a/lib/graphql/api/follows.ex +++ b/lib/graphql/api/follows.ex @@ -54,8 +54,8 @@ defmodule Mobilizon.GraphQL.API.Follows do def reject(%Actor{} = follower, %Actor{} = followed) do Logger.debug("We're trying to reject a follow") - with %Follower{} = follow <- - Actors.is_following(follower, followed), + with {:follower, %Follower{} = follow} <- + {:follower, Actors.is_following(follower, followed)}, {:ok, %Activity{} = activity, %Follower{} = follow} <- ActivityPub.reject( :follow, @@ -64,7 +64,10 @@ defmodule Mobilizon.GraphQL.API.Follows do ) do {:ok, activity, follow} else - %Follower{approved: true} -> + {:follower, nil} -> + {:error, "Follow not found"} + + {:follower, %Follower{approved: true}} -> {:error, "Follow already accepted"} end end diff --git a/lib/mix/tasks/mobilizon/instance.ex b/lib/mix/tasks/mobilizon/instance.ex index 451997f8c..b4d91ced3 100644 --- a/lib/mix/tasks/mobilizon/instance.ex +++ b/lib/mix/tasks/mobilizon/instance.ex @@ -58,7 +58,7 @@ defmodule Mix.Tasks.Mobilizon.Instance do paths = [config_path, psql_path] = [ - Keyword.get(options, :output, ".env"), + Keyword.get(options, :output, "config/prod.secret.exs"), Keyword.get(options, :output_psql, "setup_db.psql") ] @@ -113,11 +113,30 @@ defmodule Mix.Tasks.Mobilizon.Instance do "autogenerated" ) - secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) + listen_port = + Common.get_option( + options, + :listen_port, + "What port will the app listen to (leave it if you are using the default setup with nginx)?", + 4000 + ) + + listen_ip = + Common.get_option( + options, + :listen_ip, + "What ip will the app listen to (leave it if you are using the default setup with nginx)?", + "127.0.0.1" + ) + + instance_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) + auth_secret = :crypto.strong_rand_bytes(64) |> Base.encode64() |> binary_part(0, 64) + + template_dir = Application.app_dir(:mobilizon, "priv") <> "/templates" result_config = EEx.eval_file( - ".env.sample" |> Path.expand(__DIR__ <> "../../../../../"), + "#{template_dir}/config.template.eex", instance_domain: domain, instance_port: port, instance_email: email, @@ -128,12 +147,15 @@ defmodule Mix.Tasks.Mobilizon.Instance do database_username: dbuser, database_password: dbpass, version: Mobilizon.Mixfile.project() |> Keyword.get(:version), - instance_secret: secret + instance_secret: instance_secret, + auth_secret: auth_secret, + listen_ip: listen_ip, + listen_port: listen_port ) result_psql = EEx.eval_file( - "support/postgresql/setup_db.psql" |> Path.expand(__DIR__ <> "../../../../../"), + "#{template_dir}/setup_db.eex", database_name: dbname, database_username: dbuser, database_password: dbpass @@ -155,12 +177,7 @@ defmodule Mix.Tasks.Mobilizon.Instance do 2. Run `sudo -u postgres psql -f #{Common.escape_sh_path(psql_path)} && rm #{ Common.escape_sh_path(psql_path) }`. - """ <> - if config_path in [".env.production", ".env.dev", ".env.test"] do - "" - else - "3. Run `mv #{Common.escape_sh_path(config_path)} '.env.production'`." - end + """ ) else Mix.shell().error( diff --git a/lib/mobilizon/addresses/addresses.ex b/lib/mobilizon/addresses/addresses.ex index 0c250e107..07bdca708 100644 --- a/lib/mobilizon/addresses/addresses.ex +++ b/lib/mobilizon/addresses/addresses.ex @@ -37,7 +37,7 @@ defmodule Mobilizon.Addresses do %Address{} |> Address.changeset(attrs) |> Repo.insert( - on_conflict: :replace_all_except_primary_key, + on_conflict: {:replace_all_except, [:id]}, conflict_target: [:origin_id] ) end diff --git a/mix.exs b/mix.exs index f36b27857..cc7da7928 100644 --- a/mix.exs +++ b/mix.exs @@ -135,6 +135,7 @@ defmodule Mobilizon.Mixfile do "ecto.setup" ], test: [ + "ecto.create", "ecto.migrate", &run_test/1 ], diff --git a/mkdocs.yml b/mkdocs.yml index 1089c44d9..72ff2109f 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -21,6 +21,8 @@ markdown_extensions: plugins: - search - git-revision-date-localized + - minify: + minify_html: true theme: name: 'material' custom_dir: 'docs/theme/' diff --git a/priv/templates/config.template.eex b/priv/templates/config.template.eex new file mode 100644 index 000000000..59c75369c --- /dev/null +++ b/priv/templates/config.template.eex @@ -0,0 +1,31 @@ +# Mobilizon instance configuration + +import Config + +config :mobilizon, Mobilizon.Web.Endpoint, + url: [host: "<%= instance_domain %>", scheme: "https", port: <%= instance_port %>], + http: [ip: {<%= String.replace(listen_ip, ".", ", ") %>}, port: <%= listen_port %>], + secret_key_base: "<%= instance_secret %>" + +config :mobilizon, Mobilizon.Web.Auth.Guardian, + secret_key: "<%= auth_secret %>" + +config :mobilizon, :instance, + name: "<%= instance_name %>", + description: "Change this to a proper description of your instance", + hostname: "<%= instance_domain %>", + registrations_open: false, + demo: false, + allow_relay: true, + federating: true, + email_from: "tcit@tcit.fr", + email_reply_to: "tcit@tcit.fr" + +config :mobilizon, Mobilizon.Storage.Repo, + adapter: Ecto.Adapters.Postgres, + username: "<%= database_username %>", + password: "<%= database_password %>", + database: "<%= database_name %>", + hostname: "<%= database_host %>", + port: "<%= database_port %>", + pool_size: 10 diff --git a/support/postgresql/setup_db.psql b/priv/templates/setup_db.eex similarity index 100% rename from support/postgresql/setup_db.psql rename to priv/templates/setup_db.eex diff --git a/support/systemd/mobilizon.service b/support/systemd/mobilizon.service index 2a979950a..6fc25bb6f 100644 --- a/support/systemd/mobilizon.service +++ b/support/systemd/mobilizon.service @@ -9,7 +9,7 @@ ExecStart=/usr/local/bin/mix phx.server ExecReload=/bin/kill $MAINPID KillMode=process Restart=on-failure -EnvironmentFile=/var/www/mobilizon/.env +Environment=MIX_ENV=prod ; Some security directives. ; Use private /tmp and /var/tmp folders inside a new file system namespace, which are discarded after the process stops.