mirror of
				https://github.com/kevinveenbirkenbach/computer-playbook.git
				synced 2025-10-31 10:19:09 +00:00 
			
		
		
		
	feat(nextcloud): integrate Talk & Whiteboard; refactor to NEXTCLOUD_* vars; full-stack setup
config(ports): add Nextcloud websocket port (4003); canonical domains (nextcloud/talk/whiteboard) refactor: unify get_app_conf usage & Jinja spacing; migrate paths/handlers to new NEXTCLOUD_* vars feat(plugins): split plugin routines; configure Whiteboard via occ (URL + JWT) fix(oidc): use NEXTCLOUD_URL for logout; correct LDAP attribute mappings; add OIDC flavor switch feat: Whiteboard container & reverse-proxy location; Talk STUN/WS ports; Redis URL for Whiteboard chore: drop obsolete TODO; minor cleanups in oauth2-proxy, matrix, peertube, pgadmin, phpldapadmin, pixelfed, phpmyadmin security(schema): Bluesky jwt_secret now base64_prefixed_32; add Nextcloud whiteboard_jwt_secret db: normalize postgres image tag templating; central DB host checks spacing fixes ops: add full-stack bootstrap (certs, proxy, volumes); internal nginx config reload handler update refs: https://chatgpt.com/share/68b5f5b7-8d64-800f-b001-1241f818dc0e
This commit is contained in:
		| @@ -1,2 +0,0 @@ | ||||
