Optimized listmonk settings

This commit is contained in:
2025-04-24 13:11:25 +02:00
parent c9ab0cd7cc
commit 250f26e03c
26 changed files with 292 additions and 42 deletions

View File

@@ -22,8 +22,8 @@ services:
dockerfile: Dockerfile
# It doesn't compile yet with this parameters. @todo Fix it
args:
REACT_APP_PDS_URL: "https://{{domains.bluesky_api}}" # URL des PDS
REACT_APP_API_URL: "https://{{domains.bluesky_api}}" # API-URL des PDS
REACT_APP_PDS_URL: "{{ web_protocol }}://{{domains.bluesky_api}}" # URL des PDS
REACT_APP_API_URL: "{{ web_protocol }}://{{domains.bluesky_api}}" # API-URL des PDS
REACT_APP_SITE_NAME: "{{primary_domain | upper}} - Bluesky"
REACT_APP_SITE_DESCRIPTION: "Decentral Social "
ports:

View File

@@ -13,7 +13,7 @@ SSH_LISTEN_PORT=22
DOMAIN={{domains[application_id]}}
SSH_DOMAIN={{domains[application_id]}}
RUN_MODE="{{run_mode}}"
ROOT_URL="https://{{domains[application_id]}}/"
ROOT_URL="{{ web_protocol }}://{{domains[application_id]}}/"
# Mail Configuration
# @see https://docs.gitea.com/next/installation/install-with-docker#managing-deployments-with-environment-variables

View File

@@ -517,7 +517,7 @@
"/realms/{{realm}}/account/*"
],
"webOrigins": [
"https://{{domains.keycloak}}"
"{{ web_protocol }}://{{domains.keycloak}}"
],
"notBefore": 0,
"bearerOnly": false,
@@ -825,9 +825,9 @@
"clientId": "{{realm}}",
"name": "",
"description": "",
"rootUrl": "https://{{realm}}/",
"adminUrl": "https://{{realm}}/",
"baseUrl": "https://{{realm}}/",
"rootUrl": "{{ web_protocol }}://{{realm}}/",
"adminUrl": "{{ web_protocol }}://{{realm}}/",
"baseUrl": "{{ web_protocol }}://{{realm}}/",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": false,
@@ -837,10 +837,10 @@
{%- for application, domain in domains.items() %}
{%- if applications[application] is defined and (applications | get_oauth2_enabled(application) or applications | get_oidc_enabled(application)) %}
{%- if domain is string %}
{%- set _ = redirect_uris.append("https://{}/*".format(domain)) %}
{%- set _ = redirect_uris.append("{{ web_protocol }}://{}/*".format(domain)) %}
{%- else %}
{%- for d in domain %}
{%- set _ = redirect_uris.append("https://{}/*".format(d)) %}
{%- set _ = redirect_uris.append("{{ web_protocol }}://{}/*".format(d)) %}
{%- endfor %}
{%- endif %}
{%- endif %}
@@ -848,7 +848,7 @@
"redirectUris": {{ redirect_uris | tojson }},
"webOrigins": [
"https://*.{{primary_domain}}"
"{{ web_protocol }}://*.{{primary_domain}}"
],
"notBefore": 0,
"bearerOnly": false,
@@ -865,7 +865,7 @@
"oidc.ciba.grant.enabled": "false",
"client.secret.creation.time": "0",
"backchannel.logout.session.required": "true",
"post.logout.redirect.uris": "https://{{primary_domain}}/*##+",
"post.logout.redirect.uris": "{{ web_protocol }}://{{primary_domain}}/*##+",
"frontchannel.logout.session.required": "true",
"oauth2.device.authorization.grant.enabled": "false",
"display.on.consent.screen": "false",

View File

@@ -54,6 +54,7 @@
shell: |
docker exec -i {{ database_host }} psql \
-U {{ database_username }} \
-v ON_ERROR_STOP=1 \
-d {{ database_name }} << 'EOSQL'
UPDATE settings
SET value = '{{ item.value }}'::jsonb

View File

@@ -1,4 +1,4 @@
TZ=Etc/UTC
TZ={{timezone}}
# Administrator setup

