diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 9d241b539eedf9ac95ebee2b290b8a173c651e1c..f3f787fecd58ec78d5937cca542cd0a3d6ef5679 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -71,7 +71,7 @@ review_docs: - cd docs - apt-get update - apt-get install -y graphviz - - pip install sphinx sphinx_rtd_theme + - pip install sphinx sphinx_rtd_theme django-environ django script: - ./build_docs.sh cache: diff --git a/api/config/settings/common.py b/api/config/settings/common.py index c98b1b8c6d78d53f449ff5cd70f98c8ffc8add7b..9f8cfdf5063d1685db76deee57c6f6f35382a7e8 100644 --- a/api/config/settings/common.py +++ b/api/config/settings/common.py @@ -1,13 +1,4 @@ # -*- coding: utf-8 -*- -""" -Django settings for funkwhale_api project. - -For more information on this file, see -https://docs.djangoproject.com/en/dev/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/dev/ref/settings/ -""" from __future__ import absolute_import, unicode_literals import datetime @@ -29,6 +20,9 @@ APPS_DIR = ROOT_DIR.path("funkwhale_api") env = environ.Env() LOGLEVEL = env("LOGLEVEL", default="info").upper() +""" +Default logging level for the Funkwhale processes""" # pylint: disable=W0105 + LOGGING_CONFIG = None logging.config.dictConfig( { @@ -57,7 +51,10 @@ logging.config.dictConfig( } ) -env_file = env("ENV_FILE", default=None) +ENV_FILE = env_file = env("ENV_FILE", default=None) +""" +Path to a .env file to load +""" if env_file: logger.info("Loading specified env file at %s", env_file) # we have an explicitely specified env file @@ -85,6 +82,9 @@ else: FUNKWHALE_PLUGINS_PATH = env( "FUNKWHALE_PLUGINS_PATH", default="/srv/funkwhale/plugins/" ) +""" +Path to a directory containing Funkwhale plugins. These will be imported at runtime. +""" sys.path.append(FUNKWHALE_PLUGINS_PATH) FUNKWHALE_HOSTNAME = None @@ -99,7 +99,14 @@ if FUNKWHALE_HOSTNAME_PREFIX and FUNKWHALE_HOSTNAME_SUFFIX: else: try: FUNKWHALE_HOSTNAME = env("FUNKWHALE_HOSTNAME") + """ + Hostname of your Funkwhale pod, e.g ``mypod.audio`` + """ + FUNKWHALE_PROTOCOL = env("FUNKWHALE_PROTOCOL", default="https") + """ + Protocol end users will use to access your pod, either ``http`` or ``https``. + """ except Exception: FUNKWHALE_URL = env("FUNKWHALE_URL") _parsed = urlsplit(FUNKWHALE_URL) @@ -112,6 +119,16 @@ FUNKWHALE_URL = "{}://{}".format(FUNKWHALE_PROTOCOL, FUNKWHALE_HOSTNAME) FUNKWHALE_SPA_HTML_ROOT = env( "FUNKWHALE_SPA_HTML_ROOT", default=FUNKWHALE_URL + "/front/" ) +""" +URL or path to the Web Application files. Funkwhale needs access to it so that +it can inject <meta> tags relevant to the given page (e.g page title, cover, etc.). + +If a URL is specified, the index.html file will be fetched through HTTP. If a path is provided, +it will be accessed from disk. + +Use something like ``/srv/funkwhale/front/dist/`` if the web processes shows request errors related to this. +""" + FUNKWHALE_SPA_HTML_CACHE_DURATION = env.int( "FUNKWHALE_SPA_HTML_CACHE_DURATION", default=60 * 15 ) @@ -148,7 +165,16 @@ FEDERATION_SERVICE_ACTOR_USERNAME = env( ) # How many pages to fetch when crawling outboxes and third-party collections FEDERATION_COLLECTION_MAX_PAGES = env.int("FEDERATION_COLLECTION_MAX_PAGES", default=5) +""" +Number of existing pages of content to fetch when discovering/refreshing an actor or channel. + +More pages means more content will be loaded, but will require more resources. +""" + ALLOWED_HOSTS = env.list("DJANGO_ALLOWED_HOSTS", default=[]) + [FUNKWHALE_HOSTNAME] +""" +List of allowed hostnames for which the Funkwhale server will answer. +""" # APP CONFIGURATION # ------------------------------------------------------------------------------ @@ -223,12 +249,18 @@ LOCAL_APPS = ( PLUGINS = [p for p in env.list("FUNKWHALE_PLUGINS", default=[]) if p] +""" +List of Funkwhale plugins to load. +""" if PLUGINS: logger.info("Running with the following plugins enabled: %s", ", ".join(PLUGINS)) else: logger.info("Running with no plugins") ADDITIONAL_APPS = env.list("ADDITIONAL_APPS", default=[]) +""" +List of Django apps to load in addition to Funkwhale plugins and apps. +""" INSTALLED_APPS = ( DJANGO_APPS + THIRD_PARTY_APPS @@ -257,8 +289,11 @@ MIDDLEWARE = tuple(ADDITIONAL_MIDDLEWARES_BEFORE) + ( # DEBUG # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#debug -DEBUG = env.bool("DJANGO_DEBUG", False) - +DJANGO_DEBUG = DEBUG = env.bool("DJANGO_DEBUG", False) +""" +Whether to enable debugging info and pages. Never enable this on a production server, +as it can leak very sensitive information. +""" # FIXTURE CONFIGURATION # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#std:setting-FIXTURE_DIRS @@ -272,25 +307,70 @@ FIXTURE_DIRS = (str(APPS_DIR.path("fixtures")),) DEFAULT_FROM_EMAIL = env( "DEFAULT_FROM_EMAIL", default="Funkwhale <noreply@{}>".format(FUNKWHALE_HOSTNAME) ) +""" +Name and email address used to send system emails. + +Default: ``Funkwhale <noreply@yourdomain>`` + +.. note:: + + Both the forms ``Funkwhale <noreply@yourdomain>`` and + ``noreply@yourdomain`` work. +""" EMAIL_SUBJECT_PREFIX = env("EMAIL_SUBJECT_PREFIX", default="[Funkwhale] ") +""" +Subject prefix for system emails. +""" SERVER_EMAIL = env("SERVER_EMAIL", default=DEFAULT_FROM_EMAIL) EMAIL_CONFIG = env.email_url("EMAIL_CONFIG", default="consolemail://") +""" +SMTP configuration for sending emails. Possible values: + +- ``EMAIL_CONFIG=consolemail://``: output emails to console (the default) +- ``EMAIL_CONFIG=dummymail://``: disable email sending completely + +On a production instance, you'll usually want to use an external SMTP server: +- ``EMAIL_CONFIG=smtp://user@:password@youremail.host:25`` +- ``EMAIL_CONFIG=smtp+ssl://user@:password@youremail.host:465`` +- ``EMAIL_CONFIG=smtp+tls://user@:password@youremail.host:587`` + +.. note:: + + If ``user`` or ``password`` contain special characters (eg. + ``noreply@youremail.host`` as ``user``), be sure to urlencode them, using + for example the command: + ``python3 -c 'import urllib.parse; print(urllib.parse.quote_plus("noreply@youremail.host"))'`` + (returns ``noreply%40youremail.host``) + +""" vars().update(EMAIL_CONFIG) # DATABASE CONFIGURATION # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#databases +DATABASE_URL = env.db("DATABASE_URL") +""" +URL to connect to the PostgreSQL database. Examples: + +- ``postgresql://funkwhale@:5432/funkwhale`` +- ``postgresql://<user>:<password>@<host>:<port>/<database>`` +- ``postgresql://funkwhale:passw0rd@localhost:5432/funkwhale_database`` +""" DATABASES = { # Raises ImproperlyConfigured exception if DATABASE_URL not in os.environ - "default": env.db("DATABASE_URL") + "default": DATABASE_URL } DATABASES["default"]["ATOMIC_REQUESTS"] = True -DATABASES["default"]["CONN_MAX_AGE"] = env("DB_CONN_MAX_AGE", default=60 * 5) - +DB_CONN_MAX_AGE = DATABASES["default"]["CONN_MAX_AGE"] = env( + "DB_CONN_MAX_AGE", default=60 * 5 +) +""" +Max time, in seconds, before database connections are closed. +""" MIGRATION_MODULES = { # see https://github.com/jazzband/django-oauth-toolkit/issues/634 # swappable models are badly designed in oauth2_provider @@ -299,13 +379,6 @@ MIGRATION_MODULES = { "sites": "funkwhale_api.contrib.sites.migrations", } -# -# DATABASES = { -# 'default': { -# 'ENGINE': 'django.db.backends.sqlite3', -# 'NAME': 'db.sqlite3', -# } -# } # GENERAL CONFIGURATION # ------------------------------------------------------------------------------ # Local time zone for this installation. Choices can be found here: @@ -370,31 +443,79 @@ CRISPY_TEMPLATE_PACK = "bootstrap3" # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-root STATIC_ROOT = env("STATIC_ROOT", default=str(ROOT_DIR("staticfiles"))) - +""" +Path were static files should be collected. +""" # See: https://docs.djangoproject.com/en/dev/ref/settings/#static-url STATIC_URL = env("STATIC_URL", default=FUNKWHALE_URL + "/staticfiles/") DEFAULT_FILE_STORAGE = "funkwhale_api.common.storage.ASCIIFileSystemStorage" PROXY_MEDIA = env.bool("PROXY_MEDIA", default=True) +""" +Wether to proxy audio files through your reverse proxy. It's recommended to keep this on, +as a way to enforce access control, however, if you're using S3 storage with :attr:`AWS_QUERYSTRING_AUTH`, +it's safe to disable it. +""" AWS_DEFAULT_ACL = None AWS_QUERYSTRING_AUTH = env.bool("AWS_QUERYSTRING_AUTH", default=not PROXY_MEDIA) +""" +Whether to include signatures in S3 urls, as a way to enforce access-control. + +Defaults to the inverse of :attr:`PROXY_MEDIA`. +""" + AWS_S3_MAX_MEMORY_SIZE = env.int( "AWS_S3_MAX_MEMORY_SIZE", default=1000 * 1000 * 1000 * 20 ) + AWS_QUERYSTRING_EXPIRE = env.int("AWS_QUERYSTRING_EXPIRE", default=3600) +""" +Expiration delay, in seconds, of signatures generated when :attr:`AWS_QUERYSTRING_AUTH` is enabled. +""" + AWS_ACCESS_KEY_ID = env("AWS_ACCESS_KEY_ID", default=None) +""" +Access-key ID for your S3 storage. +""" if AWS_ACCESS_KEY_ID: AWS_ACCESS_KEY_ID = AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY = env("AWS_SECRET_ACCESS_KEY") + """ + Secret access key for your S3 storage. + """ AWS_STORAGE_BUCKET_NAME = env("AWS_STORAGE_BUCKET_NAME") + """ + Bucket name of your S3 storage. + """ AWS_S3_CUSTOM_DOMAIN = env("AWS_S3_CUSTOM_DOMAIN", default=None) + """ + Custom domain to use for your S3 storage. + """ AWS_S3_ENDPOINT_URL = env("AWS_S3_ENDPOINT_URL", default=None) + """ + If you use a S3-compatible storage such as minio, set the following variable to + the full URL to the storage server. Example: + + - ``https://minio.mydomain.com`` + - ``https://s3.wasabisys.com`` + """ AWS_S3_REGION_NAME = env("AWS_S3_REGION_NAME", default=None) + """If you are using Amazon S3 to serve media directly, you will need to specify your region + name in order to access files. Example: + + - ``eu-west-2`` + """ + AWS_S3_SIGNATURE_VERSION = "s3v4" AWS_LOCATION = env("AWS_LOCATION", default="") + """ + An optional bucket subdirectory were you want to store the files. This is especially useful + if you plan to use share the bucket with other services + """ DEFAULT_FILE_STORAGE = "funkwhale_api.common.storage.ASCIIS3Boto3Storage" + # See: https://docs.djangoproject.com/en/dev/ref/contrib/staticfiles/#std:setting-STATICFILES_DIRS STATICFILES_DIRS = (str(APPS_DIR.path("static")),) @@ -408,14 +529,25 @@ STATICFILES_FINDERS = ( # ------------------------------------------------------------------------------ # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-root MEDIA_ROOT = env("MEDIA_ROOT", default=str(APPS_DIR("media"))) - +""" +Where media files (such as album covers or audio tracks) should be stored +on your system? (Ensure this directory actually exists) +""" # See: https://docs.djangoproject.com/en/dev/ref/settings/#media-url MEDIA_URL = env("MEDIA_URL", default=FUNKWHALE_URL + "/media/") +""" +URL where media files are served. The default value should work fine on most +configurations, but could can tweak this if you are hosting media files on a separate +domain, or if you host Funkwhale on a non-standard port. +""" FILE_UPLOAD_PERMISSIONS = 0o644 ATTACHMENTS_UNATTACHED_PRUNE_DELAY = env.int( "ATTACHMENTS_UNATTACHED_PRUNE_DELAY", default=3600 * 24 ) +""" +Delay in seconds before uploaded but unattached attachements are pruned from the system. +""" # URL Configuration # ------------------------------------------------------------------------------ @@ -441,6 +573,14 @@ ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_VERIFICATION_ENFORCE = env.bool( "ACCOUNT_EMAIL_VERIFICATION_ENFORCE", default=False ) +""" +Determine wether users need to verify their email address before using the service. Enabling this can be useful +to reduce spam or bots accounts, however, you'll need to configure a mail server so that your users can receive the +verification emails, using :attr:`EMAIL_CONFIG`. + +Note that regardless of the setting value, superusers created through the command line will never require verification. + +""" ACCOUNT_EMAIL_VERIFICATION = ( "mandatory" if ACCOUNT_EMAIL_VERIFICATION_ENFORCE else "optional" ) @@ -472,6 +612,10 @@ OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "users.RefreshToken" # LDAP AUTHENTICATION CONFIGURATION # ------------------------------------------------------------------------------ AUTH_LDAP_ENABLED = env.bool("LDAP_ENABLED", default=False) +""" +Wether to enable LDAP authentication. See :doc:`/installation/ldap` for more information. +""" + if AUTH_LDAP_ENABLED: # Import the LDAP modules here; this way, we don't need the dependency unless someone @@ -541,8 +685,22 @@ if AUTH_LDAP_ENABLED: AUTOSLUG_SLUGIFY_FUNCTION = "slugify.slugify" CACHE_DEFAULT = "redis://127.0.0.1:6379/0" +CACHE_URL = env.cache_url("CACHE_URL", default=CACHE_DEFAULT) +""" +URL to your redis server. Examples: + +- `redis://<host>:<port>/<database>` +- `redis://127.0.0.1:6379/0` +- `redis://:password@localhost:6379/0` for password auth (the extra semicolon is important) +- `redis:///run/redis/redis.sock?db=0` over unix sockets + +.. note:: + + If you want to use Redis over unix sockets, you'll also need to update :attr:`CELERY_BROKER_URL` + +""" CACHES = { - "default": env.cache_url("CACHE_URL", default=CACHE_DEFAULT), + "default": CACHE_URL, "local": { "BACKEND": "django.core.cache.backends.locmem.LocMemCache", "LOCATION": "local-cache", @@ -567,7 +725,7 @@ CACHEOPS_ENABLED = bool(CACHEOPS_DURATION) if CACHEOPS_ENABLED: INSTALLED_APPS += ("cacheops",) - CACHEOPS_REDIS = env("CACHE_URL", default=CACHE_DEFAULT) + CACHEOPS_REDIS = CACHE_URL CACHEOPS_PREFIX = lambda _: "cacheops" # noqa CACHEOPS_DEFAULTS = {"timeout": CACHEOPS_DURATION} CACHEOPS = { @@ -581,6 +739,15 @@ INSTALLED_APPS += ("funkwhale_api.taskapp.celery.CeleryConfig",) CELERY_BROKER_URL = env( "CELERY_BROKER_URL", default=env("CACHE_URL", default=CACHE_DEFAULT) ) +""" +URL to celery's task broker. Defaults to :attr:`CACHE_URL`, so you shouldn't have to tweak this, unless you want +to use a different one, or use Redis sockets to connect. + +Exemple: + +- `redis://127.0.0.1:6379/0` +- `redis+socket:///run/redis/redis.sock?virtual_host=0` +""" # END CELERY # Location of root django.contrib.admin URL, use {% url 'admin:index' %} @@ -667,7 +834,11 @@ AUTH_PASSWORD_VALIDATORS = [ {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, ] -if env.bool("DISABLE_PASSWORD_VALIDATORS", default=False): +DISABLE_PASSWORD_VALIDATORS = env.bool("DISABLE_PASSWORD_VALIDATORS", default=False) +""" +Wether to disable password validators (length, common words, similarity with username…) used during regitration. +""" +if DISABLE_PASSWORD_VALIDATORS: AUTH_PASSWORD_VALIDATORS = [] ACCOUNT_ADAPTER = "funkwhale_api.users.adapters.FunkwhaleAccountAdapter" CORS_ORIGIN_ALLOW_ALL = True @@ -705,6 +876,11 @@ REST_FRAMEWORK = { "NUM_PROXIES": env.int("NUM_PROXIES", default=1), } THROTTLING_ENABLED = env.bool("THROTTLING_ENABLED", default=True) +""" +Wether to enable throttling (also known as rate-limiting). Leaving this enabled is recommended +especially on public pods, to improve the quality of service. +""" + if THROTTLING_ENABLED: REST_FRAMEWORK["DEFAULT_THROTTLE_CLASSES"] = env.list( "THROTTLE_CLASSES", @@ -853,7 +1029,16 @@ THROTTLING_RATES = { "description": "Fetch remote objects", }, } +THROTTLING_RATES = THROTTLING_RATES +""" +Throttling rates for specific endpoints and features of the app. You can tweak this if you are +encountering to severe rate limiting issues or, on the contrary, if you want to reduce +the consumption on some endpoints. +Example: + +- ``signup=5/d,password-reset=2/d,anonymous-reports=5/d`` +""" BROWSABLE_API_ENABLED = env.bool("BROWSABLE_API_ENABLED", default=False) if BROWSABLE_API_ENABLED: @@ -874,24 +1059,48 @@ USE_X_FORWARDED_PORT = True # Wether we should use Apache, Nginx (or other) headers when serving audio files # Default to Nginx REVERSE_PROXY_TYPE = env("REVERSE_PROXY_TYPE", default="nginx") +""" +Depending on the reverse proxy used in front of your funkwhale instance, +the API will use different kind of headers to serve audio files + +Allowed values: ``nginx``, ``apache2`` +""" assert REVERSE_PROXY_TYPE in ["apache2", "nginx"], "Unsupported REVERSE_PROXY_TYPE" -# Which path will be used to process the internal redirection -# **DO NOT** put a slash at the end PROTECT_FILES_PATH = env("PROTECT_FILES_PATH", default="/_protected") +""" +Which path will be used to process the internal redirection to the reverse proxy +**DO NOT** put a slash at the end. +You shouldn't have to tweak this. +""" -# use this setting to tweak for how long you want to cache -# musicbrainz results. (value is in seconds) MUSICBRAINZ_CACHE_DURATION = env.int("MUSICBRAINZ_CACHE_DURATION", default=300) - -# Use this setting to change the musicbrainz hostname, for instance to -# use a mirror. The hostname can also contain a port number (so, e.g., -# "localhost:5000" is a valid name to set). +""" +How long to cache MusicBrainz results, in seconds +""" MUSICBRAINZ_HOSTNAME = env("MUSICBRAINZ_HOSTNAME", default="musicbrainz.org") +""" +Use this setting to change the musicbrainz hostname, for instance to +use a mirror. The hostname can also contain a port number. +Example: + +- ``mymusicbrainz.mirror`` +- ``localhost:5000`` + +""" # Custom Admin URL, use {% url 'admin:index' %} ADMIN_URL = env("DJANGO_ADMIN_URL", default="^api/admin/") +""" +Path to the Django admin area. + +Exemples: + +- `^api/admin/` +- `^api/mycustompath/` + +""" CSRF_USE_SESSIONS = True SESSION_ENGINE = "django.contrib.sessions.backends.cache" @@ -899,6 +1108,7 @@ SESSION_ENGINE = "django.contrib.sessions.backends.cache" # XXX: deprecated, see #186 PLAYLISTS_MAX_TRACKS = env.int("PLAYLISTS_MAX_TRACKS", default=250) + ACCOUNT_USERNAME_BLACKLIST = [ "funkwhale", "library", @@ -923,24 +1133,71 @@ ACCOUNT_USERNAME_BLACKLIST = [ "shared_inbox", "actor", ] + env.list("ACCOUNT_USERNAME_BLACKLIST", default=[]) - +""" +List of usernames that will be unavailable during registration. +""" EXTERNAL_REQUESTS_VERIFY_SSL = env.bool("EXTERNAL_REQUESTS_VERIFY_SSL", default=True) +""" +Wether to enforce HTTPS certificates verification when doing outgoing HTTP requests (typically with federation). +Disabling this is not recommended. +""" EXTERNAL_REQUESTS_TIMEOUT = env.int("EXTERNAL_REQUESTS_TIMEOUT", default=10) +""" +Default timeout for external requests. +""" # XXX: deprecated, see #186 API_AUTHENTICATION_REQUIRED = env.bool("API_AUTHENTICATION_REQUIRED", True) MUSIC_DIRECTORY_PATH = env("MUSIC_DIRECTORY_PATH", default=None) -# on Docker setup, the music directory may not match the host path, -# and we need to know it for it to serve stuff properly +""" +The path on your server where Funkwhale can import files using :ref:`in-place import +<in-place-import>`. It must be readable by the webserver and Funkwhale +api and worker processes. + +On docker installations, we recommend you use the default of ``/music`` +for this value. For non-docker installation, you can use any absolute path. +``/srv/funkwhale/data/music`` is a safe choice if you don't know what to use. + +.. note:: This path should not include any trailing slash + +.. warning:: + + You need to adapt your :ref:`reverse-proxy configuration<reverse-proxy-setup>` to + serve the directory pointed by ``MUSIC_DIRECTORY_PATH`` on + ``/_protected/music`` URL. + +""" MUSIC_DIRECTORY_SERVE_PATH = env( "MUSIC_DIRECTORY_SERVE_PATH", default=MUSIC_DIRECTORY_PATH ) +""" +Default: :attr:`MUSIC_DIRECTORY_PATH` + +When using Docker, the value of :attr:`MUSIC_DIRECTORY_PATH` in your containers +may differ from the real path on your host. Assuming you have the following directive +in your :file:`docker-compose.yml` file:: + + volumes: + - /srv/funkwhale/data/music:/music:ro + +Then, the value of :attr:`MUSIC_DIRECTORY_SERVE_PATH` should be +``/srv/funkwhale/data/music``. This must be readable by the webserver. + +On non-docker setup, you don't need to configure this setting. + +.. note:: This path should not include any trailing slash + +""" # When this is set to default=True, we need to reenable migration music/0042 # to ensure data is populated correctly on existing pods MUSIC_USE_DENORMALIZATION = env.bool("MUSIC_USE_DENORMALIZATION", default=False) + USERS_INVITATION_EXPIRATION_DAYS = env.int( "USERS_INVITATION_EXPIRATION_DAYS", default=14 ) +""" +Expiration delay in days, for user invitations. +""" VERSATILEIMAGEFIELD_RENDITION_KEY_SETS = { "square": [ @@ -964,40 +1221,84 @@ ACTOR_KEY_ROTATION_DELAY = env.int("ACTOR_KEY_ROTATION_DELAY", default=3600 * 48 SUBSONIC_DEFAULT_TRANSCODING_FORMAT = ( env("SUBSONIC_DEFAULT_TRANSCODING_FORMAT", default="mp3") or None ) - +""" +Default format for transcoding when using Subsonic API. +""" # extra tags will be ignored TAGS_MAX_BY_OBJ = env.int("TAGS_MAX_BY_OBJ", default=30) +""" +Maximum number of tags that can be associated with an object. Extra tags will be ignored. +""" FEDERATION_OBJECT_FETCH_DELAY = env.int( "FEDERATION_OBJECT_FETCH_DELAY", default=60 * 24 * 3 ) - +""" +Number of minutes before a remote object will be automatically refetched when accessed in the UI. +""" MODERATION_EMAIL_NOTIFICATIONS_ENABLED = env.bool( "MODERATION_EMAIL_NOTIFICATIONS_ENABLED", default=True ) +""" +Whether to enable email notifications to moderators and pods admins. +""" FEDERATION_AUTHENTIFY_FETCHES = True FEDERATION_SYNCHRONOUS_FETCH = env.bool("FEDERATION_SYNCHRONOUS_FETCH", default=True) FEDERATION_DUPLICATE_FETCH_DELAY = env.int( "FEDERATION_DUPLICATE_FETCH_DELAY", default=60 * 50 ) -# Delay in days after signup before we show the "support us" messages +""" +Delay, in seconds, between two manual fetch of the same remote object. +""" INSTANCE_SUPPORT_MESSAGE_DELAY = env.int("INSTANCE_SUPPORT_MESSAGE_DELAY", default=15) +""" +Delay in days after signup before we show the "support your pod" message +""" FUNKWHALE_SUPPORT_MESSAGE_DELAY = env.int("FUNKWHALE_SUPPORT_MESSAGE_DELAY", default=15) +""" +Delay in days after signup before we show the "support Funkwhale" message +""" # XXX Stable release: remove USE_FULL_TEXT_SEARCH = env.bool("USE_FULL_TEXT_SEARCH", default=True) MIN_DELAY_BETWEEN_DOWNLOADS_COUNT = env.int( "MIN_DELAY_BETWEEN_DOWNLOADS_COUNT", default=60 * 60 * 6 ) +""" +Minimum required period, in seconds, for two downloads of the same track by the same IP +or user to be recorded in statistics. +""" MARKDOWN_EXTENSIONS = env.list("MARKDOWN_EXTENSIONS", default=["nl2br", "extra"]) +""" +List of markdown extensions to enable. +Cf `<https://python-markdown.github.io/extensions/>`_ +""" LINKIFIER_SUPPORTED_TLDS = ["audio"] + env.list("LINKINFIER_SUPPORTED_TLDS", default=[]) +""" +Additional TLDs to support with our markdown linkifier. +""" EXTERNAL_MEDIA_PROXY_ENABLED = env.bool("EXTERNAL_MEDIA_PROXY_ENABLED", default=True) - -# By default, only people who subscribe to a podcast RSS will have access to it -# switch to "instance" or "everyone" to change that +""" +Wether to proxy attachment files hosted on third party pods and and servers. Keeping +this to true is recommended, to reduce leaking browsing information of your users, and +reduce the bandwidth used on remote pods. +""" PODCASTS_THIRD_PARTY_VISIBILITY = env("PODCASTS_THIRD_PARTY_VISIBILITY", default="me") +""" +By default, only people who subscribe to a podcast RSS will have access to their episodes. +switch to "instance" or "everyone" to change that. + +Changing it only affect new podcasts. +""" PODCASTS_RSS_FEED_REFRESH_DELAY = env.int( "PODCASTS_RSS_FEED_REFRESH_DELAY", default=60 * 60 * 24 ) +""" +Delay in seconds between to fetch of RSS feeds. Reducing this mean you'll receive new episodes faster, +but will require more resources. +""" # maximum items loaded through XML feed PODCASTS_RSS_FEED_MAX_ITEMS = env.int("PODCASTS_RSS_FEED_MAX_ITEMS", default=250) +""" +Maximum number of RSS items to load in each podcast feed. +""" diff --git a/docs/Dockerfile b/docs/Dockerfile index 1de9a3ede37b494c63d634db1dbf2e39000b1a15..0d868d5b2856a24ef6747b6b9f0c695623cba345 100644 --- a/docs/Dockerfile +++ b/docs/Dockerfile @@ -1,5 +1,5 @@ FROM python:3.6 RUN apt-get update && apt-get install -y graphviz -RUN pip install sphinx livereload sphinx_rtd_theme +RUN pip install sphinx livereload sphinx_rtd_theme django-environ django WORKDIR /app/docs diff --git a/docs/admin/configuration.rst b/docs/admin/configuration.rst index b3f2063780ba3b0b34f626ca24f1a14c464f1d40..c2bbd5f9f42a9e79bffb22b0abfafe1b442a32f3 100644 --- a/docs/admin/configuration.rst +++ b/docs/admin/configuration.rst @@ -1,13 +1,18 @@ Instance configuration ====================== -General configuration is achieved using two type of settings. +General configuration is achieved using two type of settings: +:ref:`environment variables <environment-variables>` and +:ref:`instance settings <instance-settings>`. + +.. _environment-variables: Environment variables --------------------- Those are located in your ``.env`` file, which you should have created -during installation. +during installation. A full list of available variables can be seen +:ref:`below <environment-variables>`. Options from this file are heavily commented, and usually target lower level and technical aspects of your instance, such as database credentials. @@ -33,7 +38,7 @@ and technical aspects of your instance, such as database credentials. Instance settings ----------------- -Those settings are stored in database and do not require a restart of your +These settings are stored in the database and do not require a restart of your instance after modification. They typically relate to higher level configuration, such your instance description, signup policy and so on. @@ -42,7 +47,7 @@ you have the required permissions. The URL is ``/manage/settings``, and you will also find a link to this page in the sidebar. If you plan to use acoustid and external imports -(e.g. with the youtube backends), you should edit the corresponding +(e.g. with the YouTube backends), you should edit the corresponding settings in this interface. .. note:: @@ -58,113 +63,114 @@ settings in this interface. Configuration reference ----------------------- -.. _setting-ACCOUNT_EMAIL_VERIFICATION_ENFORCE: - -``ACCOUNT_EMAIL_VERIFICATION_ENFORCE`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Determine wether users need to verify their email address before using the service. Enabling this can be useful -to reduce spam or bots accounts, however, you'll need to configure a SMTP server so that your users can receive the -verification emails. - -Note that regardless of the setting value, superusers created through the command line will never require verification. - -Default: ``false`` - -.. _setting-EMAIL_CONFIG: - -``EMAIL_CONFIG`` -^^^^^^^^^^^^^^^^ - -Determine how emails are sent. - -Default: ``consolemail://`` - -Possible values: - -- ``consolemail://``: Output sent emails to stdout -- ``dummymail://``: Completely discard sent emails -- ``smtp://user:password@youremail.host:25``: Send emails via SMTP via youremail.host on port 25, without encryption, authenticating as user "user" with password "password" -- ``smtp+ssl://user:password@youremail.host:465``: Send emails via SMTP via youremail.host on port 465, using SSL encryption, authenticating as user "user" with password "password" -- ``smtp+tls://user:password@youremail.host:587``: Send emails via SMTP via youremail.host on port 587, using TLS encryption, authenticating as user "user" with password "password" - -.. note:: - - If ``user`` or ``password`` contain special characters (eg. - ``noreply@youremail.host`` as ``user``), be sure to urlencode them, using - for example the command: - ``python3 -c 'import urllib.parse; print(urllib.parse.quote_plus("noreply@youremail.host"))'`` - (returns ``noreply%40youremail.host``) - - -.. _setting-DEFAULT_FROM_EMAIL: - -``DEFAULT_FROM_EMAIL`` -^^^^^^^^^^^^^^^^^^^^^^ - -The email address to use to send email. - -Default: ``Funkwhale <noreply@yourdomain>`` - -.. note:: - - Both the forms ``Funkwhale <noreply@yourdomain>`` and - ``noreply@yourdomain`` work. - - -.. _setting-MUSIC_DIRECTORY_PATH: - -``MUSIC_DIRECTORY_PATH`` -^^^^^^^^^^^^^^^^^^^^^^^^ - -Default: ``None`` - -The path on your server where Funkwhale can import files using :ref:`in-place import -<in-place-import>`. It must be readable by the webserver and Funkwhale -api and worker processes. - -On docker installations, we recommend you use the default of ``/music`` -for this value. For non-docker installation, you can use any absolute path. -``/srv/funkwhale/data/music`` is a safe choice if you don't know what to use. - -.. note:: This path should not include any trailing slash - -.. warning:: - - You need to adapt your :ref:`reverse-proxy configuration<reverse-proxy-setup>` to - serve the directory pointed by ``MUSIC_DIRECTORY_PATH`` on - ``/_protected/music`` URL. - -.. _setting-MUSIC_DIRECTORY_SERVE_PATH: - -``MUSIC_DIRECTORY_SERVE_PATH`` -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Default: :ref:`setting-MUSIC_DIRECTORY_PATH` - -When using Docker, the value of :ref:`setting-MUSIC_DIRECTORY_PATH` in your containers -may differ from the real path on your host. Assuming you have the following directive -in your :file:`docker-compose.yml` file:: - - volumes: - - /srv/funkwhale/data/music:/music:ro - -Then, the value of :ref:`setting-MUSIC_DIRECTORY_SERVE_PATH` should be -``/srv/funkwhale/data/music``. This must be readable by the webserver. - -On non-docker setup, you don't need to configure this setting. - -.. note:: This path should not include any trailing slash - -.. _setting-REVERSE_PROXY_TYPE: - -``REVERSE_PROXY_TYPE`` -^^^^^^^^^^^^^^^^^^^^^^ - -Default: ``nginx`` - -The type of reverse-proxy behind which Funkwhale is served. Either ``apache2`` -or ``nginx``. This is only used if you are using in-place import. +Pod +^^^ + +.. autodata:: config.settings.common.FUNKWHALE_HOSTNAME + :annotation: +.. autodata:: config.settings.common.FUNKWHALE_PROTOCOL + +Database and redis +^^^^^^^^^^^^^^^^^^ + +.. autodata:: config.settings.common.DATABASE_URL + :annotation: +.. autodata:: config.settings.common.DB_CONN_MAX_AGE +.. autodata:: config.settings.common.CACHE_URL + :annotation: +.. autodata:: config.settings.common.CELERY_BROKER_URL + :annotation: + +Accounts and registration +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autodata:: config.settings.common.ACCOUNT_EMAIL_VERIFICATION_ENFORCE + :annotation: +.. autodata:: config.settings.common.USERS_INVITATION_EXPIRATION_DAYS + :annotation: +.. autodata:: config.settings.common.DISABLE_PASSWORD_VALIDATORS + :annotation: +.. autodata:: config.settings.common.ACCOUNT_USERNAME_BLACKLIST + :annotation: +.. autodata:: config.settings.common.AUTH_LDAP_ENABLED + :annotation: + +Media storage and serving +^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. autodata:: config.settings.common.MEDIA_URL + :annotation: = https://mypod.audio/media/ +.. autodata:: config.settings.common.MEDIA_ROOT + :annotation: = /srv/funkwhale/data/media +.. autodata:: config.settings.common.PROXY_MEDIA + :annotation: = true +.. autodata:: config.settings.common.EXTERNAL_MEDIA_PROXY_ENABLED +.. autodata:: config.settings.common.ATTACHMENTS_UNATTACHED_PRUNE_DELAY + :annotation: = true +.. autodata:: config.settings.common.REVERSE_PROXY_TYPE +.. autodata:: config.settings.common.PROTECT_FILES_PATH + +Audio acquisition +^^^^^^^^^^^^^^^^^ + +.. autodata:: config.settings.common.MUSIC_DIRECTORY_PATH +.. autodata:: config.settings.common.MUSIC_DIRECTORY_SERVE_PATH + +S3 Storage +^^^^^^^^^^ + +.. autodata:: config.settings.common.AWS_QUERYSTRING_AUTH +.. autodata:: config.settings.common.AWS_QUERYSTRING_EXPIRE +.. autodata:: config.settings.common.AWS_ACCESS_KEY_ID +.. autodata:: config.settings.common.AWS_SECRET_ACCESS_KEY +.. autodata:: config.settings.common.AWS_STORAGE_BUCKET_NAME +.. autodata:: config.settings.common.AWS_S3_CUSTOM_DOMAIN +.. autodata:: config.settings.common.AWS_S3_ENDPOINT_URL +.. autodata:: config.settings.common.AWS_S3_REGION_NAME +.. autodata:: config.settings.common.AWS_LOCATION + +API configuration +^^^^^^^^^^^^^^^^^ + +.. autodata:: config.settings.common.THROTTLING_ENABLED +.. autodata:: config.settings.common.THROTTLING_RATES +.. autodata:: config.settings.common.ADMIN_URL +.. autodata:: config.settings.common.EXTERNAL_REQUESTS_VERIFY_SSL +.. autodata:: config.settings.common.EXTERNAL_REQUESTS_TIMEOUT + +Federation +^^^^^^^^^^ + +.. autodata:: config.settings.common.FEDERATION_OBJECT_FETCH_DELAY +.. autodata:: config.settings.common.FEDERATION_DUPLICATE_FETCH_DELAY + +Metadata +^^^^^^^^ + +.. autodata:: config.settings.common.TAGS_MAX_BY_OBJ +.. autodata:: config.settings.common.MUSICBRAINZ_HOSTNAME +.. autodata:: config.settings.common.MUSICBRAINZ_CACHE_DURATION + +Channels and podcasts +^^^^^^^^^^^^^^^^^^^^^ + +.. autodata:: config.settings.common.PODCASTS_RSS_FEED_REFRESH_DELAY +.. autodata:: config.settings.common.PODCASTS_RSS_FEED_MAX_ITEMS +.. autodata:: config.settings.common.PODCASTS_THIRD_PARTY_VISIBILITY + +Subsonic +^^^^^^^^ + +.. autodata:: config.settings.common.SUBSONIC_DEFAULT_TRANSCODING_FORMAT + +Other settings +^^^^^^^^^^^^^^ + +.. autodata:: config.settings.common.INSTANCE_SUPPORT_MESSAGE_DELAY +.. autodata:: config.settings.common.FUNKWHALE_SUPPORT_MESSAGE_DELAY +.. autodata:: config.settings.common.MIN_DELAY_BETWEEN_DOWNLOADS_COUNT +.. autodata:: config.settings.common.MARKDOWN_EXTENSIONS +.. autodata:: config.settings.common.LINKIFIER_SUPPORTED_TLDS User permissions ---------------- @@ -194,7 +200,7 @@ to users at ``/api/admin/users/user/``. Front-end settings ------------------ -We offer a basic mechanism to customize the behaviour and look and feel of Funkwhale's Web UI. +We offer a basic mechanism to customize the behavior and look and feel of Funkwhale's Web UI. To use any of the options below, you will need to create a custom JSON configuration file and serve it on ``https://yourinstanceurl/settings.json``. @@ -296,7 +302,7 @@ On nginx, add the following snippet to your vhost config:: alias /srv/funkwhale/custom; } -On apache, use the following one:: +On apache, use the following:: Alias /custom /srv/funkwhale/custom diff --git a/docs/conf.py b/docs/conf.py index 64fcfc8e0c19584c139538a93f3d13d2617aebed..ae278c5e27eb437e8944095df9e4959672e335e4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -26,6 +26,16 @@ sys.path.insert(0, os.path.abspath("../api")) import funkwhale_api # NOQA +FUNKWHALE_CONFIG = { + "FUNKWHALE_URL": "mypod.funkwhale", + "FUNKWHAL_PROTOCOL": "https", + "DATABASE_URL": "postgres://localhost:5432/db", + "AWS_ACCESS_KEY_ID": 'my_access_key', + "AWS_SECRET_ACCESS_KEY": 'my_secret_key', + "AWS_STORAGE_BUCKET_NAME": 'my_bucket', +} +for key, value in FUNKWHALE_CONFIG.items(): + os.environ[key] = value # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. @@ -35,8 +45,9 @@ import funkwhale_api # NOQA # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. -extensions = ["sphinx.ext.graphviz"] - +extensions = ["sphinx.ext.graphviz", "sphinx.ext.autodoc"] +autodoc_mock_imports = ["celery", "django_auth_ldap", "ldap"] +add_module_names = False # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] diff --git a/docs/serve.py b/docs/serve.py index 28e5020e692a3b2e8634e6c89e6e6d01ef93ed37..dfc12ebfcefb16b6dcf16b8815fc78ecb45b6f99 100644 --- a/docs/serve.py +++ b/docs/serve.py @@ -6,5 +6,5 @@ call(["python", "-m", "sphinx", ".", "/tmp/_build"]) from livereload import Server, shell server = Server() -server.watch(".", shell("python -m sphinx . /tmp/_build")) +server.watch("..", shell("python -m sphinx . /tmp/_build")) server.serve(root="/tmp/_build/", liveport=35730, port=8001, host="0.0.0.0")