| # Todo | ||||
| - Implement Collabora and Talk Supper . [See](https://www.youtube.com/watch?v=7cRmvTyt1ik) | ||||
| @@ -17,8 +17,9 @@ server: | ||||
|         - "{{ WEBSOCKET_PROTOCOL }}://collabora.{{ PRIMARY_DOMAIN }}" | ||||
|   domains: | ||||
|     canonical: | ||||
|       - "cloud.{{ PRIMARY_DOMAIN }}" | ||||
|       # talk:       "talk.{{ PRIMARY_DOMAIN }}" @todo needs to be activated | ||||
|       nextcloud:    "cloud.{{ PRIMARY_DOMAIN }}" | ||||
|       talk:         "talk.{{ PRIMARY_DOMAIN }}" | ||||
|       whiteboard:   "whiteboard.{{ PRIMARY_DOMAIN }}" | ||||
| docker: | ||||
|   volumes: | ||||
|     data: nextcloud_data | ||||
| @@ -45,8 +46,14 @@ docker: | ||||
|       name:     "nextcloud-talk" | ||||
|       image:    "nextcloud/aio-talk" | ||||
|       version:  "latest" | ||||
|       enabled:  false # Not enabled yet, because just implemented during refactoring and not tested yet. if tested activate | ||||
| oidc: | ||||
|       backup: | ||||
|         no_stop_required: false | ||||
|     whiteboard: | ||||
|       name:     "nextcloud-whiteboard" | ||||
|       image:    "ghcr.io/nextcloud-releases/whiteboard" | ||||
|       version:  "latest" | ||||
|       backup: | ||||
|         no_stop_required: true | ||||
|   enabled:  "{{ applications | get_app_conf('web-app-nextcloud', 'features.oidc', False, True) }}"   # Activate OIDC for Nextcloud | ||||
|   # floavor decides which OICD plugin should be used.  | ||||
|   # Available options: oidc_login, sociallogin | ||||
| @@ -72,7 +79,8 @@ performance: | ||||
|     opcache_memory_consumption: "{{ ((ansible_memtotal_mb | int) / 30)|int }}M" # Dynamic set memory consumption | ||||
|  | ||||
| plugins_enabled:                true                                            # Implemented for speeding up testing and debugging process. For productive environments keep it true and steer the apps via the plugins config | ||||
|  | ||||
| oidc: | ||||
|   flavor:                       "{{ _applications_nextcloud_oidc_flavor }}"  | ||||
| plugins:                                                   | ||||
| # List for Nextcloud Plugin Routine | ||||
| # Decides if plugins should be activated or deactivated | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| --- | ||||
| - name: restart nextcloud nginx service | ||||
|   command: | ||||
|     cmd: "docker exec {{ nextcloud_proxy_name }} nginx -s reload" | ||||
|     cmd: "docker exec {{ NEXTCLOUD_PROXY_CONTAINER }} nginx -s reload" | ||||
|   listen: restart nextcloud nginx service | ||||
|   ignore_errors: true # Ignoring if container is restarting | ||||
|   | ||||
| @@ -1,5 +1,8 @@ | ||||
| credentials: | ||||
|  | ||||
|   whiteboard_jwt_secret: | ||||
|     description: "Secret used for JWT signing" | ||||
|     algorithm: "base64_prefixed_32" | ||||
|     validation: "^base64:[A-Za-z0-9+/]{43}=$" | ||||
|   administrator_password: | ||||
|     description: "Initial password for the Nextcloud administrator (change immediately and enable 2FA)" | ||||
|     algorithm: "sha256" | ||||
|   | ||||
							
								
								
									
										37
									
								
								roles/web-app-nextcloud/tasks/01_fullstack.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								roles/web-app-nextcloud/tasks/01_fullstack.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| - name: "include role for {{ application_id }} to receive certs & do modification routines for '{{ domain }}:{{ port }}'" | ||||
|   include_role: | ||||
|     name: sys-util-csp-cert | ||||
|  | ||||
| - name: create nextcloud proxy configuration file | ||||
|   template:  | ||||
|     src:    "nginx/host.conf.j2"  | ||||
|     dest:   "{{ NEXTCLOUD_HOST_NGINX_PATH }}" | ||||
|   notify: restart openresty | ||||
|  | ||||
| - name: "load docker and db for {{ application_id }}" | ||||
|   include_role:  | ||||
|     name: sys-stk-back-stateful | ||||
|   vars: | ||||
|     docker_compose_flush_handlers: false | ||||
|  | ||||
| - name: "create {{ NEXTCLOUD_HOST_CONF_ADD_PATH }}" | ||||
|   file: | ||||
|     path: "{{ NEXTCLOUD_HOST_CONF_ADD_PATH }}" | ||||
|     state: directory | ||||
|     mode: "0755" | ||||
|  | ||||
| - name: "Create config files at {{ NEXTCLOUD_HOST_CONF_ADD_PATH }}" | ||||
|   template: | ||||
|     src:    "{{ item }}" | ||||
|     dest:   "{{ NEXTCLOUD_HOST_CONF_ADD_PATH }}/{{ item | basename | regex_replace('\\.j2$', '') }}" | ||||
|     owner:  "{{ NEXTCLOUD_DOCKER_USER_id }}" | ||||
|     group:  "{{ NEXTCLOUD_DOCKER_USER_id }}" | ||||
|   loop:     "{{ lookup('fileglob', role_path ~ '/templates/config/*.j2', wantlist=True) }}" | ||||
|   # Not all type of changes take instantly place. Due to this reason a rebuild is required. | ||||
|   notify: docker compose up | ||||
|  | ||||
| - name: create internal nextcloud nginx configuration | ||||
|   template:  | ||||
|     src:  "nginx/docker.conf.j2"  | ||||
|     dest: "{{ [docker_compose.directories.volumes, 'nginx.conf'] | path_join }}" | ||||
|   notify: restart nextcloud nginx service | ||||
| @@ -3,7 +3,7 @@ | ||||
|     - name: Add dynamic config merging from Jinja template | ||||
|       template: | ||||
|         src: include.php.j2 | ||||
|         dest: "{{ nextcloud_host_include_instructions_file }}" | ||||
|         dest: "{{ NEXTCLOUD_HOST_INCL_PATH }}" | ||||
|       notify: docker compose restart | ||||
| 
 | ||||
|     - name: Flush handlers so Nextcloud container is restarted and ready | ||||
| @@ -19,11 +19,11 @@ | ||||
| 
 | ||||
|     - name: Copy include instructions to the container | ||||
|       command: > | ||||
|         docker cp {{ nextcloud_host_include_instructions_file }} {{ NEXTCLOUD_CONTAINER }}:{{ nextcloud_docker_include_instructions_file }} | ||||
|         docker cp {{ NEXTCLOUD_HOST_INCL_PATH }} {{ NEXTCLOUD_CONTAINER }}:{{ NEXTCLOUD_DOCKER_INCL_PATH }} | ||||
| 
 | ||||
|     - name: Append generated config to config.php only if not present | ||||
|       command: > | ||||
|         docker exec -u {{ NEXTCLOUD_DOCKER_USER }} {{ NEXTCLOUD_CONTAINER }} sh -c " | ||||
|           grep -q '{{ nextcloud_docker_config_additives_directory }}' {{ nextcloud_docker_config_file }} ||  | ||||
|           cat {{ nextcloud_docker_include_instructions_file }} >> {{ nextcloud_docker_config_file }}" | ||||
|           grep -q '{{ NEXTCLOUD_DOCKER_CONF_ADD_PATH }}' {{ NEXTCLOUD_DOCKER_CONFIG_FILE }} ||  | ||||
|           cat {{ NEXTCLOUD_DOCKER_INCL_PATH }} >> {{ NEXTCLOUD_DOCKER_CONFIG_FILE }}" | ||||
|       notify: docker compose restart | ||||
| @@ -1,7 +1,7 @@ | ||||
| - name: Ensure Nextcloud administrator is in the 'admin' group | ||||
|   command: > | ||||
|     docker exec -u {{ NEXTCLOUD_DOCKER_USER }} {{ NEXTCLOUD_CONTAINER }} | ||||
|     php occ group:adduser admin {{ nextcloud_administrator_username }} | ||||
|     php occ group:adduser admin {{ NEXTCLOUD_ADMINISTRATOR_USERNAME }} | ||||
|   register: add_admin_to_group | ||||
|   changed_when: "not ASYNC_ENABLED and 'Added user' in (add_admin_to_group.stdout | default(''))" | ||||
|   failed_when: > | ||||
| @@ -1,5 +1,5 @@ | ||||
| - block: | ||||
|     - include_tasks: 06_plugin_routines.yml | ||||
|     - include_tasks: _plugin_a_routines.yml | ||||
|   when: plugin_value.enabled | bool | ||||
| 
 | ||||
| - name: disable {{ plugin_key }} nextcloud plugin | ||||
| @@ -35,9 +35,12 @@ | ||||
|     and | ||||
|     ("already installed" not in install_result.stdout) | ||||
| 
 | ||||
| - include_tasks: 07_plugin_enable_and_configure.yml | ||||
| - include_tasks: _plugin_b_enable_and_configure.yml | ||||
|   when: | ||||
|     - install_result is defined  | ||||
|     - > | ||||
|       install_result.rc == 0 | ||||
|       or "already installed" in install_result.stdout | ||||
|       or "already installed" in install_result.stdout | ||||
|   vars: | ||||
|     plugin_task_path: "{{ NEXTCLOUD_CNODE_PLUGIN_TASKS_PATH }}{{ plugin_key }}.yml" | ||||
|     plugin_vars_path: "{{ NEXTCLOUD_CNODE_PLUGIN_VARS_PATH }}{{ plugin_key }}.yml" | ||||
| @@ -3,16 +3,16 @@ | ||||
|   register: enable_result | ||||
|   changed_when: enable_result.rc == 0 and ("already enabled" not in enable_result.stdout) | ||||
| 
 | ||||
| - name: Check if {{ nextcloud_control_node_plugin_vars_directory }}{{ plugin_key }}.yml exists | ||||
| - name: Check if {{ plugin_vars_path }} exists | ||||
|   stat: | ||||
|     path: "{{ nextcloud_control_node_plugin_vars_directory }}{{ plugin_key }}.yml" | ||||
|     path: "{{ plugin_vars_path }}" | ||||
|   delegate_to: localhost | ||||
|   become: false | ||||
|   register: plugin_vars_file | ||||
| 
 | ||||
| - name: "Load {{ plugin_key }} configuration variables" | ||||
|   include_vars: | ||||
|     file: "{{ nextcloud_control_node_plugin_vars_directory }}{{ plugin_key }}.yml" | ||||
|     file: "{{ plugin_vars_path }}" | ||||
|   when: plugin_vars_file.stat.exists | ||||
| 
 | ||||
| - name: "Set plugin configuration (batched shell, no async)" | ||||
| @@ -35,13 +35,13 @@ | ||||
|   async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}" | ||||
|   poll:  "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}" | ||||
| 
 | ||||
