mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2025-05-18 10:40:33 +02:00
Optimized Docker Matrix Role in Preparation for use on CyMaIS.Cloud Server
This commit is contained in:
parent
9c65c320f9
commit
76aef5949b
@ -0,0 +1,2 @@
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
@ -1,3 +1,4 @@
|
||||
CYMAIS_ENVIRONMENT: "production"
|
||||
|
||||
HOST_CURRENCY: "EUR"
|
||||
HOST_TIMEZONE: "UTC"
|
||||
|
@ -12,7 +12,7 @@ SSH_PORT={{ports.public.ssh[application_id]}}
|
||||
SSH_LISTEN_PORT=22
|
||||
DOMAIN={{domains[application_id]}}
|
||||
SSH_DOMAIN={{domains[application_id]}}
|
||||
RUN_MODE="{{run_mode}}"
|
||||
RUN_MODE="{{ 'dev' if (CYMAIS_ENVIRONMENT | lower) == 'development' else 'prod' }}"
|
||||
ROOT_URL="{{ web_protocol }}://{{domains[application_id]}}/"
|
||||
|
||||
# Mail Configuration
|
||||
|
@ -14,4 +14,6 @@ features:
|
||||
csp:
|
||||
flags:
|
||||
script-src:
|
||||
unsafe-inline: true
|
||||
style-src:
|
||||
unsafe-inline: true
|
5
roles/docker-matrix/Todo.md
Normal file
5
roles/docker-matrix/Todo.md
Normal file
@ -0,0 +1,5 @@
|
||||
# Todo
|
||||
- Enable Whatsapp by default
|
||||
- Enable Telegram by default
|
||||
- Enable Slack by default
|
||||
- Enable ChatGPT by default
|
2
roles/docker-matrix/filter_plugins/__init__.py
Normal file
2
roles/docker-matrix/filter_plugins/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
from pkgutil import extend_path
|
||||
__path__ = extend_path(__path__, __name__)
|
13
roles/docker-matrix/filter_plugins/bridge_filters.py
Normal file
13
roles/docker-matrix/filter_plugins/bridge_filters.py
Normal file
@ -0,0 +1,13 @@
|
||||
def filter_enabled_bridges(bridges, plugins):
|
||||
"""
|
||||
Return only those bridge definitions whose 'bridge_name' is set to True in plugins.
|
||||
:param bridges: list of dicts, each with a 'bridge_name' key
|
||||
:param plugins: dict mapping bridge_name to a boolean
|
||||
"""
|
||||
return [b for b in bridges if plugins.get(b['bridge_name'], False)]
|
||||
|
||||
class FilterModule(object):
|
||||
def filters(self):
|
||||
return {
|
||||
'filter_enabled_bridges': filter_enabled_bridges,
|
||||
}
|
@ -1,4 +1,13 @@
|
||||
---
|
||||
- name: Load bridges configuration
|
||||
include_vars:
|
||||
file: "bridges.yml"
|
||||
|
||||
- name: Filter enabled bridges and register as fact
|
||||
set_fact:
|
||||
bridges: "{{ bridges_configuration | filter_enabled_bridges(applications[application_id].plugins) }}"
|
||||
changed_when: false
|
||||
|
||||
- name: "include docker-central-database"
|
||||
include_role:
|
||||
name: docker-central-database
|
||||
|
@ -25,7 +25,11 @@ services:
|
||||
interval: 1m
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
{% if bridges | bool %}
|
||||
{% include 'templates/docker/container/depends-on-also-database.yml.j2' %}
|
||||
{% else %}
|
||||
{% include 'templates/docker/container/depends-on-just-database.yml.j2' %}
|
||||
{% endif %}
|
||||
{% for item in bridges %}
|
||||
mautrix-{{item.bridge_name}}:
|
||||
condition: service_healthy
|
||||
@ -61,47 +65,47 @@ services:
|
||||
retries: 3
|
||||
{% include 'templates/docker/container/networks.yml.j2' %}
|
||||
{% endfor %}
|
||||
# Deactivated chatgpt.
|
||||
# @todo needs to be reactivated as soon as bug is found
|
||||
# matrix-chatgpt-bot:
|
||||
# restart: {{docker_restart_policy}}
|
||||
# container_name: matrix-chatgpt
|
||||
# image: ghcr.io/matrixgpt/matrix-chatgpt-bot:latest
|
||||
# volumes:
|
||||
# - chatgpt_data:/storage
|
||||
# environment:
|
||||
# OPENAI_API_KEY: '{{applications[application_id].credentials.chatgpt_bridge_openai_api_key}}'
|
||||
# # Uncomment the next two lines if you are using Azure OpenAI API
|
||||
# # OPENAI_AZURE: 'false'
|
||||
# # CHATGPT_REVERSE_PROXY: 'your-completion-endpoint-here'
|
||||
# CHATGPT_CONTEXT: 'thread'
|
||||
# CHATGPT_API_MODEL: 'gpt-3.5-turbo'
|
||||
# # Uncomment and edit the next line if needed
|
||||
# # CHATGPT_PROMPT_PREFIX: 'Instructions:\nYou are ChatGPT, a large language model trained by OpenAI.'
|
||||
# # CHATGPT_IGNORE_MEDIA: 'false'
|
||||
# CHATGPT_REVERSE_PROXY: 'https://api.openai.com/v1/chat/completions'
|
||||
# # Uncomment and edit the next line if needed
|
||||
# # CHATGPT_TEMPERATURE: '0.8'
|
||||
# # Uncomment and edit the next line if needed
|
||||
# #CHATGPT_MAX_CONTEXT_TOKENS: '4097'
|
||||
# CHATGPT_MAX_PROMPT_TOKENS: '3000'
|
||||
# KEYV_BACKEND: 'file'
|
||||
# KEYV_URL: ''
|
||||
# KEYV_BOT_ENCRYPTION: 'false'
|
||||
# KEYV_BOT_STORAGE: 'true'
|
||||
# MATRIX_HOMESERVER_URL: 'https://{{domains.synapse}}'
|
||||
# MATRIX_BOT_USERNAME: '@chatgptbot:{{applications.matrix.server_name}}'
|
||||
# MATRIX_ACCESS_TOKEN: '{{ applications[application_id].credentials.chatgpt_bridge_access_token | default('') }}'
|
||||
# MATRIX_BOT_PASSWORD: '{{applications[application_id].credentials.chatgpt_bridge_user_password}}'
|
||||
# MATRIX_DEFAULT_PREFIX: '!chatgpt'
|
||||
# MATRIX_DEFAULT_PREFIX_REPLY: 'false'
|
||||
# #MATRIX_BLACKLIST: ''
|
||||
# MATRIX_WHITELIST: ':{{applications.matrix.server_name}}'
|
||||
# MATRIX_AUTOJOIN: 'true'
|
||||
# MATRIX_ENCRYPTION: 'true'
|
||||
# MATRIX_THREADS: 'true'
|
||||
# MATRIX_PREFIX_DM: 'false'
|
||||
# MATRIX_RICH_TEXT: 'true'
|
||||
{% if applications[application_id] | bool %}
|
||||
matrix-chatgpt-bot:
|
||||
restart: {{docker_restart_policy}}
|
||||
container_name: matrix-chatgpt
|
||||
image: ghcr.io/matrixgpt/matrix-chatgpt-bot:latest
|
||||
volumes:
|
||||
- chatgpt_data:/storage
|
||||
environment:
|
||||
OPENAI_API_KEY: '{{applications[application_id].credentials.chatgpt_bridge_openai_api_key}}'
|
||||
# Uncomment the next two lines if you are using Azure OpenAI API
|
||||
# OPENAI_AZURE: 'false'
|
||||
# CHATGPT_REVERSE_PROXY: 'your-completion-endpoint-here'
|
||||
CHATGPT_CONTEXT: 'thread'
|
||||
CHATGPT_API_MODEL: 'gpt-3.5-turbo'
|
||||
# Uncomment and edit the next line if needed
|
||||
# CHATGPT_PROMPT_PREFIX: 'Instructions:\nYou are ChatGPT, a large language model trained by OpenAI.'
|
||||
# CHATGPT_IGNORE_MEDIA: 'false'
|
||||
CHATGPT_REVERSE_PROXY: 'https://api.openai.com/v1/chat/completions'
|
||||
# Uncomment and edit the next line if needed
|
||||
# CHATGPT_TEMPERATURE: '0.8'
|
||||
# Uncomment and edit the next line if needed
|
||||
#CHATGPT_MAX_CONTEXT_TOKENS: '4097'
|
||||
CHATGPT_MAX_PROMPT_TOKENS: '3000'
|
||||
KEYV_BACKEND: 'file'
|
||||
KEYV_URL: ''
|
||||
KEYV_BOT_ENCRYPTION: 'false'
|
||||
KEYV_BOT_STORAGE: 'true'
|
||||
MATRIX_HOMESERVER_URL: 'https://{{domains.synapse}}'
|
||||
MATRIX_BOT_USERNAME: '@chatgptbot:{{applications.matrix.server_name}}'
|
||||
MATRIX_ACCESS_TOKEN: '{{ applications[application_id].credentials.chatgpt_bridge_access_token | default('') }}'
|
||||
MATRIX_BOT_PASSWORD: '{{applications[application_id].credentials.chatgpt_bridge_user_password}}'
|
||||
MATRIX_DEFAULT_PREFIX: '!chatgpt'
|
||||
MATRIX_DEFAULT_PREFIX_REPLY: 'false'
|
||||
#MATRIX_BLACKLIST: ''
|
||||
MATRIX_WHITELIST: ':{{applications.matrix.server_name}}'
|
||||
MATRIX_AUTOJOIN: 'true'
|
||||
MATRIX_ENCRYPTION: 'true'
|
||||
MATRIX_THREADS: 'true'
|
||||
MATRIX_PREFIX_DM: 'false'
|
||||
MATRIX_RICH_TEXT: 'true'
|
||||
{% endif %}
|
||||
|
||||
{% include 'templates/docker/compose/volumes.yml.j2' %}
|
||||
synapse_data:
|
||||
|
@ -61,7 +61,9 @@ oidc_providers:
|
||||
backchannel_logout_enabled: true
|
||||
{% endif %}
|
||||
|
||||
{% if bridges | bool %}
|
||||
app_service_config_files:
|
||||
{% for item in bridges %}
|
||||
- {{registration_file_folder}}{{item.bridge_name}}.registration.yaml
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endif %}
|
30
roles/docker-matrix/vars/bridges.yml
Normal file
30
roles/docker-matrix/vars/bridges.yml
Normal file
@ -0,0 +1,30 @@
|
||||
bridges_configuration:
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_whatsapp_bridge_database_password }}"
|
||||
database_username: "mautrix_whatsapp_bridge"
|
||||
database_name: "mautrix_whatsapp_bridge"
|
||||
bridge_name: "whatsapp"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_telegram_bridge_database_password }}"
|
||||
database_username: "mautrix_telegram_bridge"
|
||||
database_name: "mautrix_telegram_bridge"
|
||||
bridge_name: "telegram"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_signal_bridge_database_password }}"
|
||||
database_username: "mautrix_signal_bridge"
|
||||
database_name: "mautrix_signal_bridge"
|
||||
bridge_name: "signal"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_slack_bridge_database_password }}"
|
||||
database_username: "mautrix_slack_bridge"
|
||||
database_name: "mautrix_slack_bridge"
|
||||
bridge_name: "slack"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_facebook_bridge_database_password }}"
|
||||
database_username: "mautrix_facebook_bridge"
|
||||
database_name: "mautrix_facebook_bridge"
|
||||
bridge_name: "facebook"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_instagram_bridge_database_password }}"
|
||||
database_username: "mautrix_instagram_bridge"
|
||||
database_name: "mautrix_instagram_bridge"
|
||||
bridge_name: "instagram"
|
@ -3,7 +3,6 @@ users:
|
||||
administrator:
|
||||
username: "{{users.administrator.username}}" # Accountname of the matrix admin
|
||||
playbook_tags: "setup-all,start" # For the initial update use: install-all,ensure-matrix-users-created,start
|
||||
role: "compose" # Role to setup Matrix. Valid values: ansible, compose
|
||||
server_name: "{{primary_domain}}" # Adress for the account names etc.
|
||||
synapse:
|
||||
version: "latest"
|
||||
@ -30,3 +29,14 @@ csp:
|
||||
script-src:
|
||||
- "{{ domains.synapse }}"
|
||||
- "https://cdn.jsdelivr.net"
|
||||
plugins:
|
||||
# You need to enable them in the inventory file
|
||||
chatgpt: false
|
||||
facebook: false
|
||||
immesage: false
|
||||
instagram: false
|
||||
signal: false
|
||||
slack: false
|
||||
telegram: false
|
||||
whatsapp: false
|
||||
|
||||
|
@ -1,38 +1,5 @@
|
||||
---
|
||||
application_id: "matrix"
|
||||
application_id: "matrix"
|
||||
database_type: "postgres"
|
||||
registration_file_folder: "/data/"
|
||||
well_known_directory: "{{nginx.directories.data.well_known}}/matrix/"
|
||||
|
||||
bridges:
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_whatsapp_bridge_database_password }}"
|
||||
database_username: "mautrix_whatsapp_bridge"
|
||||
database_name: "mautrix_whatsapp_bridge"
|
||||
bridge_name: "whatsapp"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_telegram_bridge_database_password }}"
|
||||
database_username: "mautrix_telegram_bridge"
|
||||
database_name: "mautrix_telegram_bridge"
|
||||
bridge_name: "telegram"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_signal_bridge_database_password }}"
|
||||
database_username: "mautrix_signal_bridge"
|
||||
database_name: "mautrix_signal_bridge"
|
||||
bridge_name: "signal"
|
||||
|
||||
# Deactivated temporary, due to bug which is hard to find
|
||||
# @todo Reactivate
|
||||
# - database_password: "{{ applications[application_id].credentials.mautrix_slack_bridge_database_password }}"
|
||||
# database_username: "mautrix_slack_bridge"
|
||||
# database_name: "mautrix_slack_bridge"
|
||||
# bridge_name: "slack"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_facebook_bridge_database_password }}"
|
||||
database_username: "mautrix_facebook_bridge"
|
||||
database_name: "mautrix_facebook_bridge"
|
||||
bridge_name: "facebook"
|
||||
|
||||
- database_password: "{{ applications[application_id].credentials.mautrix_instagram_bridge_database_password }}"
|
||||
database_username: "mautrix_instagram_bridge"
|
||||
database_name: "mautrix_instagram_bridge"
|
||||
bridge_name: "instagram"
|
||||
well_known_directory: "{{nginx.directories.data.well_known}}/matrix/"
|
@ -96,7 +96,7 @@
|
||||
shell: >
|
||||
docker compose exec web bash -c "
|
||||
cd /app &&
|
||||
RAILS_ENV=production bundle exec rails runner \"
|
||||
RAILS_ENV={{ CYMAIS_ENVIRONMENT | lower }} bundle exec rails runner \"
|
||||
user = User.find_by(mail: '{{ users.administrator.email }}');
|
||||
if user.nil?;
|
||||
puts 'User with email {{ users.administrator.email }} not found.';
|
||||
|
@ -49,7 +49,7 @@
|
||||
- name: Set settings in OpenProject
|
||||
shell: >
|
||||
docker compose exec web bash -c "cd /app &&
|
||||
RAILS_ENV=production bundle exec rails runner \"Setting[:{{ item.key }}] = '{{ item.value }}'\""
|
||||
RAILS_ENV={{ CYMAIS_ENVIRONMENT | lower }} bundle exec rails runner \"Setting[:{{ item.key }}] = '{{ item.value }}'\""
|
||||
args:
|
||||
chdir: "{{ docker_compose.directories.instance }}"
|
||||
loop: "{{ openproject_rails_settings | dict2items }}"
|
||||
|
@ -149,7 +149,7 @@ def update_mastodon():
|
||||
Runs the database migration for Mastodon to ensure all required tables are up to date.
|
||||
"""
|
||||
print("Starting Mastodon database migration.")
|
||||
run_command("docker compose exec -T web bash -c 'RAILS_ENV=production bin/rails db:migrate'")
|
||||
run_command("docker compose exec -T web bash -c 'RAILS_ENV={{ CYMAIS_ENVIRONMENT | lower }} bin/rails db:migrate'")
|
||||
print("Mastodon database migration complete.")
|
||||
|
||||
def upgrade_listmonk():
|
||||
|
64
tests/unit/test_bridge_filters.py
Normal file
64
tests/unit/test_bridge_filters.py
Normal file
@ -0,0 +1,64 @@
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
# Add the filter_plugins directory from the docker-matrix role to the import path
|
||||
sys.path.insert(
|
||||
0,
|
||||
os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "../../roles/docker-matrix")
|
||||
),
|
||||
)
|
||||
|
||||
from filter_plugins.bridge_filters import filter_enabled_bridges
|
||||
|
||||
|
||||
class TestBridgeFilters(unittest.TestCase):
|
||||
def test_no_bridges_returns_empty_list(self):
|
||||
# When there are no bridges defined, result should be an empty list
|
||||
self.assertEqual(filter_enabled_bridges([], {}), [])
|
||||
|
||||
def test_all_bridges_disabled(self):
|
||||
# Define two bridges, but plugins dict has them disabled or missing
|
||||
bridges = [
|
||||
{'bridge_name': 'whatsapp', 'config': {}},
|
||||
{'bridge_name': 'telegram', 'config': {}},
|
||||
]
|
||||
plugins = {
|
||||
'whatsapp': False,
|
||||
'telegram': False,
|
||||
}
|
||||
result = filter_enabled_bridges(bridges, plugins)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
def test_some_bridges_enabled(self):
|
||||
# Only bridges with True in plugins should be returned
|
||||
bridges = [
|
||||
{'bridge_name': 'whatsapp', 'version': '1.0'},
|
||||
{'bridge_name': 'telegram', 'version': '1.0'},
|
||||
{'bridge_name': 'signal', 'version': '1.0'},
|
||||
]
|
||||
plugins = {
|
||||
'whatsapp': True,
|
||||
'telegram': False,
|
||||
'signal': True,
|
||||
}
|
||||
result = filter_enabled_bridges(bridges, plugins)
|
||||
expected = [
|
||||
{'bridge_name': 'whatsapp', 'version': '1.0'},
|
||||
{'bridge_name': 'signal', 'version': '1.0'},
|
||||
]
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_bridge_without_plugin_entry_defaults_to_disabled(self):
|
||||
# If a bridge_name is not present in plugins, it should be treated as disabled
|
||||
bridges = [
|
||||
{'bridge_name': 'facebook', 'enabled': True},
|
||||
]
|
||||
plugins = {} # no entries
|
||||
result = filter_enabled_bridges(bridges, plugins)
|
||||
self.assertEqual(result, [])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
@ -1,6 +1,16 @@
|
||||
# tests/unit/test_configuration_filters.py
|
||||
|
||||
import unittest
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(
|
||||
0,
|
||||
os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "../../")
|
||||
),
|
||||
)
|
||||
|
||||
from filter_plugins.configuration_filters import (
|
||||
is_feature_enabled,
|
||||
)
|
||||
|
@ -1,6 +1,16 @@
|
||||
import unittest
|
||||
import hashlib
|
||||
import base64
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(
|
||||
0,
|
||||
os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "../../")
|
||||
),
|
||||
)
|
||||
|
||||
from filter_plugins.csp_filters import FilterModule, AnsibleFilterError
|
||||
|
||||
class TestCspFilters(unittest.TestCase):
|
||||
|
@ -2,8 +2,12 @@ import os
|
||||
import sys
|
||||
import unittest
|
||||
|
||||
PROJECT_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../.."))
|
||||
sys.path.insert(0, PROJECT_ROOT)
|
||||
sys.path.insert(
|
||||
0,
|
||||
os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), "../../")
|
||||
),
|
||||
)
|
||||
|
||||
from filter_plugins.redirect_filters import FilterModule
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user