View File

@@ -4,7 +4,10 @@ database_type: "postgres"
listmonk_settings:
- key: "app.root_url"
value: '"https://{{ domains[application_id] }}"'
value: '"{{ web_protocol }}://{{ domains[application_id] }}"'
- key: "app.notify_emails"
value: "{{ [ users.administrator.email ] | to_json }}"
# OIDC integration (conditional)
- key: "security.oidc"
@@ -17,9 +20,178 @@ listmonk_settings:
} | to_json }}
when: applications[application_id].features.oidc | bool
# hCaptcha toggles and credentials\ n - key: "security.enable_captcha"
value: "true"
# hCaptcha toggles and credentials
- key: "security.enable_captcha"
value: 'true'
- key: "security.captcha_key"
value: '"{{ applications[application_id].credentials.hcaptcha.site_key }}"'
- key: "security.captcha_secret"
value: '"{{ applications[application_id].credentials.hcaptcha.secret }}"'
value: '"{{ applications[application_id].credentials.hcaptcha.secret }}"'
# SMTP servers
- key: "smtp"
value: >-
{{ [
{
"host": system_email.host,
"port": system_email.port,
"enabled": system_email.smtp,
"username": system_email.username,
"password": system_email.password,
"tls_type": (
system_email.tls
| ternary("TLS",
system_email.start_tls
| ternary("STARTTLS","NONE")
)
),
"email_headers": [],
"hello_hostname": "",
"max_conns": 10,
"idle_timeout": "15s",
"wait_timeout": "5s",
"auth_protocol": "login",
"max_msg_retries": 2,
"tls_skip_verify": false
}
] | to_json }}
when: system_email.smtp | bool
- key: "app.lang"
value: '"{{ locale }}"'
# - key: "messengers"
# value: '[]'
- key: "app.logo_url"
value: '"{{ service_provider.platform.logo }}"'
- key: "app.site_name"
value: '"Mailing list"'
# - key: "bounce.enabled"
# value: 'false'
#
# - key: "upload.max_file_size"
# value: '5000'
#
# - key: "upload.s3.aws_secret_access_key"
# value: '""'
#
# - key: "app.batch_size"
# value: '1000'
- key: "app.from_email"
value: '"{{ service_provider.company.titel }} Newsletter <{{ system_email.from }}>"'
- key: "bounce.actions"
value: >-
{"hard": {"count": 1, "action": "blocklist"}, "soft": {"count": 2, "action": "none"}, "complaint": {"count": 1, "action": "blocklist"}}
- key: "app.concurrency"
value: '10'
- key: "app.favicon_url"
value: '"{{ service_provider.platform.favicon }}"'
# - key: "bounce.postmark"
# value: '{"enabled": false, "password": "", "username": ""}'
#
# - key: "upload.provider"
# value: '"filesystem"'
# - key: "app.message_rate"
# value: '10'
#
# - key: "bounce.mailboxes"
# value: >-
# [{"host": "pop.yoursite.com", "port": 995, "type": "pop", "uuid": "471fd0e9-8c33-4e4a-9183-c4679699faca", "enabled": false, "password": "password", "username": "username", "return_path": "bounce@listmonk.yoursite.com", "tls_enabled": true, "auth_protocol": "userpass", "scan_interval": "15m", "tls_skip_verify": false}]
# - key: "upload.s3.url"
# value: '"https://ap-south-1.s3.amazonaws.com"'
#
# - key: "upload.s3.bucket"
# value: '""'
#
# - key: "upload.s3.expiry"
# value: '"167h"'
- key: "app.check_updates"
value: 'true'
# - key: "upload.extensions"
# value: '["jpg", "jpeg", "png", "gif", "svg", "*"]'
#
# - key: "bounce.ses_enabled"
# value: 'false'
#
# - key: "privacy.allow_wipe"
# value: 'true'
#
# - key: "privacy.exportable"
# value: '["profile", "subscriptions", "campaign_views", "link_clicks"]'
#
# - key: "app.max_send_errors"
# value: '1000'
#
# - key: "bounce.forwardemail"
# value: '{"key": "", "enabled": false}'
#
# - key: "bounce.sendgrid_key"
# value: '""'
#
# - key: "privacy.allow_export"
# value: 'true'
#
# - key: "upload.s3.public_url"
# value: '""'
#
# - key: "upload.s3.bucket_path"
# value: '"/"'
#
# - key: "upload.s3.bucket_type"
# value: '"public"'
#
# - key: "app.cache_slow_queries"
# value: 'false'
#
# - key: "bounce.sendgrid_enabled"
# value: 'false'
#
# - key: "bounce.webhooks_enabled"
# value: 'false'
#
# - key: "privacy.domain_blocklist"
# value: '[]'
#
# - key: "privacy.allow_blocklist"
# value: 'true'
#
# - key: "privacy.record_optin_ip"
# value: 'false'
#
# - key: "app.enable_public_archive"
# value: 'true'
#
# - key: "privacy.allow_preferences"
# value: 'true'
#
# - key: "app.message_sliding_window"
# value: 'false'
#
# - key: "app.message_sliding_window_rate"
# value: '10000'
#
# - key: "app.enable_public_subscription_page"
# value: 'true'
#
# - key: "app.message_sliding_window_duration"
# value: '"1h"'
- key: "app.enable_public_archive_rss_content"
value: 'true'

