From 0c50d8d667a50fe1c52e2a36436ac0e19e84f937 Mon Sep 17 00:00:00 2001 From: Giacomo Sanchietti Date: Fri, 12 Dec 2025 17:52:55 +0100 Subject: [PATCH 1/3] feat: add matrix2acrobits proxy --- README.md | 4 +- build-images.sh | 4 +- .../actions/configure-module/20configure | 67 +++++++++++++++++++ .../actions/configure-module/80start_services | 11 +++ .../configure-module/validate-input.json | 17 ++++- imageroot/actions/get-configuration/20read | 2 + .../get-configuration/validate-output.json | 14 ++++ imageroot/bin/configure-matrix | 16 ++++- imageroot/etc/state-include.conf | 1 + .../systemd/user/matrix2acrobits.service | 39 +++++++++++ imageroot/templates/matrix2acrobits.yaml | 15 +++++ 11 files changed, 185 insertions(+), 5 deletions(-) create mode 100644 imageroot/systemd/user/matrix2acrobits.service create mode 100644 imageroot/templates/matrix2acrobits.yaml diff --git a/README.md b/README.md index fa7e055..68c36a7 100644 --- a/README.md +++ b/README.md @@ -42,15 +42,17 @@ Launch `configure-module`, by setting the following parameters: - `lets_encrypt`: Set to `true` to enable automatic SSL certificate generation via Let's Encrypt for both domains. - `ldap_domain`: The LDAP domain name to be used for authentication. - `mail_from`: The mail sender address for notification mails +- `nethvoice_auth_url`: The NethVoice authentication URL to enable NethVoice integration, if set the `matrix2acrobits` service will be configured and started. Example: - api-cli run module/matrix1/configure-module --data '{"synapse_domain_name": "matrix.example.com", "element_domain_name": "chat.example.com", "cinny_domain_name": "cinny.example.com", "lets_encrypt": true, "ldap_domain": "users.example.com", "mail_from": "noreply@example.com"}' + api-cli run module/matrix1/configure-module --data '{"synapse_domain_name": "matrix.example.com", "element_domain_name": "chat.example.com", "cinny_domain_name": "cinny.example.com", "lets_encrypt": true, "ldap_domain": "users.example.com", "mail_from": "noreply@example.com", "nethvoice_auth_url": "https://nethvoice.nethserver.org/freepbx/rest/testextauth"}' The above command will: - Start and configure the Synapse Matrix homeserver with LDAP authentication - Deploy Element Web client and Cinny client configured to connect to the local Synapse instance - Set up Traefik routes for both domains with automatic SSL certificates +- Start Matrix2Acrobits Application Service Access your Matrix installation: - Matrix server: `https://matrix.example.com` diff --git a/build-images.sh b/build-images.sh index 3658430..dd16f57 100755 --- a/build-images.sh +++ b/build-images.sh @@ -37,10 +37,10 @@ buildah add "${container}" ui/dist /ui # Setup the entrypoint, ask to reserve three TCP ports and set a rootless container buildah config --entrypoint=/ \ --label="org.nethserver.authorizations=traefik@node:routeadm cluster:accountconsumer" \ - --label="org.nethserver.tcp-ports-demand=4" \ + --label="org.nethserver.tcp-ports-demand=5" \ --label="org.nethserver.rootfull=0" \ --label="org.nethserver.max-per-node=1" \ - --label="org.nethserver.images=docker.io/postgres:14.19-alpine ghcr.io/element-hq/synapse:v1.138.2 ghcr.io/element-hq/element-web:v1.12.0 ghcr.io/cinnyapp/cinny:v4.10.2" \ + --label="org.nethserver.images=docker.io/postgres:14.19-alpine ghcr.io/element-hq/synapse:v1.138.2 ghcr.io/element-hq/element-web:v1.12.0 ghcr.io/cinnyapp/cinny:v4.10.2 ghcr.io/nethesis/matrix2acrobits:0.0.4" \ "${container}" # Commit the image buildah commit "${container}" "${repobase}/${reponame}" diff --git a/imageroot/actions/configure-module/20configure b/imageroot/actions/configure-module/20configure index 7a59eb5..0141b3c 100755 --- a/imageroot/actions/configure-module/20configure +++ b/imageroot/actions/configure-module/20configure @@ -20,6 +20,7 @@ cinny_domain_name = request.get('cinny_domain_name', '') lets_encrypt = request.get('lets_encrypt', False) ldap_domain = request.get('ldap_domain', '') mail_from = request.get('mail_from', '') +nethvoice_auth_url = request.get('nethvoice_auth_url', '') if not synapse_domain_name: print("ERROR: Synapse_domain_name is required", file=sys.stderr) @@ -38,6 +39,7 @@ env_vars['CINNY_DOMAIN_NAME'] = cinny_domain_name env_vars['LDAP_DOMAIN'] = ldap_domain env_vars['MAIL_FROM'] = mail_from env_vars['LETS_ENCRYPT'] = '1' if lets_encrypt else '0' +env_vars['NETHVOICE_AUTH_URL'] = nethvoice_auth_url # Get allocated TCP ports from environment - NS8 allocates sequential ports tcp_ports = os.environ.get('TCP_PORTS', '20001').split(',') @@ -47,6 +49,7 @@ env_vars['SYNAPSE_PORT'] = tcp_ports[0] env_vars['ELEMENT_WEB_PORT'] = tcp_ports[1] env_vars['CINNY_PORT'] = tcp_ports[2] env_vars['POSTGRES_PORT'] = tcp_ports[3] +env_vars['M2A_PROXY_PORT'] = tcp_ports[4] # Write updated configuration agent.write_envfile("config.env", env_vars) @@ -117,3 +120,67 @@ else: # Bind the new domain, overriding previous values (unbind) agent.bind_user_domains([ldap_domain]) + + +# Enable or disable matrix2acrobits proxy routes depending on nethvoice_auth_url +m2a_port = env_vars.get('M2A_PROXY_PORT', '') +module_id = os.environ.get('MODULE_ID', 'matrix1') + +if nethvoice_auth_url: + proxy_url = 'http://127.0.0.1:' + (m2a_port if m2a_port else '8080') + # create /m2a route + response = agent.tasks.run( + agent_id=agent.resolve_agent_id('traefik@node'), + action='set-route', + data={ + 'instance': module_id + '-m2a', + 'name': module_id + '-m2a', + 'host': synapse_domain_name, + 'path': '/m2a', + 'url': proxy_url, + 'lets_encrypt': lets_encrypt, + 'strip_prefix': True, + }, + ) + agent.assert_exp(response['exit_code'] == 0) + + # create push notification route + response = agent.tasks.run( + agent_id=agent.resolve_agent_id('traefik@node'), + action='set-route', + data={ + 'instance': module_id + '-push', + 'name': module_id + '-push', + 'host': synapse_domain_name, + 'path': '/_matrix/push/v1/notify', + 'url': proxy_url, + 'lets_encrypt': lets_encrypt, + 'strip_prefix': False, + }, + ) + agent.assert_exp(response['exit_code'] == 0) + + m2a = { + 'MATRIX_HOMESERVER_URL': 'https://' + synapse_domain_name, + 'PROXY_PORT': env_vars['M2A_PROXY_PORT'], + 'AS_USER_ID': '@_acrobits_proxy:' + synapse_domain_name, + 'EXT_AUTH_URL': nethvoice_auth_url, + 'LOGLEVEL': 'info', + } + agent.write_envfile("m2a.env", m2a) +else: + response = agent.tasks.run( + agent_id=agent.resolve_agent_id('traefik@node'), + action='delete-route', + data={ + 'instance': module_id + '-m2a' + }, + ) + + response = agent.tasks.run( + agent_id=agent.resolve_agent_id('traefik@node'), + action='delete-route', + data={ + 'instance': module_id + '-push' + }, + ) diff --git a/imageroot/actions/configure-module/80start_services b/imageroot/actions/configure-module/80start_services index acfc6a0..f410641 100755 --- a/imageroot/actions/configure-module/80start_services +++ b/imageroot/actions/configure-module/80start_services @@ -51,3 +51,14 @@ if [[ -n ${CINNY_DOMAIN_NAME} ]]; then else systemctl --user stop cinny.service fi + +# Manage matrix2acrobits service +if [[ -n ${NETHVOICE_AUTH_URL} ]]; then + # Enable NethVoice Matrix Auth service + systemctl --user enable matrix2acrobits.service + # Restart NethVoice Matrix Auth service + systemctl --user restart matrix2acrobits.service +else + # Disable and stop NethVoice Matrix Auth service + systemctl --user disable --now matrix2acrobits.service +fi \ No newline at end of file diff --git a/imageroot/actions/configure-module/validate-input.json b/imageroot/actions/configure-module/validate-input.json index 1716942..16d74ac 100644 --- a/imageroot/actions/configure-module/validate-input.json +++ b/imageroot/actions/configure-module/validate-input.json @@ -10,7 +10,8 @@ "cinny_domain_name": "cinny.example.com", "lets_encrypt": true, "ldap_domain": "users.example.com", - "mail_from": "noreply@example.com" + "mail_from": "noreply@example.com", + "nethvoice_auth_url": "https://voice.nethserver.org/freepbx/rest/testextauth" } ], "type": "object", @@ -73,5 +74,19 @@ } ] } + , + "nethvoice_auth_url": { + "description": "NethVoice Authentication URL", + "oneOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "string", + "maxLength": 0 + } + ] + } } } diff --git a/imageroot/actions/get-configuration/20read b/imageroot/actions/get-configuration/20read index cb87dff..f876ea5 100755 --- a/imageroot/actions/get-configuration/20read +++ b/imageroot/actions/get-configuration/20read @@ -35,6 +35,7 @@ if os.path.exists("config.env"): config['mail_from'] = env_vars.get('MAIL_FROM', '') config['lets_encrypt'] = env_vars.get('LETS_ENCRYPT', '0') == '1' config['domains_list'] = domains + config['nethvoice_auth_url'] = env_vars.get('NETHVOICE_AUTH_URL', '') else: # No configuration file found, return empty configuration config['synapse_domain_name'] = '' @@ -44,5 +45,6 @@ else: config['mail_from'] = '' config['lets_encrypt'] = False config['domains_list'] = domains + config['nethvoice_auth_url'] = '' json.dump(config, fp=sys.stdout) diff --git a/imageroot/actions/get-configuration/validate-output.json b/imageroot/actions/get-configuration/validate-output.json index ac91efe..1d0ddff 100644 --- a/imageroot/actions/get-configuration/validate-output.json +++ b/imageroot/actions/get-configuration/validate-output.json @@ -50,5 +50,19 @@ } ] } + , + "nethvoice_auth_url": { + "description": "NethVoice Authentication URL", + "oneOf": [ + { + "type": "string", + "format": "uri" + }, + { + "type": "string", + "maxLength": 0 + } + ] + } } } diff --git a/imageroot/bin/configure-matrix b/imageroot/bin/configure-matrix index 1005ed0..bc3606e 100755 --- a/imageroot/bin/configure-matrix +++ b/imageroot/bin/configure-matrix @@ -47,8 +47,11 @@ if not os.path.exists(secrets_file): "REGISTRATION_SECRET": generate_secret(), "MACAROON_SECRET": generate_secret(), "FORM_SECRET": generate_secret(), - "SYNAPSE_SECRET": generate_secret() + "SYNAPSE_SECRET": generate_secret(), + "M2A_AS_TOKEN": generate_secret(64), + "M2A_HS_TOKEN": generate_secret(64) } + secrets_env["MATRIX_AS_TOKEN"] = secrets_env['M2A_AS_TOKEN'] agent.write_envfile(secrets_file, secrets_env) # Read generated secrets @@ -74,6 +77,17 @@ with open(f'{template_dir}/synapse-homeserver.yaml', 'r') as f: with open('synapse-config/homeserver.yaml', 'w') as f: f.write(synapse_config) + # If NETHVOICE_AUTH_URL is set, append the appservice lines to the generated homeserver.yaml + if env_vars.get('NETHVOICE_AUTH_URL'): + f.write('\napp_service_config_files:\n - /data/config/matrix2acrobits.yaml\n') + +# Expand matrix2acrobits.yaml into the synapse config directory +with open(f'{template_dir}/matrix2acrobits.yaml', 'r') as f: + matrix_a2a = substitute_env_vars(f.read(), env_vars) + +with open('synapse-config/matrix2acrobits.yaml', 'w') as f: + f.write(matrix_a2a) + # Copy log config with open(f'{template_dir}/synapse-log.config', 'r') as f: log_config = f.read() diff --git a/imageroot/etc/state-include.conf b/imageroot/etc/state-include.conf index 50e3541..6cbc378 100644 --- a/imageroot/etc/state-include.conf +++ b/imageroot/etc/state-include.conf @@ -3,3 +3,4 @@ state/database.env state/config.json volumes/synapse-data +volumes/matrix2acrobits-data diff --git a/imageroot/systemd/user/matrix2acrobits.service b/imageroot/systemd/user/matrix2acrobits.service new file mode 100644 index 0000000..c42b8a7 --- /dev/null +++ b/imageroot/systemd/user/matrix2acrobits.service @@ -0,0 +1,39 @@ +# +# Copyright (C) 2025 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +# +# This systemd unit starts matrix2acrobits client using Podman. +# +[Unit] +Description=matrix2acrobits Matrix Client +After=network-online.target synapse.service +Wants=synapse.service + +[Service] +Environment=PODMAN_SYSTEMD_UNIT=%n +EnvironmentFile=%S/state/environment +EnvironmentFile=%S/state/config.env +WorkingDirectory=%S/state +Restart=always +ExecStartPre=/bin/rm -f %t/matrix2acrobits.pid %t/matrix2acrobits.ctr-id +ExecStartPre=-runagent configure-matrix +ExecStart=/usr/bin/podman run \ + --detach \ + --conmon-pidfile=%t/matrix2acrobits.pid \ + --cidfile=%t/matrix2acrobits.ctr-id \ + --cgroups=no-conmon \ + --replace --name=matrix2acrobits \ + --network=host \ + --env-file=%S/state/matrix-secrets.env \ + --env-file=%S/state/m2a.env \ + --volume=matrix2acrobits-data:/tmp \ + ${MATRIX2ACROBITS_IMAGE} +ExecStop=/usr/bin/podman stop --ignore --cidfile %t/matrix2acrobits.ctr-id -t 10 +ExecStopPost=/usr/bin/podman rm --ignore -f --cidfile %t/matrix2acrobits.ctr-id +PIDFile=%t/matrix2acrobits.pid +Type=forking + +[Install] +WantedBy=default.target diff --git a/imageroot/templates/matrix2acrobits.yaml b/imageroot/templates/matrix2acrobits.yaml new file mode 100644 index 0000000..d094133 --- /dev/null +++ b/imageroot/templates/matrix2acrobits.yaml @@ -0,0 +1,15 @@ +id: acrobits-proxy +url: http://127.0.0.1:${M2A_PROXY_PORT} +as_token: "${M2A_AS_TOKEN}" +hs_token: "${M2A_HS_TOKEN}" +sender_localpart: _acrobits_proxy +namespaces: + users: + - exclusive: false + regex: '@.*' + aliases: + - exclusive: false + regex: '.*' + rooms: + - exclusive: false + regex: '.*' \ No newline at end of file From a30d4cfd2cb0048c8a0b40a1299ddc6558e3e027 Mon Sep 17 00:00:00 2001 From: Giacomo Sanchietti Date: Fri, 12 Dec 2025 18:14:12 +0100 Subject: [PATCH 2/3] feat: add UI for nethvoice auth url --- ui/public/i18n/ar/translation.json | 57 --------------------------- ui/public/i18n/de/translation.json | 57 --------------------------- ui/public/i18n/en/translation.json | 5 ++- ui/public/i18n/es/translation.json | 57 --------------------------- ui/public/i18n/eu/translation.json | 57 --------------------------- ui/public/i18n/it/translation.json | 54 ++++++++++++++++--------- ui/public/i18n/pt/translation.json | 57 --------------------------- ui/public/i18n/pt_BR/translation.json | 57 --------------------------- ui/public/i18n/ta/translation.json | 51 ------------------------ ui/public/i18n/uk/translation.json | 5 --- ui/src/views/Settings.vue | 16 ++++++++ 11 files changed, 55 insertions(+), 418 deletions(-) delete mode 100644 ui/public/i18n/ar/translation.json delete mode 100644 ui/public/i18n/de/translation.json delete mode 100644 ui/public/i18n/es/translation.json delete mode 100644 ui/public/i18n/eu/translation.json delete mode 100644 ui/public/i18n/pt/translation.json delete mode 100644 ui/public/i18n/pt_BR/translation.json delete mode 100644 ui/public/i18n/ta/translation.json delete mode 100644 ui/public/i18n/uk/translation.json diff --git a/ui/public/i18n/ar/translation.json b/ui/public/i18n/ar/translation.json deleted file mode 100644 index 753d1f1..0000000 --- a/ui/public/i18n/ar/translation.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "error": { - "403": "العملية غير مصرح بها", - "401": "مصادقة غير صالحة", - "generic_error": "حدث خطأ", - "error": "خطأ", - "network_timeout": "انتهت مهلة الخادم", - "404": "لم يتم العثور على المصدر", - "network_error": "خطا في الاتصال", - "validation_error": "خطئ في التحقق", - "cannot_retrieve_module_info": "لا يمكن استرداد معلومات الـ module", - "cannot_retrieve_installed_modules": "لا يمكن استرداد الـ moduls المثبتة" - }, - "common": { - "required": "مطلوب", - "work_in_progress": "جاري المعالجة", - "processing": "جاري المعالجة" - }, - "about": { - "title": "معلومات" - }, - "action": { - "list-backup-repositories": "قائمة مستودعات النسخ الاحتياطي", - "list-backups": "قائمة النسخ الاحتياطية", - "get-status": "معلومات الحالة", - "get-configuration": "احصل على التهيئة", - "configure-module": "تهيئة الـmodule", - "get-module-info": "احصل على معلومات الـ module", - "get-name": "احصل على إسم", - "list-installed-modules": "قائمة الـ modules المثبتة" - }, - "task": { - "cannot_create_task": "لا يمكن إنشاء المهمة {action}" - }, - "settings": { - "title": "إعدادات", - "save": "حفظ", - "configure_instance": "تهيئة {instance}", - "test_field": "حقل اختبار" - }, - "status": { - "title": "الحالة", - "app_instance": "تطبيق instance", - "services": "الخدمة | الخدمات", - "app_images": "صورة التطبيق | صور التطبيق", - "app_volumes": "حجم التطبيق | احجام التطبيق", - "mount": "تحميل (تلقيم)", - "no_services": "لا خدمات", - "installation_node": "تثبيت Node", - "node": "Node", - "name": "الاسم", - "size": "الحجم", - "created": "تم الانشاء", - "no_images": "لا يوجد صور", - "no_volumes": "لايوجد احجام" - } -} diff --git a/ui/public/i18n/de/translation.json b/ui/public/i18n/de/translation.json deleted file mode 100644 index a5b8265..0000000 --- a/ui/public/i18n/de/translation.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "error": { - "403": "Vorgang nicht autorisiert", - "cannot_retrieve_installed_modules": "Fehler beim Abruf installierter Module", - "401": "Authentifizierung fehlerhaft", - "cannot_retrieve_module_info": "Fehler beim Abruf der Modul-Info", - "generic_error": "Etwas ist schief gegangen", - "error": "Fehler", - "network_timeout": "Netzwerk-Zeitüberschreitung", - "404": "Ressource nicht gefunden", - "network_error": "Netzwerk-Fehler", - "validation_error": "Fehler bei der Validierung" - }, - "settings": { - "test_field": "Testfeld", - "configure_instance": "{instance} konfigurieren", - "save": "Speichern", - "title": "Einstellungen" - }, - "common": { - "required": "Erforderlich", - "work_in_progress": "In Bearbeitung", - "processing": "Verarbeitung läuft..." - }, - "status": { - "created": "Erstellt", - "size": "Größe", - "name": "Name", - "no_images": "Keine Abbilder", - "app_volumes": "App Volumen | App Volumen", - "services": "Dienst | Dienste", - "app_images": "App Abbild | App Abbilder", - "no_services": "Keine Dienste", - "app_instance": "App Instanz", - "mount": "Mount", - "title": "Status", - "installation_node": "Knoten der Installation", - "node": "Knoten", - "no_volumes": "Keine Volumen" - }, - "about": { - "title": "Über" - }, - "action": { - "list-backup-repositories": "Backup-Speicherorte auflisten", - "get-module-info": "Modul-Info abrufen", - "list-installed-modules": "Installierte Module auflisten", - "list-backups": "Backups auflisten", - "get-status": "Status abrufen", - "configure-module": "Modul konfigurieren", - "get-configuration": "Konfiguration abrufen", - "get-name": "Name abrufen" - }, - "task": { - "cannot_create_task": "Aufgabe {action} kann nicht erstellt werden" - } -} diff --git a/ui/public/i18n/en/translation.json b/ui/public/i18n/en/translation.json index 4280500..3839673 100644 --- a/ui/public/i18n/en/translation.json +++ b/ui/public/i18n/en/translation.json @@ -37,7 +37,10 @@ "mail_from_placeholder": "e.g. noreply@example.com", "mail_from_format": "Must be a valid mail address", "enabled": "Enabled", - "disabled": "Disabled" + "disabled": "Disabled", + "nethvoice_auth_url": "NethVoice authentication URL", + "nethvoice_auth_url_placeholder": "e.g. https://voice.nethserver.org/freepbx/rest/testextauth", + "nethvoice_auth_url_placeholder_tooltip": "The URL used to authenticate users against NethVoice. It should point to the FreePBX External Auth endpoint. If not set, NethVoice mobile app chat integration will be disabled." }, "about": { "title": "About" diff --git a/ui/public/i18n/es/translation.json b/ui/public/i18n/es/translation.json deleted file mode 100644 index da1961e..0000000 --- a/ui/public/i18n/es/translation.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "error": { - "403": "Operación no autorizada", - "cannot_retrieve_installed_modules": "No se pueden recuperar los módulos instalados", - "401": "Autentificación inválida", - "cannot_retrieve_module_info": "No se puede recuperar la información del módulo", - "generic_error": "Algo ha salido mal", - "error": "Error", - "network_timeout": "Tiempo de espera de la red agotado", - "404": "Recurso no encontrado", - "network_error": "Error en la red", - "validation_error": "Error en la validación" - }, - "settings": { - "test_field": "Campo de prueba", - "configure_instance": "Configurar {instance}", - "save": "Guardar", - "title": "Ajustes" - }, - "common": { - "required": "Obligatorio", - "work_in_progress": "Trabajando", - "processing": "Procesando..." - }, - "status": { - "created": "Creado", - "size": "Tamaño", - "name": "Nombre", - "no_images": "Sin imágenes", - "app_volumes": "Volumen de la aplicación| Volumen de las aplicaciones", - "services": "Servicio | Servicios", - "app_images": "Imagen de la aplicación| Imágenes de las aplicaciones", - "no_services": "Sin servicios", - "app_instance": "Instancia de la aplicación", - "mount": "Montar", - "title": "Estado", - "installation_node": "Nodo de la instalación", - "node": "Nodo", - "no_volumes": "Sin volumenes" - }, - "about": { - "title": "Acerca de" - }, - "action": { - "list-backup-repositories": "Mostrar repositorios de respaldo", - "get-module-info": "Obtener información del módulo", - "list-installed-modules": "Lista de módulos instalados", - "list-backups": "Mostrar copias de seguridad", - "get-status": "Obtener el estado", - "configure-module": "Configurar el módulo", - "get-configuration": "Obtener los ajustes", - "get-name": "Obtener el nombre" - }, - "task": { - "cannot_create_task": "No se puede crear la tarea {action}" - } -} diff --git a/ui/public/i18n/eu/translation.json b/ui/public/i18n/eu/translation.json deleted file mode 100644 index 38649d9..0000000 --- a/ui/public/i18n/eu/translation.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "error": { - "403": "Operation not authorized", - "cannot_retrieve_installed_modules": "Cannot retrieve installed modules", - "401": "Invalid authentication", - "cannot_retrieve_module_info": "Cannot retrieve module info", - "generic_error": "Something went wrong", - "error": "Error", - "network_timeout": "Network timeout", - "404": "Resource not found", - "network_error": "Network error", - "validation_error": "Validation error" - }, - "settings": { - "test_field": "Proba-eremua", - "configure_instance": "Configure {instance}", - "save": "Save", - "title": "Settings" - }, - "common": { - "required": "Beharrezkoa", - "work_in_progress": "Work in progress", - "processing": "Processing..." - }, - "status": { - "created": "Created", - "size": "Size", - "name": "Name", - "no_images": "No images", - "app_volumes": "App volume | App volumes", - "services": "Service | Services", - "app_images": "App image | App images", - "no_services": "No services", - "app_instance": "App instance", - "mount": "Mount", - "title": "Status", - "installation_node": "Installation node", - "node": "Node", - "no_volumes": "No volumes" - }, - "about": { - "title": "Honi buruz" - }, - "action": { - "list-backup-repositories": "List backup repositories", - "get-module-info": "Get module info", - "list-installed-modules": "List installed modules", - "list-backups": "List backups", - "get-status": "Get status", - "configure-module": "Configure module", - "get-configuration": "Get configuration", - "get-name": "Get name" - }, - "task": { - "cannot_create_task": "Cannot create task {action}" - } -} diff --git a/ui/public/i18n/it/translation.json b/ui/public/i18n/it/translation.json index 5e1952c..4fb8303 100644 --- a/ui/public/i18n/it/translation.json +++ b/ui/public/i18n/it/translation.json @@ -1,47 +1,63 @@ { "common": { "required": "Obbligatorio", - "processing": "In elaborazione…", - "work_in_progress": "In lavorazione" + "work_in_progress": "In lavorazione", + "processing": "In elaborazione…" }, "status": { - "node": "Nodo", - "services": "Servizio | Servizi", - "no_volumes": "Nessun volume", "title": "Stato", + "app_instance": "Istanza app", + "services": "Servizio | Servizi", + "app_images": "Immagine app | Immagini app", + "app_volumes": "Volume app | Volumi app", + "installation_node": "Nodo di installazione", + "node": "Nodo", "name": "Nome", "size": "Dimensione", "created": "Creato", + "mount": "Percorso di mount", "no_services": "Nessun servizio", "no_images": "Nessuna immagine", - "app_images": "Immagine app|Immagini app", - "app_instance": "Istanza app", - "app_volumes": "Volume app|Volumi app", - "installation_node": "Nodo di installazione", - "mount": "Percorso di mount" + "no_volumes": "Nessun volume" }, "settings": { - "save": "Salva", "title": "Impostazioni", "configure_instance": "Configura {instance}", - "test_field": "Campo di test" + "save": "Salva", + "synapse_domain_name": "Dominio server Matrix", + "synapse_domain_placeholder": "es. matrix.example.com", + "element_domain_name": "Dominio client Element", + "element_domain_placeholder": "es. chat.example.com", + "cinny_domain_name": "Dominio client Cinny", + "cinny_domain_placeholder": "es. cinny.example.com", + "ldap_domain": "Dominio LDAP", + "choose_ldap_domain": "Scegli il dominio LDAP", + "choose_the_ldap_domain_to_use": "Scegli il dominio LDAP da usare", + "mail_from": "Indirizzo mittente notifiche", + "mail_from_placeholder": "es. noreply@example.com", + "mail_from_format": "Deve essere un indirizzo email valido", + "enabled": "Abilitato", + "disabled": "Disabilitato", + "nethvoice_auth_url": "URL autenticazione NethVoice", + "nethvoice_auth_url_placeholder": "es. https://voice.nethserver.org/freepbx/rest/testextauth", + "nethvoice_auth_url_placeholder_tooltip": "L'URL usato per autenticare gli utenti contro NethVoice. Dovrebbe puntare all'endpoint FreePBX External Auth. Se non impostato, l'integrazione chat dell'app mobile NethVoice sarà disabilitata." }, "error": { "error": "Errore", - "404": "Risorsa non trovata", + "generic_error": "Qualcosa è andato storto", "validation_error": "Errore di validazione", "network_error": "Errore di rete", - "cannot_retrieve_installed_modules": "Impossibile elencare moduli installati", - "cannot_retrieve_module_info": "Impossibile recuperare info modulo", "network_timeout": "Timeout di rete", - "generic_error": "Qualcosa è andato storto", "401": "Autenticazione non valida", - "403": "Operazione non autorizzata" + "403": "Operazione non autorizzata", + "404": "Risorsa non trovata", + "cannot_retrieve_module_info": "Impossibile recuperare info modulo", + "cannot_retrieve_installed_modules": "Impossibile elencare moduli installati" }, "action": { - "configure-module": "Configura modulo", - "get-configuration": "Visualizza configurazione", "get-status": "Visualizza stato", + "get-configuration": "Visualizza configurazione", + "configure-module": "Configura modulo", "get-module-info": "Visualizza info modulo", "get-name": "Visualizza nome", "list-backup-repositories": "Elenca repository di backup", diff --git a/ui/public/i18n/pt/translation.json b/ui/public/i18n/pt/translation.json deleted file mode 100644 index 4ca6726..0000000 --- a/ui/public/i18n/pt/translation.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "error": { - "403": "Operation not authorized", - "cannot_retrieve_installed_modules": "Cannot retrieve installed modules", - "401": "Invalid authentication", - "cannot_retrieve_module_info": "Cannot retrieve module info", - "generic_error": "Something went wrong", - "error": "Error", - "network_timeout": "Network timeout", - "404": "Resource not found", - "network_error": "Network error", - "validation_error": "Validation error" - }, - "settings": { - "test_field": "Campo de teste", - "configure_instance": "Configure {instance}", - "save": "Save", - "title": "Settings" - }, - "common": { - "required": "Required", - "work_in_progress": "Work in progress", - "processing": "Processing..." - }, - "status": { - "created": "Created", - "size": "Size", - "name": "Name", - "no_images": "No images", - "app_volumes": "App volume | App volumes", - "services": "Service | Services", - "app_images": "App image | App images", - "no_services": "No services", - "app_instance": "App instance", - "mount": "Mount", - "title": "Status", - "installation_node": "Installation node", - "node": "Node", - "no_volumes": "No volumes" - }, - "about": { - "title": "About" - }, - "action": { - "list-backup-repositories": "List backup repositories", - "get-module-info": "Get module info", - "list-installed-modules": "List installed modules", - "list-backups": "List backups", - "get-status": "Get status", - "configure-module": "Configure module", - "get-configuration": "Get configuration", - "get-name": "Get name" - }, - "task": { - "cannot_create_task": "Cannot create task {action}" - } -} diff --git a/ui/public/i18n/pt_BR/translation.json b/ui/public/i18n/pt_BR/translation.json deleted file mode 100644 index bcdcb52..0000000 --- a/ui/public/i18n/pt_BR/translation.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "common": { - "required": "Required", - "work_in_progress": "Work in progress", - "processing": "Processing..." - }, - "status": { - "services": "Service | Services", - "app_instance": "App instance", - "title": "Status", - "created": "Created", - "size": "Size", - "name": "Name", - "no_images": "No images", - "app_volumes": "App volume | App volumes", - "app_images": "App image | App images", - "no_services": "No services", - "mount": "Mount", - "installation_node": "Installation node", - "node": "Node", - "no_volumes": "No volumes" - }, - "error": { - "403": "Operation not authorized", - "401": "Invalid authentication", - "generic_error": "Something went wrong", - "error": "Error", - "network_timeout": "Network timeout", - "404": "Resource not found", - "network_error": "Network error", - "validation_error": "Validation error", - "cannot_retrieve_installed_modules": "Cannot retrieve installed modules", - "cannot_retrieve_module_info": "Cannot retrieve module info" - }, - "about": { - "title": "About" - }, - "action": { - "list-backup-repositories": "List backup repositories", - "list-backups": "List backups", - "get-module-info": "Get module info", - "list-installed-modules": "List installed modules", - "get-status": "Get status", - "configure-module": "Configure module", - "get-configuration": "Get configuration", - "get-name": "Get name" - }, - "task": { - "cannot_create_task": "Cannot create task {action}" - }, - "settings": { - "title": "Settings", - "configure_instance": "Configure {instance}", - "save": "Save", - "test_field": "Test field" - } -} diff --git a/ui/public/i18n/ta/translation.json b/ui/public/i18n/ta/translation.json deleted file mode 100644 index 107113d..0000000 --- a/ui/public/i18n/ta/translation.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "error": { - "network_timeout": "பிணையம் நேரம் முடிந்தது", - "403": "செயல்பாடு அங்கீகரிக்கப்படவில்லை", - "cannot_retrieve_installed_modules": "நிறுவப்பட்ட தொகுதிகளை மீட்டெடுக்க முடியாது", - "401": "தவறான ஏற்பு", - "cannot_retrieve_module_info": "தொகுதி தகவலை மீட்டெடுக்க முடியாது", - "generic_error": "ஏதோ தவறு நடந்தது", - "error": "பிழை", - "404": "சான்று கிடைக்கவில்லை", - "network_error": "பிணைய பிழை", - "validation_error": "சரிபார்ப்பு பிழை" - }, - "common": { - "required": "தேவை", - "work_in_progress": "வேலை முன்னேற்றத்தில் உள்ளது" - }, - "status": { - "created": "உருவாக்கப்பட்டது", - "size": "அளவு", - "name": "பெயர்", - "no_images": "படங்கள் இல்லை", - "app_volumes": "பயன்பாட்டு தொகுதி | பயன்பாட்டு தொகுதிகள்", - "services": "பணி | சேவைகள்", - "app_images": "பயன்பாட்டு படம் | பயன்பாட்டு படங்கள்", - "no_services": "சேவைகள் இல்லை", - "app_instance": "பயன்பாட்டு நிகழ்வு", - "mount": "மவுண்ட்", - "title": "நிலை", - "installation_node": "நிறுவல் முனை", - "node": "கணு", - "no_volumes": "தொகுதிகள் இல்லை" - }, - "about": { - "title": "பற்றி" - }, - "action": { - "get-module-info": "தொகுதி தகவலைப் பெறுங்கள்", - "get-status": "அந்தச்தைப் பெறுங்கள்", - "configure-module": "தொகுதியை உள்ளமைக்கவும்", - "get-configuration": "உள்ளமைவைப் பெறுங்கள்", - "get-name": "பெயரைப் பெறுங்கள்" - }, - "task": { - "cannot_create_task": "பணியை உருவாக்க முடியாது {action}" - }, - "settings": { - "save": "சேமி", - "title": "அமைப்புகள்" - } -} diff --git a/ui/public/i18n/uk/translation.json b/ui/public/i18n/uk/translation.json deleted file mode 100644 index 3b7f2f2..0000000 --- a/ui/public/i18n/uk/translation.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "settings": { - "test_field": "Тестове поле" - } -} diff --git a/ui/src/views/Settings.vue b/ui/src/views/Settings.vue index 7849548..d3c115f 100644 --- a/ui/src/views/Settings.vue +++ b/ui/src/views/Settings.vue @@ -74,6 +74,18 @@ :invalid-message="error.mail_from" ref="mail_from" > + + + Date: Mon, 15 Dec 2025 14:21:06 +0100 Subject: [PATCH 3/3] feat: add matrixadm role --- imageroot/actions/create-module/30grants | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100755 imageroot/actions/create-module/30grants diff --git a/imageroot/actions/create-module/30grants b/imageroot/actions/create-module/30grants new file mode 100755 index 0000000..30b31d8 --- /dev/null +++ b/imageroot/actions/create-module/30grants @@ -0,0 +1,21 @@ +#!/bin/bash + +# +# Copyright (C) 2025 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +set -e # exit immediately if an error occurs +exec 1>&2 # ensure any output is sent to stderr + +# +# Allow other modules to configure Matrix and read the +# module configuration +# +redis-exec SADD "${AGENT_ID}/roles/matrixadm" "configure-module" "get-*" + +# +# Allow the instance itself to run some subtasks during restore and clone actions +# +redis-exec SADD "${AGENT_ID}/roles/selfadm" \ + "configure-module"