diff options
-rwxr-xr-x | bin/taler-config-generate | 13 | ||||
-rwxr-xr-x | bin/taler-deployment-hier | 60 | ||||
-rwxr-xr-x | bin/taler-deployment-prepare | 33 | ||||
-rw-r--r-- | buildbot/master.cfg | 4 | ||||
-rw-r--r-- | weblate/NOTES | 23 | ||||
-rw-r--r-- | weblate/settings.py | 871 | ||||
-rw-r--r-- | weblate/systemd-nonpriv/README | 3 | ||||
-rw-r--r-- | weblate/systemd-nonpriv/celery-weblate.service | 24 | ||||
-rw-r--r-- | weblate/systemd-nonpriv/uwsgi-weblate.service | 17 |
9 files changed, 979 insertions, 69 deletions
diff --git a/bin/taler-config-generate b/bin/taler-config-generate index 54a4012..33ac4a6 100755 --- a/bin/taler-config-generate +++ b/bin/taler-config-generate @@ -122,7 +122,6 @@ def config(obj): obj.cfg_put("backoffice-all", "uwsgi_unixpath", "$HOME/sockets/backoffice.uwsgi") obj.cfg_put("backoffice-all", "instances", "FSF default Tor") - obj.cfg_put("merchant", "wireformat", "test") obj.cfg_put("merchant", "serve", "unix") obj.cfg_put("merchant", "unixpath", "$HOME/sockets/merchant.http") obj.cfg_put("merchant", "wire_transfer_delay", "0 s") @@ -148,7 +147,7 @@ def config(obj): obj.cfg_put("merchant-exchange-{}".format(obj.currency), "master_key", obj.exchange_pub) obj.cfg_put("merchant-exchange-{}".format(obj.currency), "currency", obj.currency) - obj.cfg_put("merchant-exchange-{}".format(obj.currency), "base_url", "https://exchange.{}.taler.net/".format(obj.envname)) + obj.cfg_put("merchant-exchange-{}".format(obj.currency), "exchange_base_url", "https://exchange.{}.taler.net/".format(obj.envname)) obj.cfg_put("auditor", "serve", "unix") obj.cfg_put("auditor", "auditor_url", "https://auditor.{}.taler.net/service/".format(obj.envname)) @@ -244,12 +243,10 @@ def config(obj): obj.cfg_put("merchant-account-merchant", "wire_response", "${TALER_DATA_HOME}/merchant/wire/merchant.json") obj.cfg_put("merchant-account-merchant", "wire_file_mode", "770") - obj.cfg_put("merchant-account-merchant", "HONOR_default", "YES") - obj.cfg_put("merchant-account-merchant", "HONOR_Tor", "YES") - obj.cfg_put("merchant-account-merchant", "HONOR_GNUnet", "YES") - obj.cfg_put("merchant-account-merchant", "HONOR_Taler", "YES") - obj.cfg_put("merchant-account-merchant", "HONOR_FSF", "YES") - obj.cfg_put("merchant-account-merchant", "HONOR_Tutorial", "YES") + merchant_instance_names = ("default", "Tor", "GNUnet", "Taler", "FSF", "Tutorial") + for mi in merchant_instance_names: + obj.cfg_put("merchant-account-merchant", f"HONOR_{mi}", "YES") + obj.cfg_put("merchant-account-merchant", f"ACTIVE_{mi}", "YES") coin(obj, "ct_10", "0.10") coin(obj, "1", "1") diff --git a/bin/taler-deployment-hier b/bin/taler-deployment-hier deleted file mode 100755 index 52c3c25..0000000 --- a/bin/taler-deployment-hier +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash - -# @author Marcello Stanisci -# @brief Creates the directories hierarchy under $HOME/shared-data/. -# Note: shared-data/ must already exist and give execute -# permission to the group (test|demo). Also note that any -# program that creates files must set perms by its own! - -set -eu - -if test -z $TALER_ENV_NAME; then - echo Please run 'source ~/activate' first. - exit 1 -fi - -if ! test -e $HOME/.config/taler.conf; then - echo "Please generate config first (taler-deployment-config-generate)." - exit 1 -fi - -TALER_DATA=$(taler-config -s paths -o taler_deployment_data -f) - -# Check if shared-data/ is clean. -if test -e $TALER_DATA/hier.lock; then - echo "$TALER_DATA locked, exiting" - exit 0 -fi - -# Check if it's writeable. -if ! test -w $TALER_DATA; then - echo "Can't write under $TALER_DATA, please adjust permissions" - exit 0 -fi - -declare -A TALER_DIRS=( - [MERCHANT_WIRE]=$(dirname $(taler-config -s account-merchant -o wire_response -f)) - [MERCHANT]=$(dirname $(taler-config -s instance-default -o keyfile -f)) - - [EXCHANGE_WIREFEES]=$(taler-config -s exchangedb -o wirefee_base_dir -f) - [EXCHANGE_AUDITOR_REQUEST]=$(taler-config -s exchangedb -o auditor_inputs -f) - [EXCHANGE_WIRE]=$(basename $(taler-config -s account-1 -o wire_response -f)) - [EXCHANGE_OFFLINE_KEYS]=$(dirname $(taler-config -s exchange -o master_priv_file -f)) - [EXCHANGE_LIVE_KEYS]=$(taler-config -s -o keydir -f) - [EXCHANGE_AUDITORS]=$(taler-config -s exchangedb -o auditor_base_dir -f) - - [AUDITOR_REPORTS]=$(taler-config -s auditor -o reports -f) - [AUDITOR_OFFLINE_KEYS]=$(dirname $(taler-config -s auditor -o auditor_priv_file -f)) -) - -for dir in ${TALER_DIRS[@]}; do - - ## - # Ineffective for exchange's and auditor's privs paths - # as those were created when the configuration was generated - # (recall: this script is very dependent on taler.conf!) - mkdir -p $dir -done - -# All dirs will give only the group RWX perms. -touch $TALER_DATA/hier.lock diff --git a/bin/taler-deployment-prepare b/bin/taler-deployment-prepare index 8319161..3c9fe80 100755 --- a/bin/taler-deployment-prepare +++ b/bin/taler-deployment-prepare @@ -56,6 +56,20 @@ taler-exchange-dbinit ## Step 2: Copy key material and update denom keys ## +# For demo, make sure the link to shared data between demo-blue and demo-green is +# set up properly. +case $TALER_ENV_NAME in + demo) + echo "linking taler-data" + ln -sfT ~demo/shared-data ~/taler-data + # Check if we won't mess up permissions later + if [[ ! -g ~/taler-data ]]; then + echo "the shared-data directory should have the set-group-id bit set" + exit 1 + fi + ;; +esac + case $TALER_ENV_NAME in demo|test|int) EXCHANGE_PUB=$(gnunet-ecc -p "$HOME/deployment/private-keys/${TALER_ENV_NAME}-exchange-master.priv") @@ -95,7 +109,6 @@ chmod 750 "$HOME/.config" WIRE_RESPONSE=$(taler-config -s exchange-account-1 -o wire_response -f) taler-exchange-wire -chmod 770 "$WIRE_RESPONSE" ## @@ -111,3 +124,21 @@ case $TALER_ENV_NAME in echo "Not setting unsafe Exchange bank account password for env $TALER_ENV_NAME" ;; esac + + +## +## Step 5: Adjust some permissions +## + +case $TALER_ENV_NAME in + demo|test|int) + # Make sure the web server can read ~/local + chmod og+rx ~/local + + # Make sure that shared files created by this user + # are group writable and readable. + find ~/taler-data/ -user "$USER" -exec chmod g+rw {} \; + ;; + *) + ;; +esac diff --git a/buildbot/master.cfg b/buildbot/master.cfg index f8e1c22..43f778e 100644 --- a/buildbot/master.cfg +++ b/buildbot/master.cfg @@ -101,6 +101,10 @@ c["workers"] = [ ## # health checks performed by wallet-cli for test worker.Worker("taler-test-healthcheck", "taler-test-healthcheck-pass"), + + ## + # testing buildbot using the "weblatetest" user (for no specific reason except it exists) + worker.Worker("buildbot-weblatetest", "Gei8naiyox4uuhoo") ] # 'protocols' contains information about protocols which master diff --git a/weblate/NOTES b/weblate/NOTES new file mode 100644 index 0000000..70bad6b --- /dev/null +++ b/weblate/NOTES @@ -0,0 +1,23 @@ +For detailed information, checkout deployment.git/weblate + +Quick notes about this configuration: + +- weekly cron job runs ~/weblateBackup.sh + +- activate virtualenv: + . ~/weblate-env/bin/activate + +- `~/celery-weblate` is required by celery-weblate systemd service: https://docs.weblate.org/en/latest/admin/install.html#running-celery-as-system-service + +- `settings.py` symlink in /home/weblate goes to the active copy + +- Running celery-weblate as a systemd service with local user prvs: + systemd --user start |stop | status celery-weblate + +- Running uswgi as systemd service with local user privs. + +- Local systemd files belong in /home/weblate/.config/systemd/user + +- `DATA_DIR` symlink is referenced in nginx config. Should point to ~/weblate-env/lib/python3.8/site-packages/data + +- `webroot` ymlinks referenced in nginx config. At this time, I don't know what this should point to locally. It is the equivalent of /usr/shre/weblate if not running in virtualenv (see nginx config: ) diff --git a/weblate/settings.py b/weblate/settings.py new file mode 100644 index 0000000..7caca60 --- /dev/null +++ b/weblate/settings.py @@ -0,0 +1,871 @@ +# +# Copyright © 2012 - 2020 Michal Čihař <michal@cihar.com> +# +# This file is part of Weblate <https://weblate.org/> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. +# + + +import os +import platform +from logging.handlers import SysLogHandler + +# +# Django settings for Weblate project. +# + +DEBUG = False + +ADMINS = ( + ("Weblate Admin", "weblate@taler.net"), +) + +MANAGERS = ADMINS + +DATABASES = { + "default": { + # Use 'postgresql' or 'mysql'. + "ENGINE": "django.db.backends.postgresql", + # Database name. + "NAME": "weblate", + # Database user. + "USER": "weblate", + # Database password. + "PASSWORD": "Mei3ein7ooZieGhe", + # Set to empty string for localhost. + "HOST": "127.0.0.1", + # Set to empty string for default. + "PORT": "", + # Customizations for databases. + "OPTIONS": { + # In case of using an older MySQL server, + # which has MyISAM as a default storage + # 'init_command': 'SET storage_engine=INNODB', + # Uncomment for MySQL older than 5.7: + # 'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", + # Set emoji capable charset for MySQL: + # 'charset': 'utf8mb4', + # Change connection timeout in case you get MySQL gone away error: + # 'connect_timeout': 28800, + }, + } +} + +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Data directory +DATA_DIR = os.path.join(BASE_DIR, "data") + +# Local time zone for this installation. Choices can be found here: +# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name +# although not all choices may be available on all operating systems. +# In a Windows environment this must be set to your system time zone. +TIME_ZONE = "UTC" + +# Language code for this installation. All choices can be found here: +# http://www.i18nguy.com/unicode/language-identifiers.html +LANGUAGE_CODE = "en-us" + +LANGUAGES = ( + ("ar", "العربية"), + ("az", "Azərbaycan"), + ("be", "Беларуская"), + ("be@latin", "Biełaruskaja"), + ("bg", "Български"), + ("br", "Brezhoneg"), + ("ca", "Català"), + ("cs", "Čeština"), + ("da", "Dansk"), + ("de", "Deutsch"), + ("en", "English"), + ("el", "Ελληνικά"), + ("en-gb", "English (United Kingdom)"), + ("es", "Español"), + ("fi", "Suomi"), + ("fr", "Français"), + ("gl", "Galego"), + ("he", "עברית"), + ("hu", "Magyar"), + ("hr", "Hrvatski"), + ("id", "Indonesia"), + ("it", "Italiano"), + ("ja", "日本語"), + ("kab", "Taqbaylit"), + ("kk", "Қазақ тілі"), + ("ko", "한국어"), + ("nb", "Norsk bokmål"), + ("nl", "Nederlands"), + ("pl", "Polski"), + ("pt", "Português"), + ("pt-br", "Português brasileiro"), + ("ru", "Русский"), + ("sk", "Slovenčina"), + ("sl", "Slovenščina"), + ("sq", "Shqip"), + ("sr", "Српски"), + ("sv", "Svenska"), + ("tr", "Türkçe"), + ("uk", "Українська"), + ("zh-hans", "简体字"), + ("zh-hant", "正體字"), +) + +SITE_ID = 1 + +# If you set this to False, Django will make some optimizations so as not +# to load the internationalization machinery. +USE_I18N = True + +# If you set this to False, Django will not format dates, numbers and +# calendars according to the current locale. +USE_L10N = True + +# If you set this to False, Django will not use timezone-aware datetimes. +USE_TZ = True + +# URL prefix to use, please see documentation for more details +URL_PREFIX = "" + +# Absolute filesystem path to the directory that will hold user-uploaded files. +MEDIA_ROOT = os.path.join(DATA_DIR, "media") + +# URL that handles the media served from MEDIA_ROOT. Make sure to use a +# trailing slash. +MEDIA_URL = f"{URL_PREFIX}/media/" + +# Absolute path to the directory static files should be collected to. +# Don't put anything in this directory yourself; store your static files +# in apps' "static/" subdirectories and in STATICFILES_DIRS. +STATIC_ROOT = os.path.join(DATA_DIR, "static") + +# URL prefix for static files. +STATIC_URL = f"{URL_PREFIX}/static/" + +# Additional locations of static files +STATICFILES_DIRS = ( + # Put strings here, like "/home/html/static" or "C:/www/django/static". + # Always use forward slashes, even on Windows. + # Don't forget to use absolute paths, not relative paths. +) + +# List of finder classes that know how to find static files in +# various locations. +STATICFILES_FINDERS = ( + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "compressor.finders.CompressorFinder", +) + +# Make this unique, and don't share it with anybody. +# You can generate it using weblate/examples/generate-secret-key +SECRET_KEY = "+6e0m$7i_sr26pb4p%rhag9cd5zz&9))o+!d!s0_kht0rp3&6j" + +_TEMPLATE_LOADERS = [ + "django.template.loaders.filesystem.Loader", + "django.template.loaders.app_directories.Loader", +] +if not DEBUG: + _TEMPLATE_LOADERS = [("django.template.loaders.cached.Loader", _TEMPLATE_LOADERS)] +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "OPTIONS": { + "context_processors": [ + "django.contrib.auth.context_processors.auth", + "django.template.context_processors.debug", + "django.template.context_processors.i18n", + "django.template.context_processors.request", + "django.template.context_processors.csrf", + "django.contrib.messages.context_processors.messages", + "weblate.trans.context_processors.weblate_context", + ], + "loaders": _TEMPLATE_LOADERS, + }, + } +] + + +# GitHub username for sending pull requests. +# Please see the documentation for more details. +GITHUB_USERNAME = None + +# GitLab username for sending merge requests. +# Please see the documentation for more details. +GITLAB_USERNAME = None + +# Authentication configuration +AUTHENTICATION_BACKENDS = ( + "social_core.backends.email.EmailAuth", + # "social_core.backends.google.GoogleOAuth2", + # "social_core.backends.github.GithubOAuth2", + # "social_core.backends.bitbucket.BitbucketOAuth", + # "social_core.backends.suse.OpenSUSEOpenId", + # "social_core.backends.ubuntu.UbuntuOpenId", + # "social_core.backends.fedora.FedoraOpenId", + # "social_core.backends.facebook.FacebookOAuth2", + "weblate.accounts.auth.WeblateUserBackend", +) + +# Custom user model +AUTH_USER_MODEL = "weblate_auth.User" + +# Social auth backends setup +SOCIAL_AUTH_GITHUB_KEY = "" +SOCIAL_AUTH_GITHUB_SECRET = "" +SOCIAL_AUTH_GITHUB_SCOPE = ["user:email"] + +SOCIAL_AUTH_BITBUCKET_KEY = "" +SOCIAL_AUTH_BITBUCKET_SECRET = "" +SOCIAL_AUTH_BITBUCKET_VERIFIED_EMAILS_ONLY = True + +SOCIAL_AUTH_FACEBOOK_KEY = "" +SOCIAL_AUTH_FACEBOOK_SECRET = "" +SOCIAL_AUTH_FACEBOOK_SCOPE = ["email", "public_profile"] +SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {"fields": "id,name,email"} +SOCIAL_AUTH_FACEBOOK_API_VERSION = "3.1" + +SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = "" +SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = "" + +# Social auth settings +SOCIAL_AUTH_PIPELINE = ( + "social_core.pipeline.social_auth.social_details", + "social_core.pipeline.social_auth.social_uid", + "social_core.pipeline.social_auth.auth_allowed", + "social_core.pipeline.social_auth.social_user", + "weblate.accounts.pipeline.store_params", + "weblate.accounts.pipeline.verify_open", + "social_core.pipeline.user.get_username", + "weblate.accounts.pipeline.require_email", + "social_core.pipeline.mail.mail_validation", + "weblate.accounts.pipeline.revoke_mail_code", + "weblate.accounts.pipeline.ensure_valid", + "weblate.accounts.pipeline.remove_account", + "social_core.pipeline.social_auth.associate_by_email", + "weblate.accounts.pipeline.reauthenticate", + "weblate.accounts.pipeline.verify_username", + "social_core.pipeline.user.create_user", + "social_core.pipeline.social_auth.associate_user", + "social_core.pipeline.social_auth.load_extra_data", + "weblate.accounts.pipeline.cleanup_next", + "weblate.accounts.pipeline.user_full_name", + "weblate.accounts.pipeline.store_email", + "weblate.accounts.pipeline.notify_connect", + "weblate.accounts.pipeline.password_reset", +) +SOCIAL_AUTH_DISCONNECT_PIPELINE = ( + "social_core.pipeline.disconnect.allowed_to_disconnect", + "social_core.pipeline.disconnect.get_entries", + "social_core.pipeline.disconnect.revoke_tokens", + "weblate.accounts.pipeline.cycle_session", + "weblate.accounts.pipeline.adjust_primary_mail", + "weblate.accounts.pipeline.notify_disconnect", + "social_core.pipeline.disconnect.disconnect", + "weblate.accounts.pipeline.cleanup_next", +) + +# Custom authentication strategy +SOCIAL_AUTH_STRATEGY = "weblate.accounts.strategy.WeblateStrategy" + +# Raise exceptions so that we can handle them later +SOCIAL_AUTH_RAISE_EXCEPTIONS = True + +SOCIAL_AUTH_EMAIL_VALIDATION_FUNCTION = "weblate.accounts.pipeline.send_validation" +SOCIAL_AUTH_EMAIL_VALIDATION_URL = "{0}/accounts/email-sent/".format(URL_PREFIX) +SOCIAL_AUTH_LOGIN_ERROR_URL = "{0}/accounts/login/".format(URL_PREFIX) +SOCIAL_AUTH_EMAIL_FORM_URL = "{0}/accounts/email/".format(URL_PREFIX) +SOCIAL_AUTH_NEW_ASSOCIATION_REDIRECT_URL = "{0}/accounts/profile/#account".format( + URL_PREFIX +) +SOCIAL_AUTH_PROTECTED_USER_FIELDS = ("email",) +SOCIAL_AUTH_SLUGIFY_USERNAMES = True +SOCIAL_AUTH_SLUGIFY_FUNCTION = "weblate.accounts.pipeline.slugify_username" + +# Password validation configuration +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator" # noqa: E501, pylint: disable=line-too-long + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + "OPTIONS": {"min_length": 6}, + }, + {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, + {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, + {"NAME": "weblate.accounts.password_validation.CharsPasswordValidator"}, + {"NAME": "weblate.accounts.password_validation.PastPasswordsValidator"}, + # Optional password strength validation by django-zxcvbn-password + # { + # "NAME": "zxcvbn_password.ZXCVBNValidator", + # "OPTIONS": { + # "min_score": 3, + # "user_attributes": ("username", "email", "full_name") + # } + # }, +] + +# Allow new user registrations +REGISTRATION_OPEN = True + +# Shortcut for login required setting +LOGIN_REQUIRED = False + +# Middleware +MIDDLEWARE = [ + "weblate.middleware.ProxyMiddleware", + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "weblate.accounts.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "social_django.middleware.SocialAuthExceptionMiddleware", + "weblate.accounts.middleware.RequireLoginMiddleware", + "weblate.middleware.SecurityMiddleware", +] + +ROOT_URLCONF = "weblate.urls" + +# Django and Weblate apps +INSTALLED_APPS = [ + # Weblate apps on top to override Django locales and templates + "weblate.addons", + "weblate.auth", + "weblate.checks", + "weblate.formats", + "weblate.machinery", + "weblate.trans", + "weblate.lang", + "weblate.langdata", + "weblate.memory", + "weblate.screenshots", + "weblate.fonts", + "weblate.accounts", + "weblate.utils", + "weblate.vcs", + "weblate.wladmin", + "weblate", + # Optional: Git exporter + "weblate.gitexport", + # Standard Django modules + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.sites", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.admin.apps.SimpleAdminConfig", + "django.contrib.admindocs", + "django.contrib.sitemaps", + "django.contrib.humanize", + # Third party Django modules + "social_django", + "crispy_forms", + "compressor", + "rest_framework", + "rest_framework.authtoken", +] + +# Custom exception reporter to include some details +DEFAULT_EXCEPTION_REPORTER_FILTER = "weblate.trans.debug.WeblateExceptionReporterFilter" + +# Default logging of Weblate messages +# - to syslog in production (if available) +# - otherwise to console +# - you can also choose "logfile" to log into separate file +# after configuring it below + +# Detect if we can connect to syslog +HAVE_SYSLOG = False +if platform.system() != "Windows": + try: + handler = SysLogHandler(address="/dev/log", facility=SysLogHandler.LOG_LOCAL2) + handler.close() + HAVE_SYSLOG = True + except IOError: + HAVE_SYSLOG = False + +if DEBUG or not HAVE_SYSLOG: + DEFAULT_LOG = "console" +else: + DEFAULT_LOG = "syslog" + +# A sample logging configuration. The only tangible logging +# performed by this configuration is to send an email to +# the site admins on every HTTP 500 error when DEBUG=False. +# See http://docs.djangoproject.com/en/stable/topics/logging for +# more details on how to customize your logging configuration. +LOGGING = { + "version": 1, + "disable_existing_loggers": True, + "filters": {"require_debug_false": {"()": "django.utils.log.RequireDebugFalse"}}, + "formatters": { + "syslog": {"format": "weblate[%(process)d]: %(levelname)s %(message)s"}, + "simple": {"format": "%(levelname)s %(message)s"}, + "logfile": {"format": "%(asctime)s %(levelname)s %(message)s"}, + "django.server": { + "()": "django.utils.log.ServerFormatter", + "format": "[%(server_time)s] %(message)s", + }, + }, + "handlers": { + "mail_admins": { + "level": "ERROR", + "filters": ["require_debug_false"], + "class": "django.utils.log.AdminEmailHandler", + "include_html": True, + }, + "console": { + "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "simple", + }, + "django.server": { + "level": "INFO", + "class": "logging.StreamHandler", + "formatter": "django.server", + }, + "syslog": { + "level": "DEBUG", + "class": "logging.handlers.SysLogHandler", + "formatter": "syslog", + "address": "/dev/log", + "facility": SysLogHandler.LOG_LOCAL2, + }, + # Logging to a file + # 'logfile': { + # 'level':'DEBUG', + # 'class':'logging.handlers.RotatingFileHandler', + # 'filename': "/var/log/weblate/weblate.log", + # 'maxBytes': 100000, + # 'backupCount': 3, + # 'formatter': 'logfile', + # }, + }, + "loggers": { + "django.request": { + "handlers": ["mail_admins", DEFAULT_LOG], + "level": "ERROR", + "propagate": True, + }, + "django.server": { + "handlers": ["django.server"], + "level": "INFO", + "propagate": False, + }, + # Logging database queries + # "django.db.backends": { + # "handlers": [DEFAULT_LOG], + # "level": "DEBUG", + # }, + "weblate": {"handlers": [DEFAULT_LOG], "level": "DEBUG"}, + # Logging search operations + "weblate.search": {"handlers": [DEFAULT_LOG], "level": "INFO"}, + # Logging VCS operations + "weblate.vcs": {"handlers": [DEFAULT_LOG], "level": "WARNING"}, + # Python Social Auth + "social": {"handlers": [DEFAULT_LOG], "level": "DEBUG" if DEBUG else "WARNING"}, + # Django Authentication Using LDAP + "django_auth_ldap": { + "level": "DEBUG" if DEBUG else "WARNING", + "handlers": [DEFAULT_LOG], + }, + }, +} + +# Remove syslog setup if it's not present +if not HAVE_SYSLOG: + del LOGGING["handlers"]["syslog"] + +# List of machine translations +MT_SERVICES = ( +# "weblate.machinery.apertium.ApertiumAPYTranslation", +# "weblate.machinery.baidu.BaiduTranslation", +# "weblate.machinery.deepl.DeepLTranslation", +# "weblate.machinery.glosbe.GlosbeTranslation", +# "weblate.machinery.google.GoogleTranslation", +# "weblate.machinery.microsoft.MicrosoftCognitiveTranslation", +# "weblate.machinery.microsoftterminology.MicrosoftTerminologyService", +"weblate.machinery.mymemory.MyMemoryTranslation", +# "weblate.machinery.netease.NeteaseSightTranslation", +# "weblate.machinery.tmserver.AmagamaTranslation", +# "weblate.machinery.tmserver.TMServerTranslation", +# "weblate.machinery.yandex.YandexTranslation", +# "weblate.machinery.saptranslationhub.SAPTranslationHub", +# "weblate.machinery.youdao.YoudaoTranslation", +# "weblate.machinery.weblatetm.WeblateTranslation", +# "weblate.memory.machine.WeblateMemory", +) + +# Machine translation API keys + +# URL of the Apertium APy server +MT_APERTIUM_APY = None + +# DeepL API key +MT_DEEPL_KEY = None + +# Microsoft Cognitive Services Translator API, register at +# https://portal.azure.com/ +MT_MICROSOFT_COGNITIVE_KEY = None +MT_MICROSOFT_REGION = None + +# MyMemory identification email, see +# https://mymemory.translated.net/doc/spec.php +MT_MYMEMORY_EMAIL = "ops@taler.net" + +# Optional MyMemory credentials to access private translation memory +MT_MYMEMORY_USER = None +MT_MYMEMORY_KEY = None + +# Google API key for Google Translate API +MT_GOOGLE_KEY = None + +# Baidu app key and secret +MT_BAIDU_ID = None +MT_BAIDU_SECRET = None + +# Youdao Zhiyun app key and secret +MT_YOUDAO_ID = None +MT_YOUDAO_SECRET = None + +# Netease Sight (Jianwai) app key and secret +MT_NETEASE_KEY = None +MT_NETEASE_SECRET = None + +# API key for Yandex Translate API +MT_YANDEX_KEY = None + +# tmserver URL +MT_TMSERVER = None + +# SAP Translation Hub +MT_SAP_BASE_URL = None +MT_SAP_SANDBOX_APIKEY = None +MT_SAP_USERNAME = None +MT_SAP_PASSWORD = None +MT_SAP_USE_MT = True + +# Title of site to use +SITE_TITLE = "Weblate" + +# Whether site uses https +ENABLE_HTTPS = False + +# Use HTTPS when creating redirect URLs for social authentication, see +# documentation for more details: +# https://python-social-auth-docs.readthedocs.io/en/latest/configuration/settings.html#processing-redirects-and-urlopen +SOCIAL_AUTH_REDIRECT_IS_HTTPS = ENABLE_HTTPS + +# Make CSRF cookie HttpOnly, see documentation for more details: +# https://docs.djangoproject.com/en/1.11/ref/settings/#csrf-cookie-httponly +CSRF_COOKIE_HTTPONLY = True +CSRF_COOKIE_SECURE = ENABLE_HTTPS +# Store CSRF token in session +CSRF_USE_SESSIONS = True +# Customize CSRF failure view +CSRF_FAILURE_VIEW = "weblate.trans.views.error.csrf_failure" +SESSION_COOKIE_SECURE = True +ENABLE_HTTPS +# SSL redirect +SECURE_SSL_REDIRECT = ENABLE_HTTPS +# Sent referrrer only for same origin links +SECURE_REFERRER_POLICY = "same-origin" +# SSL redirect URL exemption list +SECURE_REDIRECT_EXEMPT = (r"healthz/$",) # Allowing HTTP access to health check +# Session cookie age (in seconds) +SESSION_COOKIE_AGE = 1209600 +# Increase allowed upload size +DATA_UPLOAD_MAX_MEMORY_SIZE = 50000000 + +# Some security headers +SECURE_BROWSER_XSS_FILTER = True +X_FRAME_OPTIONS = "DENY" +SECURE_CONTENT_TYPE_NOSNIFF = True + +# Optionally enable HSTS +SECURE_HSTS_SECONDS = 0 +SECURE_HSTS_PRELOAD = False +SECURE_HSTS_INCLUDE_SUBDOMAINS = False + +# URL of login +LOGIN_URL = "{0}/accounts/login/".format(URL_PREFIX) + +# URL of logout +LOGOUT_URL = "{0}/accounts/logout/".format(URL_PREFIX) + +# Default location for login +LOGIN_REDIRECT_URL = "{0}/".format(URL_PREFIX) + +# Anonymous user name +ANONYMOUS_USER_NAME = "anonymous" + +# Reverse proxy settings +IP_PROXY_HEADER = "HTTP_X_FORWARDED_FOR" +IP_BEHIND_REVERSE_PROXY = False +IP_PROXY_OFFSET = 0 + +# Sending HTML in mails +EMAIL_SEND_HTML = True + +# Subject of emails includes site title +EMAIL_SUBJECT_PREFIX = "[{0}] ".format(SITE_TITLE) + +# Enable remote hooks +ENABLE_HOOKS = True + +# Number of nearby messages to show in each direction +NEARBY_MESSAGES = 5 + +# By default the length of a given translation is limited to the length of +# the source string * 10 characters. Set this option to False to allow longer +# translations (up to 10.000 characters) +LIMIT_TRANSLATION_LENGTH_BY_SOURCE_LENGTH = True + +# Use simple language codes for default language/country combinations +SIMPLIFY_LANGUAGES = True + +# Render forms using bootstrap +CRISPY_TEMPLATE_PACK = "bootstrap3" + +# List of quality checks +# CHECK_LIST = ( +# "weblate.checks.same.SameCheck", +# "weblate.checks.chars.BeginNewlineCheck", +# "weblate.checks.chars.EndNewlineCheck", +# "weblate.checks.chars.BeginSpaceCheck", +# "weblate.checks.chars.EndSpaceCheck", +# "weblate.checks.chars.DoubleSpaceCheck", +# "weblate.checks.chars.EndStopCheck", +# "weblate.checks.chars.EndColonCheck", +# "weblate.checks.chars.EndQuestionCheck", +# "weblate.checks.chars.EndExclamationCheck", +# "weblate.checks.chars.EndEllipsisCheck", +# "weblate.checks.chars.EndSemicolonCheck", +# "weblate.checks.chars.MaxLengthCheck", +# "weblate.checks.chars.KashidaCheck", +# "weblate.checks.chars.PuctuationSpacingCheck", +# "weblate.checks.format.PythonFormatCheck", +# "weblate.checks.format.PythonBraceFormatCheck", +# "weblate.checks.format.PHPFormatCheck", +# "weblate.checks.format.CFormatCheck", +# "weblate.checks.format.PerlFormatCheck", +# "weblate.checks.format.JavaScriptFormatCheck", +# "weblate.checks.format.CSharpFormatCheck", +# "weblate.checks.format.JavaFormatCheck", +# "weblate.checks.format.JavaMessageFormatCheck", +# "weblate.checks.format.PercentPlaceholdersCheck", +# "weblate.checks.format.I18NextInterpolationCheck", +# "weblate.checks.angularjs.AngularJSInterpolationCheck", +# "weblate.checks.qt.QtFormatCheck", +# "weblate.checks.qt.QtPluralCheck", +# "weblate.checks.ruby.RubyFormatCheck", +# "weblate.checks.consistency.PluralsCheck", +# "weblate.checks.consistency.SamePluralsCheck", +# "weblate.checks.consistency.ConsistencyCheck", +# "weblate.checks.consistency.TranslatedCheck", +# "weblate.checks.chars.EscapedNewlineCountingCheck", +# "weblate.checks.chars.NewLineCountCheck", +# "weblate.checks.markup.BBCodeCheck", +# "weblate.checks.chars.ZeroWidthSpaceCheck", +# "weblate.checks.render.MaxSizeCheck", +# "weblate.checks.markup.XMLValidityCheck", +# "weblate.checks.markup.XMLTagsCheck", +# "weblate.checks.markup.MarkdownRefLinkCheck", +# "weblate.checks.markup.MarkdownLinkCheck", +# "weblate.checks.markup.MarkdownSyntaxCheck", +# "weblate.checks.markup.URLCheck", +# "weblate.checks.markup.SafeHTMLCheck", +# "weblate.checks.placeholders.PlaceholderCheck", +# "weblate.checks.placeholders.RegexCheck", +# "weblate.checks.source.OptionalPluralCheck", +# "weblate.checks.source.EllipsisCheck", +# "weblate.checks.source.MultipleFailingCheck", +# ) + +# List of automatic fixups +# AUTOFIX_LIST = ( +# "weblate.trans.autofixes.whitespace.SameBookendingWhitespace", +# "weblate.trans.autofixes.chars.ReplaceTrailingDotsWithEllipsis", +# "weblate.trans.autofixes.chars.RemoveZeroSpace", +# "weblate.trans.autofixes.chars.RemoveControlChars", +# ) + +# List of enabled addons +# WEBLATE_ADDONS = ( +# "weblate.addons.gettext.GenerateMoAddon", +# "weblate.addons.gettext.UpdateLinguasAddon", +# "weblate.addons.gettext.UpdateConfigureAddon", +# "weblate.addons.gettext.MsgmergeAddon", +# "weblate.addons.gettext.GettextCustomizeAddon", +# "weblate.addons.gettext.GettextAuthorComments", +# "weblate.addons.cleanup.CleanupAddon", +# "weblate.addons.consistency.LangaugeConsistencyAddon", +# "weblate.addons.discovery.DiscoveryAddon", +# "weblate.addons.flags.SourceEditAddon", +# "weblate.addons.flags.TargetEditAddon", +# "weblate.addons.flags.SameEditAddon", +# "weblate.addons.flags.BulkEditAddon", +# "weblate.addons.generate.GenerateFileAddon", +# "weblate.addons.json.JSONCustomizeAddon", +# "weblate.addons.properties.PropertiesSortAddon", +# "weblate.addons.git.GitSquashAddon", +# "weblate.addons.removal.RemoveComments", +# "weblate.addons.removal.RemoveSuggestions", +# "weblate.addons.resx.ResxUpdateAddon", +# "weblate.addons.yaml.YAMLCustomizeAddon", +# "weblate.addons.autotranslate.AutoTranslateAddon", +# ) + +# E-mail address that error messages come from. +SERVER_EMAIL = 'weblate@taler.net' + +# Default email address to use for various automated correspondence from +# the site managers. Used for registration emails. +DEFAULT_FROM_EMAIL = 'weblate@taler.net' + +# List of URLs your site is supposed to serve +ALLOWED_HOSTS = ["*"] + +# Configuration for caching +CACHES = { + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://127.0.0.1:6379/1", + # If redis is running on same host as Weblate, you might + # want to use unix sockets instead: + # "LOCATION": "unix:///var/run/redis/redis.sock?db=1", + "OPTIONS": { + "CLIENT_CLASS": "django_redis.client.DefaultClient", + "PARSER_CLASS": "redis.connection.HiredisParser", + "PASSWORD": None, + "CONNECTION_POOL_KWARGS": {}, + }, + "KEY_PREFIX": "weblate", + }, + "avatar": { + "BACKEND": "django.core.cache.backends.filebased.FileBasedCache", + "LOCATION": os.path.join(DATA_DIR, "avatar-cache"), + "TIMEOUT": 86400, + "OPTIONS": {"MAX_ENTRIES": 1000}, + }, +} + +# Store sessions in cache +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +# Store messages in session +MESSAGE_STORAGE = "django.contrib.messages.storage.session.SessionStorage" + +# REST framework settings for API +REST_FRAMEWORK = { + # Use Django's standard `django.contrib.auth` permissions, + # or allow read-only access for unauthenticated users. + "DEFAULT_PERMISSION_CLASSES": [ + # Require authentication for login required sites + "rest_framework.permissions.IsAuthenticated" + if LOGIN_REQUIRED + else "rest_framework.permissions.IsAuthenticatedOrReadOnly" + ], + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework.authentication.TokenAuthentication", + "weblate.api.authentication.BearerAuthentication", + "rest_framework.authentication.SessionAuthentication", + ), + "DEFAULT_THROTTLE_CLASSES": ( + "rest_framework.throttling.AnonRateThrottle", + "rest_framework.throttling.UserRateThrottle", + ), + "DEFAULT_THROTTLE_RATES": {"anon": "100/day", "user": "5000/hour"}, + "DEFAULT_PAGINATION_CLASS": ("rest_framework.pagination.PageNumberPagination"), + "PAGE_SIZE": 20, + "VIEW_DESCRIPTION_FUNCTION": "weblate.api.views.get_view_description", + "UNAUTHENTICATED_USER": "weblate.auth.models.get_anonymous", +} + +# Example for restricting access to logged in users +if LOGIN_REQUIRED: + LOGIN_REQUIRED_URLS = (r"/(.*)$",) + +# In such case you will want to include some of the exceptions +# LOGIN_REQUIRED_URLS_EXCEPTIONS = ( +# rf"{URL_PREFIX}/accounts/(.*)$", # Required for login +# rf"{URL_PREFIX}/admin/login/(.*)$", # Required for admin login +# rf"{URL_PREFIX}/static/(.*)$", # Required for development mode +# rf"{URL_PREFIX}/widgets/(.*)$", # Allowing public access to widgets +# rf"{URL_PREFIX}/data/(.*)$", # Allowing public access to data exports +# rf"{URL_PREFIX}/hooks/(.*)$", # Allowing public access to notification hooks +# rf"{URL_PREFIX}/healthz/$", # Allowing public access to health check +# rf"{URL_PREFIX}/api/(.*)$", # Allowing access to API +# rf"{URL_PREFIX}/js/i18n/$", # JavaScript localization +# rf"{URL_PREFIX}/contact/$", # Optional for contact form +# rf"{URL_PREFIX}/legal/(.*)$", # Optional for legal app +# ) + +# Silence some of the Django system checks +SILENCED_SYSTEM_CHECKS = [ + # We have modified django.contrib.auth.middleware.AuthenticationMiddleware + # as weblate.accounts.middleware.AuthenticationMiddleware + "admin.E408" +] + +# Celery worker configuration for testing +# CELERY_TASK_ALWAYS_EAGER = True +# CELERY_BROKER_URL = "memory://" +# CELERY_TASK_EAGER_PROPAGATES = True +# Celery worker configuration for production +CELERY_TASK_ALWAYS_EAGER = False +CELERY_BROKER_URL = "redis://localhost:6379" +CELERY_RESULT_BACKEND = CELERY_BROKER_URL + +# Celery settings, it is not recommended to change these +CELERY_WORKER_MAX_MEMORY_PER_CHILD = 200000 +CELERY_BEAT_SCHEDULE_FILENAME = os.path.join(DATA_DIR, "celery", "beat-schedule") +CELERY_TASK_ROUTES = { + "weblate.trans.tasks.auto_translate": {"queue": "translate"}, + "weblate.memory.tasks.*": {"queue": "memory"}, + "weblate.accounts.tasks.notify_*": {"queue": "notify"}, + "weblate.accounts.tasks.send_mails": {"queue": "notify"}, + "weblate.utils.tasks.settings_backup": {"queue": "backup"}, + "weblate.utils.tasks.database_backup": {"queue": "backup"}, + "weblate.wladmin.tasks.backup": {"queue": "backup"}, + "weblate.wladmin.tasks.backup_service": {"queue": "backup"}, +} + +# Enable plain database backups +DATABASE_BACKUP = "plain" + +# Enable auto updating +AUTO_UPDATE = False + +# PGP commits signing +WEBLATE_GPG_IDENTITY = 'Weblate <weblate@taler.net>' + +# Third party services integration +MATOMO_SITE_ID = None +MATOMO_URL = None +GOOGLE_ANALYTICS_ID = None +SENTRY_DSN = None +AKISMET_API_KEY = None + +# E-mail settings from https://docs.weblate.org/en/latest/admin/install.html?highlight=email +#EMAIL_HOST = 'localhost' +EMAIL_HOST_USER = 'weblate' +#EMAIL_PORT = '25' + +DEFAULT_COMMITER_NAME = 'Weblate' +DEFAULT_COMMITER_EMAIL = 'weblate@taler.net' +#COMMIT_PENDING_HOURS = 1 diff --git a/weblate/systemd-nonpriv/README b/weblate/systemd-nonpriv/README new file mode 100644 index 0000000..2330cf4 --- /dev/null +++ b/weblate/systemd-nonpriv/README @@ -0,0 +1,3 @@ +These files belong in /home/weblate/.config/systemd/user/ and are invoked with: + +systemctl --user start | stop *servicename* diff --git a/weblate/systemd-nonpriv/celery-weblate.service b/weblate/systemd-nonpriv/celery-weblate.service new file mode 100644 index 0000000..bf84a03 --- /dev/null +++ b/weblate/systemd-nonpriv/celery-weblate.service @@ -0,0 +1,24 @@ +[Unit] +Description=Celery Service (Weblate) +After=network.target + +[Service] +Type=forking +#User=weblate +#Group=weblate +EnvironmentFile=/home/weblate/celery-weblate +WorkingDirectory=/home/weblate +RuntimeDirectory=celery +RuntimeDirectoryPreserve=restart +LogsDirectory=celery +ExecStart=/bin/sh -c '${CELERY_BIN} multi start ${CELERYD_NODES} \ + -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} \ + --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS}' +ExecStop=/bin/sh -c '${CELERY_BIN} multi stopwait ${CELERYD_NODES} \ + --pidfile=${CELERYD_PID_FILE}' +ExecReload=/bin/sh -c '${CELERY_BIN} multi restart ${CELERYD_NODES} \ + -A ${CELERY_APP} --pidfile=${CELERYD_PID_FILE} \ + --logfile=${CELERYD_LOG_FILE} --loglevel=${CELERYD_LOG_LEVEL} ${CELERYD_OPTS}' + +[Install] +WantedBy=multi-user.target diff --git a/weblate/systemd-nonpriv/uwsgi-weblate.service b/weblate/systemd-nonpriv/uwsgi-weblate.service new file mode 100644 index 0000000..e04ec35 --- /dev/null +++ b/weblate/systemd-nonpriv/uwsgi-weblate.service @@ -0,0 +1,17 @@ +[Unit] +Description=Weblate uWSGI Service +After=multi-user.target + +[Service] +Type=simple +Restart=always +RestartSec=1 +#User=weblate +#Group=weblate +ExecStart=/home/weblate/weblate-env/bin/uwsgi --home=/home/weblate/weblate-env --module weblate.wsgi:application -s /home/weblate/uwsgi.sock --chmod-socket=660 +PIDFile=/home/weblate/pid/weblate-uwsgi.pid +WorkingDirectory=/home/weblate +LogsDirectory=logs + +[Install] +WantedBy=multi-user.target |