View File

@@ -0,0 +1,2 @@
# Todo
- Implement create-mailu-user-and-token.yml for no-reply and bounce

View File

@@ -0,0 +1,69 @@
---
# tasks/create-mailu-user-and-token.yml
#
# Ensures a Mailu user exists and idempotently creates an API token for them.
#
# Required variables:
# mailu_compose_dir: Path to your docker-compose.yml directory
# mailu_user: Local part of the user (e.g., "alice")
# mailu_domain: Domain for the user (e.g., "example.com")
# mailu_password: Password for the new user
# mailu_api_base_url: Base URL of the Mailu API (e.g., "https://mail.example.com/api/v1")
# mailu_global_api_token: Global API token (from API_TOKEN environment variable)
#
# Optional variable:
# mailu_user_token: Pre-existing API token for the user (if already created)
- name: "Ensure Mailu user {{ mailu_user }}@{{ mailu_domain }} exists"
command: >
docker compose exec admin flask mailu user {{ mailu_user }} {{ mailu_domain }} '{{ mailu_password }}'
args:
chdir: "{{ mailu_compose_dir }}"
register: mailu_user_creation
failed_when: false
changed_when: mailu_user_creation.rc == 0 and 'User added' in mailu_user_creation.stdout
- name: "Fetch existing API tokens"
uri:
url: "{{ mailu_api_base_url }}/tokens"
method: GET
headers:
Authorization: "Bearer {{ mailu_global_api_token }}"
return_content: yes
register: mailu_tokens_response
failed_when: mailu_tokens_response.status not in [200]
- name: "Extract existing token info for {{ mailu_user }}"
set_fact:
mailu_user_existing_token: >
{{ mailu_tokens_response.json
| selectattr('comment', 'equalto', mailu_user)
| list
| first }}
- name: "Create API token for {{ mailu_user }} if none exists"
uri:
url: "{{ mailu_api_base_url }}/tokens"
method: POST
headers:
Authorization: "Bearer {{ mailu_global_api_token }}"
Content-Type: "application/json"
body_format: json
body:
comment: "{{ mailu_user }}"
ip: "0.0.0.0/0"
status_code: 201
register: mailu_token_creation
when: mailu_user_existing_token is not defined
- name: "Set mailu_user_token fact"
set_fact:
mailu_user_token: >
{{ (mailu_token_creation is defined)
? mailu_token_creation.json.secret
: (mailu_user_existing_token.secret | default('')) }}
# Note:
# - GET /tokens returns only metadata (id, comment, ip, created), not the secret itself.
# - The secret is returned only by the POST request and must be captured when created.
# - Store mailu_user_token securely (e.g., in Ansible Vault) for future use.

View File