| - name: Check if {{ nextcloud_control_node_plugin_tasks_directory }}{{ plugin_key }}.yml exists | ||||
| - name: Check if {{ plugin_task_path }} exists | ||||
|   stat: | ||||
|     path: "{{ nextcloud_control_node_plugin_tasks_directory }}{{ plugin_key }}.yml" | ||||
|     path: "{{ plugin_task_path }}" | ||||
|   delegate_to: localhost | ||||
|   become: false | ||||
|   register: plugin_tasks_file | ||||
| 
 | ||||
| - name: "include {{ nextcloud_control_node_plugin_tasks_directory }}{{ plugin_key }}.yml" | ||||
|   include_tasks: "{{ nextcloud_control_node_plugin_tasks_directory }}{{ plugin_key }}.yml" | ||||
| - name: "include {{ plugin_task_path }}" | ||||
|   include_tasks: "{{ plugin_task_path }}" | ||||
|   when: plugin_tasks_file.stat.exists | ||||
| @@ -1,75 +1,39 @@ | ||||
| --- | ||||
| - name: "Install Collabora Dependency" | ||||
|   include_role: | ||||
|     name: web-svc-collabora | ||||
|   vars: | ||||
|     flush_handlers: true | ||||
|   when:     | ||||
|     - run_once_web_svc_collabora is not defined | ||||
|     - NEXTCLOUD_COLLABORA_ENABLED | bool | ||||
|  | ||||
| - name: "include role for {{ application_id }} to receive certs & do modification routines" | ||||
|   include_role: | ||||
|     name: sys-util-csp-cert | ||||
|  | ||||
| - name: create nextcloud proxy configuration file | ||||
|   template:  | ||||
|     src:  "nginx/host.conf.j2"  | ||||
|     dest: "{{ nextcloud_host_nginx_path }}" | ||||
|   notify: restart openresty | ||||
|  | ||||
| - name: "load docker and db for {{ application_id }}" | ||||
| - name: "load docker, db and proxy for {{ application_id }}" | ||||
|   include_role:  | ||||
|     name: sys-stk-back-stateful | ||||
|     name: sys-stk-full-stateful | ||||
|   vars: | ||||
|     docker_compose_flush_handlers: false | ||||
|  | ||||
| - name: "create {{ nextcloud_host_config_additives_directory }}" | ||||
|   file: | ||||
|     path: "{{ nextcloud_host_config_additives_directory }}" | ||||
|     state: directory | ||||
|     mode: "0755" | ||||
|  | ||||
| - name: "Create config files at {{ nextcloud_host_config_additives_directory }}" | ||||
|   template: | ||||
|     src:    "{{ item }}" | ||||
|     dest:   "{{ nextcloud_host_config_additives_directory }}/{{ item | basename | regex_replace('\\.j2$', '') }}" | ||||
|     owner:  "{{ NEXTCLOUD_DOCKER_USER_id }}" | ||||
|     group:  "{{ NEXTCLOUD_DOCKER_USER_id }}" | ||||
|   loop:     "{{ lookup('fileglob', role_path ~ '/templates/config/*.j2', wantlist=True) }}" | ||||
|   # Not all type of changes take instantly place. Due to this reason a rebuild is required. | ||||
|   notify: docker compose up | ||||
|  | ||||
| - name: create internal nextcloud nginx configuration | ||||
|   template:  | ||||
|     src:  "nginx/docker.conf.j2"  | ||||
|     dest: "{{ docker_compose.directories.volumes }}nginx.conf" | ||||
|   notify: restart nextcloud nginx service | ||||
| - name: Setup the full docker stack | ||||
|   include_tasks: 01_fullstack.yml | ||||
|   vars: | ||||
|     domain:     "{{ NEXTCLOUD_DOMAIN }}" | ||||
|     http_port:  "{{ NEXTCLOUD_PORT }}" | ||||
|  | ||||
| - name: Setup config.php  | ||||
|   include_tasks: 01_config.yml | ||||
|   include_tasks: 02_config.yml | ||||
|  | ||||
| - name: Flush all handlers immediately so that occ can be used | ||||
|   meta: flush_handlers | ||||
|  | ||||
| - name: Update\Upgrade Nextcloud | ||||
|   include_tasks: 02_upgrade.yml | ||||
|   include_tasks: 03_upgrade.yml | ||||
|   when: MODE_UPDATE | bool | ||||
|  | ||||
| - name: Load system configuration steps | ||||
|   include_tasks: "{{ item }}" | ||||
|   loop: | ||||
|     - 03_admin.yml | ||||
|     - 04_system_config.yml | ||||
|     - 04_admin.yml | ||||
|     - 05_system_config.yml | ||||
|  | ||||
| - name: Setup Nextcloud Plugins | ||||
|   include_tasks: 05_plugin.yml | ||||
|   loop: "{{ applications | get_app_conf(application_id, 'plugins', True) | dict2items }}" | ||||
|   include_tasks: 06_setup_plugin.yml | ||||
|   loop: "{{ NEXTCLOUD_PLUGIN_ITEMS }}" | ||||
|   loop_control: | ||||
|     loop_var: plugin_item | ||||
|   vars: | ||||
|     plugin_key:   "{{ plugin_item.key }}" | ||||
|     plugin_value: "{{ plugin_item.value }}" | ||||
|   when: nextcloud_plugins_enabled | ||||
|   when: NEXTCLOUD_PLUGINS_ENABLED | ||||
|  | ||||
|  | ||||
|   | ||||
							
								
								
									
										7
									
								
								roles/web-app-nextcloud/tasks/plugins/richdocuments.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								roles/web-app-nextcloud/tasks/plugins/richdocuments.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| - name: "Install Collabora Dependency" | ||||
|   include_role: | ||||
|     name: web-svc-collabora | ||||
|   vars: | ||||
|     flush_handlers: true | ||||
|   when:     | ||||
|     - run_once_web_svc_collabora is not defined | ||||
							
								
								
									
										0
									
								
								roles/web-app-nextcloud/tasks/plugins/spreed.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								roles/web-app-nextcloud/tasks/plugins/spreed.yml
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										9
									
								
								roles/web-app-nextcloud/tasks/plugins/whiteboard.yml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								roles/web-app-nextcloud/tasks/plugins/whiteboard.yml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| - name: Set Whiteboard Configuration | ||||
