Huge role refactoring/cleanup. Other commits will propably follow. Because some bugs will exist. Still important for longrun and also for auto docs/help/slideshow generation

This commit is contained in:
2025-07-08 23:43:13 +02:00
parent 6b87a049d4
commit 563d5fd528
1242 changed files with 2301 additions and 1355 deletions

View File

@@ -0,0 +1,14 @@
# Installation and Configuration
## Initial Database Setup
After the first setup, run the following command to initialize the Listmonk database:
```bash
docker compose run --rm application ./listmonk --install
```
## Start Services
Use the following command to start Listmonk services:
```bash
docker-compose -p listmonk up -d --force-recreate
```

View File

@@ -0,0 +1,25 @@
# Listmonk
## Description
Elevate your email marketing with Listmonk, a high-performance, self-hosted newsletter and mailing list manager featuring a modern, intuitive dashboard. Listmonk empowers you with advanced analytics, efficient subscriber segmentation, and streamlined campaign management—all configurable via a flexible TOML configuration file.
## Overview
This role deploys Listmonk using Docker, ensuring a robust and scalable setup for your newsletter management needs. Listmonks architecture supports a dedicated PostgreSQL database and integration with an NGINX reverse proxy for secure access. To configure and manage your instance, use the provided configuration files:
- [Installation.md](./Installation.md)
- [Upgrade.md](./Upgrade.md)
## Features
- **High Performance:** Optimized for handling large-scale mailing lists and newsletters with rapid processing.
- **Modern Dashboard:** Enjoy a sleek, user-friendly interface for managing campaigns and analyzing performance.
- **Advanced Analytics:** Gain insights through detailed reporting on campaign metrics and subscriber behavior.
- **Flexible Configuration:** Easily customize settings such as database connections, admin credentials, and server configurations via a TOML file.
- **Robust Infrastructure:** Seamlessly integrates with PostgreSQL for reliable data management and supports deployment behind a reverse proxy.
## Further Resources
- [Listmonk Official Website](https://listmonk.app/)
- [Listmonk Installation Documentation](https://listmonk.app/docs/installation/)
- [Listmonk GitHub Repository](https://github.com/knadh/listmonk/)

View File

@@ -0,0 +1,5 @@
# Upgrade
```bash
docker compose run application ./listmonk --upgrade
```

View File

@@ -0,0 +1,3 @@
location /api/public/subscription {
return 403;
}

View File

@@ -0,0 +1,25 @@
---
galaxy_info:
author: "Kevin Veen-Birkenbach"
description: "Elevate your email marketing with Listmonk, a high-performance, self-hosted newsletter and mailing list manager featuring a modern dashboard, advanced analytics, and flexible configuration options."
license: "CyMaIS NonCommercial License (CNCL)"
license_url: "https://s.veen.world/cncl"
company: |
Kevin Veen-Birkenbach
Consulting & Coaching Solutions
https://www.veen.world
galaxy_tags:
- listmonk
- docker
- newsletter
- email marketing
- self-hosted
repository: "https://s.veen.world/cymais"
issue_tracker_url: "https://s.veen.world/cymaisissues"
documentation: "https://s.veen.world/cymais"
logo:
class: "fa-solid fa-list"
run_after:
- web-app-matomo
- web-app-keycloak
- web-app-mailu

View File

@@ -0,0 +1,15 @@
credentials:
administrator_password:
description: "Initial password for the Listmonk administrator account"
algorithm: "sha256"
validation: "^[a-f0-9]{64}$"
hcaptcha_site_key:
description: "Public site key used by Listmonk to render hCaptcha"
algorithm: "plain"
validation: "^[0-9a-zA-Z_-]{32,}$"
hcaptcha_secret:
description: "Private hCaptcha secret key for server-side verification"
algorithm: "plain"
validation: "^[0-9a-zA-Z_-]{32,}$"

View File

@@ -0,0 +1,11 @@
users:
administrator:
username: "administrator"
bounce:
username: "bounce"
roles:
- mail-bot
newsletter:
username: "newsletter"
roles:
- mail-bot

View File

@@ -0,0 +1,79 @@
---
- name: "include service-rdbms-central"
include_role:
name: service-rdbms-central
- name: Set nginx_docker_reverse_proxy_extra_configuration based on applications[application_id].public_api_activated
set_fact:
nginx_docker_reverse_proxy_extra_configuration: >-
{% if not applications[application_id].public_api_activated %}
{{ lookup('file', '{{ role_path }}/files/deactivate-public-api.conf') }}
{% else %}
""
{% endif %}
- name: "include role webserver-proxy-domain for {{application_id}}"
include_role:
name: webserver-proxy-domain
vars:
domain: "{{ domains | get_domain(application_id) }}"
http_port: "{{ ports.localhost.http[application_id] }}"
- name: add config.toml
template:
src: "config.toml.j2"
dest: "{{docker_compose.directories.config}}config.toml"
notify: docker compose up
- name: Check if listmonk database is already initialized
command: docker compose exec -T {{database_host}} psql -U {{database_username}} -d {{database_name}} -c "\dt"
register: db_tables
changed_when: false
failed_when: false
- name: Run Listmonk setup only if DB is empty
command:
cmd: docker compose run -T --rm application sh -c "yes | ./listmonk --install"
chdir: "{{docker_compose.directories.instance}}"
when: "'No relations found.' in db_tables.stdout"
- name: Build OIDC settings JSON
set_fact:
oidc_settings_json: >-
{{ {
"enabled": True,
"client_id": oidc.client.id,
"provider_url": oidc.client.issuer_url,
"client_secret": oidc.client.secret
} | to_json }}
- name: Update administrator email and password login in Listmonk
shell: |
docker exec -i {{ database_host }} psql \
-U {{ database_username }} \
-v ON_ERROR_STOP=1 \
-d {{ database_name }} << 'EOSQL'
UPDATE users
SET email = '{{ users.administrator.email }}',
password_login = {{ 'false' if applications[application_id].features.oidc else 'true' }}
WHERE username = 'administrator';
EOSQL
args:
executable: /bin/bash
- name: Apply all Listmonk settings
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
WHERE key = '{{ item.key }}';
EOSQL
args:
executable: /bin/bash
loop: "{{ listmonk_settings }}"
loop_control:
label: "{{ item.key }}"
when: item.when is not defined or item.when

View File

@@ -0,0 +1,24 @@
[app]
# Interface and port where the application will run its webserver. The default value
# of localhost will only listen to connections from the current machine. To
# listen on all interfaces use '0.0.0.0'. To listen on the default web address
# port, use port 80 (this will require running with elevated permissions).
address = "0.0.0.0:{{ container_port }}"
# Database.
[db]
host = "{{database_host}}"
port = {{database_port}}
user = "{{database_username}}"
password = "{{database_password}}"
# Ensure that this database has been created in Postgres.
database = "{{database_name}}"
ssl_mode = "disable"
max_open = 25
max_idle = 25
max_lifetime = "300s"
# Optional space separated Postgres DSN params. eg: "application_name=listmonk gssencmode=disable"
params = ""

View File

@@ -0,0 +1,16 @@
{% include 'roles/docker-compose/templates/base.yml.j2' %}
application:
{% set container_healthcheck = 'health' %}
{% include 'roles/docker-container/templates/base.yml.j2' %}
image: "{{ applications[application_id].images.listmonk }}"
ports:
- "127.0.0.1:{{ports.localhost.http[application_id]}}:{{ container_port }}"
volumes:
- {{docker_compose.directories.config}}config.toml:/listmonk/config.toml
{% include 'roles/docker-container/templates/networks.yml.j2' %}
{% include 'roles/docker-container/templates/depends_on/dmbs_excl.yml.j2' %}
{% include 'roles/docker-container/templates/healthcheck/wget.yml.j2' %}
{% include 'roles/docker-compose/templates/volumes-just-database.yml.j2' %}
{% include 'roles/docker-compose/templates/networks.yml.j2' %}

View File

@@ -0,0 +1,6 @@
TZ={{ HOST_TIMEZONE }}
# Administrator setup
LISTMONK_ADMIN_USER={{ applications[application_id].users.administrator.username }}
LISTMONK_ADMIN_PASSWORD={{ applications[application_id].credentials.administrator_password }}

View File

@@ -0,0 +1,17 @@
images:
listmonk: "listmonk/listmonk:latest"
public_api_activated: False # Security hole. Can be used for spaming
version: "latest" # Docker Image version
features:
matomo: true
css: false
portfolio_iframe: true
central_database: true
oidc: true
domains:
canonical:
- "newsletter.{{ primary_domain }}"
docker:
services:
database:
enabled: true

View File

@@ -0,0 +1,221 @@
application_id: "listmonk"
database_type: "postgres"
container_port: "9000"
listmonk_settings:
- key: "app.root_url"
value: '"{{ domains | get_url(application_id, web_protocol) }}"'
- key: "app.notify_emails"
value: "{{ [ users.administrator.email ] | to_json }}"
# OIDC integration (conditional)
- key: "security.oidc"
value: >-
{{ {
"enabled": True,
"client_id": oidc.client.id,
"provider_url": oidc.client.issuer_url,
"client_secret": oidc.client.secret
} | to_json }}
when: applications | is_feature_enabled('oidc',application_id)
# 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 }}"'
# SMTP servers
- key: "smtp"
value: >-
{{ [
{
"host": system_email.host,
"port": system_email.port,
"enabled": system_email.smtp,
"username": "no-reply",
"password": users['no-reply'].email,
"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: '"{{ HOST_LL }}"'
# - key: "messengers"
# value: '[]'
- key: "app.logo_url"
value: '"{{ service_provider.platform.logo }}"'
when: service_provider.platform.logo | bool
- key: "app.site_name"
value: '"{{ service_provider.company.titel }} Mailing list"'
# Enable the bounce module
- key: "bounce.enabled"
value: 'true'
# Configure POP3 mailbox for processing bounces
- key: "bounce.mailboxes"
value: >-
{{ [
{
"host": system_email.host,
"port": system_email.port,
"type": "pop",
"uuid": "471fd0e9-8c33-4e4a-9183-c4679699faca",
"enabled": true,
"username": users.bounce.email,
"password": users.bounce.mailu_token,
"return_path": users.bounce.email,
"tls_enabled": system_email.tls,
"auth_protocol": "userpass",
"scan_interval": "15m",
"tls_skip_verify": false
}
] | to_json }}
#
# - 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 <{{ users["no-reply"].email }}>"'
# - 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 }}"'
when: service_provider.platform.favicon | bool
# - 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'