@@ -1,11 +1,11 @@
{
"default_server_config": {
"m.homeserver": {
"base_url": "https://{{domains.matrix_synapse}}",
"base_url": "{{ web_protocol }}://{{domains.matrix_synapse}}",
"server_name": "{{domains.matrix_synapse}}"
},
"m.identity_server": {
"base_url": "https://{{primary_domain}}"
"base_url": "{{ web_protocol }}://{{primary_domain}}"
}
},
"brand": "Element",

View File

@@ -24,8 +24,8 @@ report_stats: true
macaroon_secret_key: "{{matrix_macaroon_secret_key}}"
form_secret: "{{matrix_form_secret}}"
signing_key_path: "/data/{{domains.matrix_synapse}}.signing.key"
web_client_location: "https://{{domains.matrix_element}}"
public_baseurl: "https://{{domains.matrix_synapse}}"
web_client_location: "{{ web_protocol }}://{{domains.matrix_element}}"
public_baseurl: "{{ web_protocol }}://{{domains.matrix_synapse}}"
trusted_key_servers:
- server_name: "matrix.org"
admin_contact: 'mailto:{{users.administrator.email}}'

View File

@@ -32,7 +32,7 @@ NEXTCLOUD_ADMIN_PASSWORD= "{{applications[application_id].users.administra
NEXTCLOUD_TRUSTED_DOMAINS= "{{domains[application_id]}}"
# Whitelist local docker gateway in Nextcloud to prevent brute-force throtteling
TRUSTED_PROXIES= "192.168.102.65"
OVERWRITECLIURL= "https://{{domains[application_id]}}"
OVERWRITECLIURL= "{{ web_protocol }}://{{domains[application_id]}}"
OVERWRITEPROTOCOL= "https"
# Redis Configuration

View File

@@ -18,4 +18,4 @@ nextcloud_system_config:
value: "{{domains[application_id]}}"
- parameter: "overwrite.cli.url"
value: "https://{{domains[application_id]}}"
value: "{{ web_protocol }}://{{domains[application_id]}}"

View File

@@ -9,7 +9,7 @@ whitelist_domains = [".{{primary_domain}}"]
# keycloak provider
client_secret = "{{oidc.client.secret}}"
client_id = "{{oidc.client.id}}"
redirect_url = "https://{{domain}}/oauth2/callback"
redirect_url = "{{ web_protocol }}://{{domain}}/oauth2/callback"
oidc_issuer_url = "{{oidc.client.issuer_url}}"
provider = "oidc"
provider_display_name = "Keycloak"

View File

@@ -26,7 +26,7 @@ accounts:
description: Follow {{ 'our' if service_provider.type == 'legal' else 'my' }} updates on Mastodon.
icon:
class: fa-brands fa-mastodon
url: "https://{{ service_provider.contact.mastodon.split('@')[2] }}/@{{ service_provider.contact.mastodon.split('@')[1] }}"
url: "{{ web_protocol }}://{{ service_provider.contact.mastodon.split('@')[2] }}/@{{ service_provider.contact.mastodon.split('@')[1] }}"
identifier: "{{service_provider.contact.mastodon}}"
iframe: {{ applications | get_features_iframe('mastodon') }}
@@ -51,7 +51,7 @@ accounts:
icon:
class: fa-solid fa-camera
identifier: "{{service_provider.contact.pixelfed}}"
url: "https://{{ service_provider.contact.pixelfed.split('@')[2] }}/@{{ service_provider.contact.pixelfed.split('@')[1] }}"
url: "{{ web_protocol }}://{{ service_provider.contact.pixelfed.split('@')[2] }}/@{{ service_provider.contact.pixelfed.split('@')[1] }}"
iframe: {{ applications | get_features_iframe('pixelfed') }}
{% endif %}
@@ -63,7 +63,7 @@ accounts:
icon:
class: fa-solid fa-video
identifier: "{{service_provider.contact.peertube}}"
url: "https://{{ service_provider.contact.peertube.split('@')[2] }}/@{{ service_provider.contact.peertube.split('@')[1] }}"
url: "{{ web_protocol }}://{{ service_provider.contact.peertube.split('@')[2] }}/@{{ service_provider.contact.peertube.split('@')[1] }}"
iframe: {{ applications | get_features_iframe('peertube') }}
{% endif %}
@@ -75,7 +75,7 @@ accounts:
icon:
class: fa-solid fa-blog
identifier: "{{service_provider.contact.wordpress}}"
url: "https://{{ service_provider.contact.wordpress.split('@')[2] }}/@{{ service_provider.contact.wordpress.split('@')[1] }}"
url: "{{ web_protocol }}://{{ service_provider.contact.wordpress.split('@')[2] }}/@{{ service_provider.contact.wordpress.split('@')[1] }}"
iframe: {{ applications | get_features_iframe('wordpress') }}
{% endif %}
@@ -97,7 +97,7 @@ accounts:
icon:
class: fas fa-network-wired
identifier: "{{service_provider.contact.friendica}}"
url: "https://{{ service_provider.contact.friendica.split('@')[2] }}/@{{ service_provider.contact.friendica.split('@')[1] }}"
url: "{{ web_protocol }}://{{ service_provider.contact.friendica.split('@')[2] }}/@{{ service_provider.contact.friendica.split('@')[1] }}"
iframe: {{ applications | get_features_iframe('friendica') }}
{% endif %}

View File

@@ -2,7 +2,7 @@
command: >
docker-compose exec -T -u www-data application
wp core install
--url="https://{{ domains[application_id][0] }}"
--url="{{ web_protocol }}://{{ domains[application_id][0] }}"
--title="{{ applications[application_id].title }}"
--admin_user="{{ applications[application_id].credentials.administrator.username }}"
--admin_password="{{ applications[application_id].credentials.administrator.password }}"

View File

@@ -11,7 +11,7 @@ discourse_settings:
- name: discourse_connect
key: url
value: "https://{{ domains.discourse }}"
value: "{{ web_protocol }}://{{ domains.discourse }}"
- name: discourse_connect
key: api-key
value: "{{ vault_discourse_api_key }}"

View File

@@ -2,6 +2,6 @@ YOURLS_DB_HOST: "{{database_host}}"
YOURLS_DB_USER: "{{database_username}}"
YOURLS_DB_PASS: "{{database_password}}"
YOURLS_DB_NAME: "{{database_name}}"
YOURLS_SITE: "https://{{domains[application_id]}}"
YOURLS_SITE: "{{ web_protocol }}://{{domains[application_id]}}"
YOURLS_USER: "{{applications.yourls.users.administrator.username}}"
YOURLS_PASS: "{{yourls_administrator_password}}"

View File

@@ -20,7 +20,7 @@ for filename in os.listdir(config_path):
parts = domain.split('.')
# Prepare the URL and expected status codes
url = f"https://{domain}"
url = f"{{ web_protocol }}://{domain}"
# Default: Expect status code 200 or 302 for a domain
expected_statuses = [200,302]

View File

@@ -28,7 +28,7 @@
- name: Add site to Matomo and get ID if not exists
uri:
url: "https://{{ domains.matomo }}/index.php"
url: "{{ web_protocol }}://{{ domains.matomo }}/index.php"
method: POST
body: "module=API&method=SitesManager.addSite&siteName={{ base_domain }}&urls=https://{{ base_domain }}&token_auth={{ applications.matomo.credentials.auth_token }}&format=json"
body_format: form-urlencoded

View File

@@ -1,2 +1,2 @@
base_domain: "{{ domain | regex_replace('^(?:.*\\.)?(.+\\..+)$', '\\1') }}"
verification_url: "https://{{domains.matomo}}/index.php?module=API&method=SitesManager.getSitesIdFromSiteUrl&url=https://{{base_domain}}&format=json&token_auth={{applications.matomo.credentials.auth_token}}"
verification_url: "{{ web_protocol }}://{{domains.matomo}}/index.php?module=API&method=SitesManager.getSitesIdFromSiteUrl&url=https://{{base_domain}}&format=json&token_auth={{applications.matomo.credentials.auth_token}}"

View File

@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="{{ locale }}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">