|   ansible.builtin.shell: > | ||||
|     {{ NEXTCLOUD_DOCKER_EXEC_OCC }} config:app:set whiteboard collabBackendUrl --value='{{ NEXTCLOUD_WHITEBOARD_URL }}' | ||||
|     && {{ NEXTCLOUD_DOCKER_EXEC_OCC }} config:app:set whiteboard jwt_secret_key --value='{{ NEXTCLOUD_WHITEBOARD_JWT }}' | ||||
|   args: | ||||
|     executable: /bin/bash | ||||
|   async: "{{ ASYNC_TIME if ASYNC_ENABLED | bool else omit }}" | ||||
|   poll:  "{{ ASYNC_POLL if ASYNC_ENABLED | bool else omit }}" | ||||
|   no_log: "{{ MASK_CREDENTIALS_IN_LOGS | bool }}" | ||||
| @@ -1,7 +1,7 @@ | ||||
| <?php | ||||
| # Implementing OICD configuration | ||||
|  | ||||
| {% if applications | get_app_conf(application_id, 'oidc.flavor', True) == "oidc_login" %} | ||||
| {% if applications | get_app_conf(application_id, 'oidc.flavor') == "oidc_login" %} | ||||
|  | ||||
| # Check out: https://github.com/pulsejet/nextcloud-oidc-login | ||||
|  | ||||
| @@ -21,7 +21,7 @@ return array ( | ||||
|     'oidc_login_auto_redirect' => true, | ||||
|  | ||||
|     // Redirect to this page after logging out the user | ||||
|     'oidc_login_logout_url' => 'https://{{ domains | get_domain(application_id) }}', | ||||
|     'oidc_login_logout_url' => '{{ NEXTCLOUD_URL }}', | ||||
|  | ||||
|     // If set to true the user will be redirected to the | ||||
|     // logout endpoint of the OIDC provider after logout | ||||
| @@ -33,7 +33,7 @@ return array ( | ||||
|     // | ||||
|     // NOTE: If you want to allow NextCloud to manage quotas, omit this option. Do not set it to | ||||
|     // zero or -1 or ''. | ||||
|     'oidc_login_default_quota' => '{{applications | get_app_conf(application_id, 'default_quota', True)}}', | ||||
|     'oidc_login_default_quota' => '{{ applications | get_app_conf(application_id, 'default_quota', True)}}', | ||||
|  | ||||
|     // Login button text | ||||
|     'oidc_login_button_text' => '{{ OIDC.BUTTON_TEXT }}', | ||||
| @@ -97,7 +97,7 @@ return array ( | ||||
|     // note: on Keycloak, OIDC name claim = "${given_name} ${family_name}" or one of them if any is missing | ||||
|     // | ||||
|     'oidc_login_attributes' => array ( | ||||
|         'id' => '{{LDAP.USER.ATTRIBUTES.ID}}', | ||||
|         'id' => '{{ LDAP.USER.ATTRIBUTES.ID }}', | ||||
|         'name' => 'name', | ||||
|         'mail' => 'email', | ||||
|         'quota' => '{{ LDAP.USER.ATTRIBUTES.NEXTCLOUD_QUOTA }}', | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| {% include 'roles/docker-compose/templates/base.yml.j2' %} | ||||
|  | ||||
|   application: | ||||
|     image: "{{ nextcloud_image }}:{{ nextcloud_version }}" | ||||
|     image: "{{ NEXTCLOUD_IMAGE }}:{{ NEXTCLOUD_VERSION }}" | ||||
|     container_name: {{ NEXTCLOUD_CONTAINER }} | ||||
|     volumes: | ||||
|       - data:{{ NEXTCLOUD_DOCKER_WORK_DIRECTORY }} | ||||
|       - {{ nextcloud_host_config_additives_directory }}:{{ nextcloud_docker_config_additives_directory }}:ro | ||||
|       - {{ NEXTCLOUD_HOST_CONF_ADD_PATH }}:{{ NEXTCLOUD_DOCKER_CONF_ADD_PATH }}:ro | ||||
|     healthcheck: | ||||
|       test: ["CMD", "su", "www-data", "-s", "/bin/sh", "-c", "php {{ NEXTCLOUD_DOCKER_WORK_DIRECTORY }}occ status"] | ||||
|       interval: 1m | ||||
| @@ -16,25 +16,36 @@ | ||||
| {% include 'roles/docker-container/templates/networks.yml.j2' %} | ||||
|         ipv4_address: 192.168.102.69 | ||||
|  | ||||
| {% if nextcloud_talk_enabled %} | ||||
| {% if NEXTCLOUD_TALK_ENABLED %} | ||||
|   talk: | ||||
|     {% include 'roles/docker-container/templates/base.yml.j2' %} | ||||
|     image: "{{ nextcloud_talk_image }}:{{ nextcloud_talk_version }}" | ||||
|     container_name: {{ nextcloud_talk_name }} | ||||
|     hostname: hpb_yt | ||||
|     image: "{{ NEXTCLOUD_TALK_IMAGE }}:{{ NEXTCLOUD_TALK_VERSION }}" | ||||
|     container_name: {{ NEXTCLOUD_TALK_CONTAINER }} | ||||
|     init: true | ||||
|     ports: | ||||
|       - {{ networks.internet.ip4 }}:{{ nextcloud_talk_stun_port }}:3478/tcp #TURN TCP | ||||
|       - {{ networks.internet.ip4 }}:{{ nextcloud_talk_stun_port }}:3478/udp #TURN UDP | ||||
|       - {{ networks.internet.ip4 }}:8181:8081/tcp                           #Signaling @todo needs to be optimized | ||||
|       - {{ networks.internet.ip4 }}:{{ NEXTCLOUD_TALK_STUN_PORT }}:3478/tcp #TURN TCP | ||||
|       - {{ networks.internet.ip4 }}:{{ NEXTCLOUD_TALK_STUN_PORT }}:3478/udp #TURN UDP | ||||
|       - {{ networks.internet.ip4 }}:{{ NEXTCLOUD_TALK_WS_PORT }}:8081/tcp | ||||
|     networks: | ||||
|       default: | ||||
|         ipv4_address: 192.168.102.68 | ||||
| {% endif %} | ||||
|  | ||||
| {% if NEXTCLOUD_WHITEBOARD_ENABLED %} | ||||
|   whiteboard: | ||||
|     {% include 'roles/docker-container/templates/base.yml.j2' %} | ||||
|     image: "{{ NEXTCLOUD_WHITEBOARD_IMAGE }}:{{ NEXTCLOUD_WHITEBOARD_VERSION }}" | ||||
|     container_name: {{ NEXTCLOUD_WHITEBOARD_CONTAINER }} | ||||
|     expose: | ||||
|       - "{{ NEXTCLOUD_WHITEBOARD_INTERNAL_PORT }}" | ||||
|     networks: | ||||
|       default: | ||||
|         ipv4_address: 192.168.102.71 | ||||
| {% endif %} | ||||
|  | ||||
|   proxy: | ||||
|     image: "{{ nextcloud_proxy_image }}:{{ nextcloud_proxy_version }}" | ||||
|     container_name: "{{ nextcloud_proxy_name }}" | ||||
|     image: "{{ NEXTCLOUD_PROXY_IMAGE }}:{{ NEXTCLOUD_PROXY_VERSION }}" | ||||
|     container_name: "{{ NEXTCLOUD_PROXY_CONTAINER }}" | ||||
|     logging: | ||||
|       driver: journald | ||||
|     restart: {{ DOCKER_RESTART_POLICY }} | ||||
| @@ -51,8 +62,8 @@ | ||||
|         ipv4_address: 192.168.102.67 | ||||
|  | ||||
|   cron: | ||||
|     container_name: "{{ nextcloud_cron_name }}" | ||||
|     image: "{{ nextcloud_image }}:{{ nextcloud_version }}" | ||||
|     container_name: "{{ NEXTCLOUD_CRON_CONTAINER }}" | ||||
|     image: "{{ NEXTCLOUD_IMAGE }}:{{ NEXTCLOUD_VERSION }}" | ||||
|     restart: {{ DOCKER_RESTART_POLICY }} | ||||
|     logging: | ||||
|       driver: journald | ||||
| @@ -70,6 +81,6 @@ | ||||
|  | ||||
| {% include 'roles/docker-compose/templates/volumes.yml.j2' %} | ||||
|   data: | ||||
|     name: {{ nextcloud_volume }} | ||||
|     name: {{ NEXTCLOUD_VOLUME }} | ||||
|  | ||||
| {% include 'roles/docker-compose/templates/networks.yml.j2' %} | ||||
|   | ||||
| @@ -8,9 +8,9 @@ MYSQL_PASSWORD=                 "{{ database_password }}" | ||||
| MYSQL_HOST=                     "{{ database_host }}:{{ database_port }}" | ||||
|        | ||||
| # PHP | ||||
| PHP_MEMORY_LIMIT=               "{{applications | get_app_conf(application_id, 'performance.php.memory_limit')}}" | ||||
| PHP_UPLOAD_LIMIT=               "{{applications | get_app_conf(application_id, 'performance.php.upload_limit')}}" | ||||
| PHP_OPCACHE_MEMORY_CONSUMPTION= "{{applications | get_app_conf(application_id, 'performance.php.opcache_memory_consumption')}}" | ||||
| PHP_MEMORY_LIMIT=               "{{ applications | get_app_conf(application_id, 'performance.php.memory_limit') }}" | ||||
| PHP_UPLOAD_LIMIT=               "{{ applications | get_app_conf(application_id, 'performance.php.upload_limit') }}" | ||||
| PHP_OPCACHE_MEMORY_CONSUMPTION= "{{ applications | get_app_conf(application_id, 'performance.php.opcache_memory_consumption') }}" | ||||
|        | ||||
| # Email Configuration | ||||
| SMTP_HOST=                      {{ SYSTEM_EMAIL.HOST }} | ||||
| @@ -24,30 +24,38 @@ MAIL_FROM_ADDRESS=              "{{ users['no-reply'].username }}" | ||||
| MAIL_DOMAIN=                    "{{ SYSTEM_EMAIL.DOMAIN }}" | ||||
|  | ||||
| # Initial Admin Data | ||||
| NEXTCLOUD_ADMIN_USER=           "{{applications | get_app_conf(application_id, 'users.administrator.username')}}" | ||||
| NEXTCLOUD_ADMIN_PASSWORD=       "{{applications | get_app_conf(application_id, 'credentials.administrator_password')}}" | ||||
| NEXTCLOUD_ADMIN_USER=           "{{ NEXTCLOUD_ADMINISTRATOR_USER }}" | ||||
| NEXTCLOUD_ADMIN_PASSWORD=       "{{ NEXTCLOUD_ADMINISTRATOR_PASSWORD }}" | ||||
|  | ||||
| # Security | ||||
|  | ||||
| NEXTCLOUD_TRUSTED_DOMAINS=      "{{ domains[application_id] | select | join(',') }}" | ||||
| NEXTCLOUD_TRUSTED_DOMAINS=      "{{ NEXTCLOUD_DOMAIN }}" | ||||
| # Whitelist local docker gateway in Nextcloud to prevent brute-force throtteling | ||||
| TRUSTED_PROXIES=                "{{ networks.internet.values() | select | join(',') }}" | ||||
| OVERWRITECLIURL=                "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" | ||||
| OVERWRITEPROTOCOL=              "https" | ||||
| OVERWRITECLIURL=                "{{ NEXTCLOUD_URL }}" | ||||
| OVERWRITEPROTOCOL=              "{{ WEB_PROTOCOL }}" | ||||
|  | ||||
| # Redis Configuration | ||||
| REDIS_HOST=                     redis | ||||
| REDIS_PORT=                     6379 | ||||
|  | ||||
| {% if nextcloud_talk_enabled %} | ||||
| {% if NEXTCLOUD_TALK_ENABLED %} | ||||
| # Talk Configuration | ||||
| # This code was just moved here during refactoring and isn't tested yet. | ||||
| # @todo move it to an own env file for encapsulation reasons | ||||
| NC_DOMAIN=cloud.yourdomain.tld | ||||
| TALK_HOST=signaling.yourdomain.tld | ||||
| NC_DOMAIN={{ NEXTCLOUD_DOMAIN }} | ||||
| TALK_HOST={{ NEXTCLOUD_TALK_DOMAIN }} | ||||
| TURN_SECRET=${TURN_SECRET} | ||||
| SIGNALING_SECRET=${SIGNALING_SECRET} | ||||
| TZ=Europe/Berlin | ||||
| TALK_PORT=3478 | ||||
| INTERNAL_SECRET=${INTERNAL_SECRET} | ||||
| {% endif %} | ||||
|  | ||||
| {% if NEXTCLOUD_WHITEBOARD_ENABLED %} | ||||
| # @todo move it to an own env file for encapsuling reasons | ||||
| NEXTCLOUD_URL=                  "{{ NEXTCLOUD_URL }}" | ||||
| JWT_SECRET_KEY=                 "{{ NEXTCLOUD_WHITEBOARD_JWT }}" | ||||
| STORAGE_STRATEGY=redis | ||||
| REDIS_URL=redis://redis:6379/0 | ||||
| {% endif %} | ||||
| @@ -3,7 +3,7 @@ | ||||
|  | ||||
| $CONFIG_EXTRA = []; | ||||
|  | ||||
| foreach (glob("{% endraw %}{{ nextcloud_docker_config_additives_directory }}{% raw %}*.php") as $file) { | ||||
| foreach (glob("{% endraw %}{{ NEXTCLOUD_DOCKER_CONF_ADD_PATH }}{% raw %}*.php") as $file) { | ||||
|     $CONFIG_EXTRA = array_merge($CONFIG_EXTRA, include $file); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -179,5 +179,15 @@ http { | ||||
|         location / { | ||||
|             try_files $uri $uri/ /index.php$request_uri; | ||||
|         } | ||||
|  | ||||
|         location {{ NEXTCLOUD_WHITEBOARD_LOCATION }} { | ||||
|             proxy_pass         http://whiteboard:{{ NEXTCLOUD_WHITEBOARD_INTERNAL_PORT }}/; | ||||
|             proxy_http_version 1.1; | ||||
|             proxy_set_header   Host              $host; | ||||
|             proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for; | ||||
|             proxy_set_header   Upgrade           $http_upgrade; | ||||
|             proxy_set_header   Connection        "upgrade"; | ||||
|             proxy_read_timeout 3600; | ||||
|         } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,72 +1,88 @@ | ||||
| --- | ||||
| # General | ||||
| application_id:                                 "web-app-nextcloud"                                           # Application identifier | ||||
| container_port:                                 80 | ||||
|  | ||||
| # Networking | ||||
| domain:                                         "{{ domains | get_domain(application_id) }}"                  # Public domain at which Nextcloud will be accessable | ||||
| http_port:                                      "{{ ports.localhost.http[application_id] }}"                  # Port at which nextcloud is reachable in the local network | ||||
| application_id:                     "web-app-nextcloud"                                           # Application identifier | ||||
| container_port:                     80 | ||||
|  | ||||
| # Database | ||||
| database_password:                              "{{ applications | get_app_conf(application_id, 'credentials.database_password', True)}}" | ||||
| database_type:                                  "mariadb"                                                                                   # Database flavor | ||||
| database_password:                  "{{ applications | get_app_conf(application_id, 'credentials.database_password') }}" | ||||
| database_type:                      "mariadb"                                                                                   # Database flavor | ||||
|  | ||||
| nextcloud_plugins_enabled:                      "{{ applications | get_app_conf(application_id, 'plugins_enabled') }}" | ||||
| nextcloud_administrator_username:               "{{ applications | get_app_conf(application_id, 'users.administrator.username') }}" | ||||
| # Nextcloud  | ||||
| ## General  | ||||
| NEXTCLOUD_DOMAIN:                   "{{ domains | get_domain(application_id) }}" | ||||
| NEXTCLOUD_PORT:                     "{{ ports.localhost.http[application_id] }}" | ||||
| NEXTCLOUD_URL:                      "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" | ||||
|  | ||||
| # Control Node | ||||
| nextcloud_control_node_plugin_vars_directory:   "{{role_path}}/vars/plugins/"                                 # Folder in which the files for the plugin configuration are stored | ||||
| nextcloud_control_node_plugin_tasks_directory:  "{{role_path}}/tasks/plugins/"                                # Folder which contains the files for extra plugin configuration tasks | ||||
| NEXTCLOUD_PLUGINS_ENABLED:          "{{ applications | get_app_conf(application_id, 'plugins_enabled') }}" | ||||
| NEXTCLOUD_ADMINISTRATOR_USERNAME:   "{{ applications | get_app_conf(application_id, 'users.administrator.username') }}" | ||||
|  | ||||
| # Host  | ||||
| ## Plugins | ||||
| NEXTCLOUD_PLUGIN_ITEMS:             "{{ applications | get_app_conf(application_id, 'plugins') | dict2items }}" | ||||
|  | ||||
| ## Host Paths | ||||
| nextcloud_host_config_additives_directory:      "{{ docker_compose.directories.volumes }}infinito/"             # This folder is the path to which the additive configurations will be copied | ||||
| nextcloud_host_include_instructions_file:       "{{ docker_compose.directories.volumes }}includes.php"          # Path to the instruction file on the host. Responsible for loading the additional configurations | ||||
| nextcloud_host_nginx_path:                      "{{ NGINX.DIRECTORIES.HTTP.SERVERS }}{{ domains | get_domain(application_id) }}.conf" # Nginx path for proxy conf | ||||
| ## Paths | ||||
|  | ||||
| # Docker | ||||
| ### Host | ||||
| NEXTCLOUD_HOST_CONF_ADD_PATH:       "{{ [ docker_compose.directories.volumes, 'infinito' ] | path_join }}"              # This folder is the path to which the additive configurations will be copied | ||||
| NEXTCLOUD_HOST_INCL_PATH:           "{{ [ docker_compose.directories.volumes, 'includes.php' ] | path_join }}"          # Path to the instruction file on the host. Responsible for loading the additional configurations | ||||
| NEXTCLOUD_HOST_NGINX_PATH:          "{{ [ NGINX.DIRECTORIES.HTTP.SERVERS, NEXTCLOUD_DOMAIN ~ '.conf' ] | path_join }}"  # Nginx path for proxy conf | ||||
|  | ||||
| nextcloud_volume:                               "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}" | ||||
|  | ||||
| nextcloud_version:                              "{{ applications | get_app_conf(application_id, 'docker.services.nextcloud.version') }}" | ||||
| nextcloud_image:                                "{{ applications | get_app_conf(application_id, 'docker.services.nextcloud.image') }}" | ||||
| NEXTCLOUD_CONTAINER:                            "{{ applications | get_app_conf(application_id, 'docker.services.nextcloud.name') }}" | ||||
|  | ||||
| nextcloud_proxy_name:                           "{{ applications | get_app_conf(application_id, 'docker.services.proxy.name') }}" | ||||
| nextcloud_proxy_image:                          "{{ applications | get_app_conf(application_id, 'docker.services.proxy.image') }}" | ||||
| nextcloud_proxy_version:                        "{{ applications | get_app_conf(application_id, 'docker.services.proxy.version') }}" | ||||
|  | ||||
| nextcloud_cron_name:                            "{{ applications | get_app_conf(application_id, 'docker.services.cron.name') }}" | ||||
|  | ||||
| # Plugins  | ||||
|  | ||||
| ## Talk  | ||||
| nextcloud_talk_name:                            "{{ applications | get_app_conf(application_id, 'docker.services.talk.name') }}" | ||||
| nextcloud_talk_image:                           "{{ applications | get_app_conf(application_id, 'docker.services.talk.image') }}" | ||||
| nextcloud_talk_version:                         "{{ applications | get_app_conf(application_id, 'docker.services.talk.version') }}" | ||||
| nextcloud_talk_enabled:                         "{{ applications | is_docker_service_enabled(application_id, 'talk') }}" | ||||
| nextcloud_talk_stun_port:                       "{{ ports.public.stun[application_id] }}" | ||||
| # nextcloud_talk_domain:                          "{{ domains[application_id].talk }}" | ||||
|  | ||||
| # Collabora | ||||
| #nextcloud_collabora_name:                      "{{ applications | get_app_conf(application_id, 'docker.services.collabora.name') }}" | ||||
| NEXTCLOUD_COLLABORA_URL:                        "{{ domains | get_url('web-svc-collabora', WEB_PROTOCOL) }}" | ||||
| #NEXTCLOUD_COLLABORA_DOMAIN:                     "{{ domains | get_domain('web-svc-collabora') }}" | ||||
| NEXTCLOUD_COLLABORA_ENABLED:                    "{{ applications | get_app_conf(application_id, 'plugins.richdocuments.enabled') }}" | ||||
|  | ||||
| ## User Configuration | ||||
| NEXTCLOUD_DOCKER_USER_id:                       82                                                            # UID of the www-data user | ||||
| NEXTCLOUD_DOCKER_USER:                          "www-data"                                                    # Name of the www-data user (Set here to easy change it in the future) | ||||
| ## Control Node | ||||
| NEXTCLOUD_CNODE_PLUGIN_VARS_PATH:   "{{ [role_path, 'vars/plugins/'] | path_join }}"                                 # Folder in which the files for the plugin configuration are stored | ||||
| NEXTCLOUD_CNODE_PLUGIN_TASKS_PATH:  "{{ [role_path, 'tasks/plugins/'] | path_join }}"                                # Folder which contains the files for extra plugin configuration tasks  | ||||
|  | ||||
| ## Internal Paths | ||||
| NEXTCLOUD_DOCKER_WORK_DIRECTORY:                "/var/www/html/"                                              # Name of the workdir in which the application is stored | ||||
| NEXTCLOUD_DOCKER_CONFIG_DIRECTORY:              "{{ NEXTCLOUD_DOCKER_WORK_DIRECTORY }}config/"                  # Folder in which the Nextcloud configurations are stored      | ||||
| nextcloud_docker_config_file:                   "{{ NEXTCLOUD_DOCKER_CONFIG_DIRECTORY }}config.php"             # Path to the Nextcloud configuration file | ||||
| nextcloud_docker_config_additives_directory:    "{{ NEXTCLOUD_DOCKER_CONFIG_DIRECTORY }}infinito/"                # Path to the folder which contains additional configurations | ||||
| nextcloud_docker_include_instructions_file:     "/tmp/includes.php"                                           # Path to the temporary file which will be included to the config.php to load the additional configurations | ||||
| NEXTCLOUD_DOCKER_WORK_DIRECTORY:    "/var/www/html/"                                              # Name of the workdir in which the application is stored | ||||
| NEXTCLOUD_DOCKER_CONF_DIRECTORY:    "{{ NEXTCLOUD_DOCKER_WORK_DIRECTORY }}config/"                # Folder in which the Nextcloud configurations are stored      | ||||
| NEXTCLOUD_DOCKER_CONFIG_FILE:       "{{ NEXTCLOUD_DOCKER_CONF_DIRECTORY }}config.php"             # Path to the Nextcloud configuration file | ||||
| NEXTCLOUD_DOCKER_CONF_ADD_PATH:     "{{ NEXTCLOUD_DOCKER_CONF_DIRECTORY }}infinito/"              # Path to the folder which contains additional configurations | ||||
| NEXTCLOUD_DOCKER_INCL_PATH:         "/tmp/includes.php"                                           # Path to the temporary file which will be included to the config.php to load the additional configurations | ||||
|  | ||||
| ## Administrator | ||||
| NEXTCLOUD_ADMINISTRATOR_USER:       "{{ applications | get_app_conf(application_id, 'users.administrator.username') }}" | ||||
| NEXTCLOUD_ADMINISTRATOR_PASSWORD:   "{{ applications | get_app_conf(application_id, 'credentials.administrator_password') }}" | ||||
|  | ||||
| ## Docker | ||||
|  | ||||
| ### Base | ||||
| NEXTCLOUD_VOLUME:                   "{{ applications | get_app_conf(application_id, 'docker.volumes.data') }}" | ||||
| NEXTCLOUD_VERSION:                  "{{ applications | get_app_conf(application_id, 'docker.services.nextcloud.version') }}" | ||||
| NEXTCLOUD_IMAGE:                    "{{ applications | get_app_conf(application_id, 'docker.services.nextcloud.image') }}" | ||||
| NEXTCLOUD_CONTAINER:                "{{ applications | get_app_conf(application_id, 'docker.services.nextcloud.name') }}" | ||||
|  | ||||
| ### Proxy | ||||
| NEXTCLOUD_PROXY_CONTAINER:          "{{ applications | get_app_conf(application_id, 'docker.services.proxy.name') }}" | ||||
| NEXTCLOUD_PROXY_IMAGE:              "{{ applications | get_app_conf(application_id, 'docker.services.proxy.image') }}" | ||||
| NEXTCLOUD_PROXY_VERSION:            "{{ applications | get_app_conf(application_id, 'docker.services.proxy.version') }}" | ||||
|  | ||||
| ### Cron | ||||
| NEXTCLOUD_CRON_CONTAINER:           "{{ applications | get_app_conf(application_id, 'docker.services.cron.name') }}" | ||||
|  | ||||
| ### Talk  | ||||
| NEXTCLOUD_TALK_CONTAINER:           "{{ applications | get_app_conf(application_id, 'docker.services.talk.name') }}" | ||||
| NEXTCLOUD_TALK_IMAGE:               "{{ applications | get_app_conf(application_id, 'docker.services.talk.image') }}" | ||||
| NEXTCLOUD_TALK_VERSION:             "{{ applications | get_app_conf(application_id, 'docker.services.talk.version') }}" | ||||
| NEXTCLOUD_TALK_ENABLED:             "{{ applications | get_app_conf(application_id, 'plugins.spreed.enabled') }}" | ||||
| NEXTCLOUD_TALK_STUN_PORT:           "{{ ports.public.stun[application_id] }}" | ||||
| NEXTCLOUD_TALK_WS_PORT:             "{{ ports.localhost.websocket[application_id] }}" | ||||
| NEXTCLOUD_TALK_DOMAIN:              "{{ domains[application_id].talk }}" | ||||
|  | ||||
| NEXTCLOUD_WHITEBOARD_CONTAINER:     "{{ applications | get_app_conf(application_id, 'docker.services.whiteboard.name') }}" | ||||
| NEXTCLOUD_WHITEBOARD_IMAGE:         "{{ applications | get_app_conf(application_id, 'docker.services.whiteboard.image') }}" | ||||
| NEXTCLOUD_WHITEBOARD_VERSION:       "{{ applications | get_app_conf(application_id, 'docker.services.whiteboard.version') }}" | ||||
| NEXTCLOUD_WHITEBOARD_ENABLED:       "{{ applications | get_app_conf(application_id, 'plugins.whiteboard.enabled') }}" | ||||
| NEXTCLOUD_WHITEBOARD_INTERNAL_PORT: "3002" | ||||
| NEXTCLOUD_WHITEBOARD_JWT:           "{{ applications | get_app_conf(application_id, 'credentials.whiteboard_jwt_secret') }}" | ||||
| NEXTCLOUD_WHITEBOARD_LOCATION:      "/whiteboard/" | ||||
| NEXTCLOUD_WHITEBOARD_URL:           "{{ [ NEXTCLOUD_URL, NEXTCLOUD_WHITEBOARD_LOCATION ] | url_join }}" | ||||
|  | ||||
| ### Collabora | ||||
| NEXTCLOUD_COLLABORA_URL:            "{{ domains | get_url('web-svc-collabora', WEB_PROTOCOL) }}" | ||||
| # NEXTCLOUD_COLLABORA_ENABLED:        "{{ applications | get_app_conf(application_id, 'plugins.richdocuments.enabled') }}" | ||||
|  | ||||
| ## User Configuration | ||||
| NEXTCLOUD_DOCKER_USER_id:           82                                                            # UID of the www-data user | ||||
| NEXTCLOUD_DOCKER_USER:              "www-data"                                                    # Name of the www-data user (Set here to easy change it in the future) | ||||
|  | ||||
| ## Execution | ||||
| NEXTCLOUD_DOCKER_EXEC:                          "docker exec -u {{ NEXTCLOUD_DOCKER_USER }} {{ NEXTCLOUD_CONTAINER }}" # General execute composition | ||||
| NEXTCLOUD_DOCKER_EXEC_OCC:                      "{{NEXTCLOUD_DOCKER_EXEC}} {{ NEXTCLOUD_DOCKER_WORK_DIRECTORY }}occ"   # Execute docker occ command | ||||
| # NEXTCLOUD_COLLOBORA_CONF_EXEC:                  "docker exec {{ applications | get_app_conf('web-svc-collabora', 'docker.services.collabora.name') }} coolconfig" | ||||
| NEXTCLOUD_DOCKER_EXEC:              "docker exec -u {{ NEXTCLOUD_DOCKER_USER }} {{ NEXTCLOUD_CONTAINER }}" # General execute composition | ||||
| NEXTCLOUD_DOCKER_EXEC_OCC:          "{{NEXTCLOUD_DOCKER_EXEC}} {{ NEXTCLOUD_DOCKER_WORK_DIRECTORY }}occ"   # Execute docker occ command | ||||
| @@ -181,4 +181,4 @@ plugin_configuration: | ||||
|   - | ||||
|     appid: "user_ldap" | ||||
|     configkey: "s01ldap_expert_username_attr" | ||||
|     configvalue: "{{LDAP.USER.ATTRIBUTES.ID}}" | ||||
|     configvalue: "{{ LDAP.USER.ATTRIBUTES.ID }}" | ||||
| @@ -15,7 +15,7 @@ nextcloud_system_config: | ||||
|     value: "{{ HOST_LL | upper }}" | ||||
|  | ||||
|   - parameter: "trusted_domains 0" | ||||
|     value: "{{ domains | get_domain(application_id) }}" | ||||
|     value: "{{ NEXTCLOUD_DOMAIN }}" | ||||
|  | ||||
|   - parameter: "overwrite.cli.url" | ||||
|     value: "{{ domains | get_url(application_id, WEB_PROTOCOL) }}" | ||||
|     value: "{{ NEXTCLOUD_URL }}" | ||||
		Reference in New Issue
	
	Block a user