5 Commits

Author SHA1 Message Date
468b6e734c Deactivated whiteboar 2025-10-20 21:17:06 +02:00
83cb94b6ff Refactored Redis resource include macro and increased memory limits
- Replaced deprecated lookup(vars=...) in svc-db-redis with macro-based include (Ansible/Jinja safe)
- Redis now uses higher resource values (1 CPU, 1G reserved, 8G max, 512 pids)
- Enables stable Whiteboard operation with >3.5 GB Redis memory usage
- Related conversation: https://chatgpt.com/share/68f67a00-d598-800f-a6be-ee5987e66fba
2025-10-20 20:08:38 +02:00
6857295969 Fix variable definition test to recognize block-style Jinja 'set ... endset' statements
This update extends the regex to detect block-style variable definitions such as:
  {% set var %} ... {% endset %}
Previously, only inline 'set var =' syntax was recognized, causing false positives
like '_snippet' being flagged as undefined in Jinja templates.

Reference: https://chatgpt.com/share/68f6799a-eb80-800f-ab5c-7c196d4c4661
2025-10-20 20:04:40 +02:00
8ab398f679 nextcloud:whiteboard: wait for Redis before start (depends_on: service_healthy) to prevent early SocketClosedUnexpectedlyError
Context: added depends_on on redis for the Whiteboard service so websockets don’t crash when Redis isn’t ready yet. See discussion: https://chatgpt.com/share/68f65a3e-aa54-800f-a1a7-e6878775fd7e
2025-10-20 17:50:47 +02:00
31133ddd90 Enhancement: Fix for Nextcloud Whiteboard recording and collaboration server
- Added Chromium headless flags and writable font cache/tmp volumes
- Enabled WebSocket proxy forwarding for /whiteboard/
- Verified and adjusted CSP and frontend integration
- Added Whiteboard-related variables and volumes in main.yml

See ChatGPT conversation (20 Oct 2025):
https://chatgpt.com/share/68f655e1-fa3c-800f-b35f-4f875dfed4fd
2025-10-20 17:31:59 +02:00
7 changed files with 65 additions and 20 deletions

View File

@@ -16,5 +16,12 @@
retries: 30
networks:
- default
{{ lookup('template', 'roles/docker-container/templates/resource.yml.j2',vars={'service_name':'redis'}) | indent(4) }}
{% macro include_resource_for(svc, indent=4) -%}
{% set service_name = svc -%}
{%- set _snippet -%}
{% include 'roles/docker-container/templates/resource.yml.j2' %}
{%- endset -%}
{{ _snippet | indent(indent, true) }}
{%- endmacro %}
{{ include_resource_for('redis') }}
{{ "\n" }}

View File

@@ -28,13 +28,15 @@ server:
docker:
volumes:
data: nextcloud_data
whiteboard_tmp: nextcloud_whiteboard_tmp
whiteboard_fontcache: nextcloud_whiteboard_fontcache
services:
redis:
enabled: true
cpus: "0.25"
mem_reservation: "64m"
mem_limit: "256m"
pids_limit: 256
cpus: "1"
mem_reservation: "1g"
mem_limit: "8g"
pids_limit: 512
database:
enabled: true
cpus: "0.75"

View File

@@ -70,13 +70,21 @@
{% include 'roles/docker-container/templates/healthcheck/nc.yml.j2' %}
image: "{{ NEXTCLOUD_WHITEBOARD_IMAGE }}:{{ NEXTCLOUD_WHITEBOARD_VERSION }}"
container_name: {{ NEXTCLOUD_WHITEBOARD_CONTAINER }}
volumes:
- whiteboard_tmp:/tmp
- whiteboard_fontcache:/var/cache/fontconfig
expose:
- "{{ container_port }}"
networks:
default:
ipv4_address: 192.168.102.71
depends_on:
redis:
condition: service_healthy
{% endif %}
{% set service_name = NEXTCLOUD_CRON_SERVICE %}
{{ service_name }}:
container_name: "{{ NEXTCLOUD_CRON_CONTAINER }}"
@@ -99,5 +107,11 @@
{% include 'roles/docker-compose/templates/volumes.yml.j2' %}
data:
name: {{ NEXTCLOUD_VOLUME }}
{% if NEXTCLOUD_WHITEBOARD_ENABLED %}
whiteboard_tmp:
name: {{ NEXTCLOUD_WHITEBOARD_TMP_VOLUME }}
whiteboard_fontcache:
name: {{ NEXTCLOUD_WHITEBOARD_FRONTCACHE_VOLUME }}
{% endif %}
{% include 'roles/docker-compose/templates/networks.yml.j2' %}

View File

@@ -60,4 +60,11 @@ NEXTCLOUD_URL= "{{ NEXTCLOUD_URL }}"
JWT_SECRET_KEY= "{{ NEXTCLOUD_WHITEBOARD_JWT }}"
STORAGE_STRATEGY=redis
REDIS_URL=redis://redis:6379/0
# Chromium (headless) hardening for Whiteboard
CHROMIUM_FLAGS=--headless=new --no-sandbox --disable-gpu --disable-dev-shm-usage --use-gl=swiftshader --disable-software-rasterizer
# Falls das Image Chromium mitbringt Pfad meistens /usr/bin/chromium oder /usr/bin/chromium-browser:
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
PUPPETEER_SKIP_DOWNLOAD=true
# Deactivated @todo implement
WHITEBOARD_ENABLE_RECORDING=false
{% endif %}

View File

@@ -23,6 +23,12 @@ server
{% include 'roles/sys-svc-proxy/templates/location/ws.conf.j2' %}
{% endif %}
{% if NEXTCLOUD_WHITEBOARD_ENABLED | bool %}
{% set location_ws = '^~ ' ~ NEXTCLOUD_WHITEBOARD_LOCATION %}
{% set ws_port = NEXTCLOUD_PORT %}
{% include 'roles/sys-svc-proxy/templates/location/ws.conf.j2' %}
{% endif %}
{% include 'roles/sys-svc-proxy/templates/location/html.conf.j2' %}
location ^~ /.well-known {

View File

@@ -116,24 +116,26 @@ NEXTCLOUD_HPB_TURN_STANDALONE_CONFIG: >-
}}
### Whiteboard
NEXTCLOUD_WHITEBOARD_SERVICE: "whiteboard"
NEXTCLOUD_WHITEBOARD_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.name') }}"
NEXTCLOUD_WHITEBOARD_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.image') }}"
NEXTCLOUD_WHITEBOARD_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.version') }}"
NEXTCLOUD_WHITEBOARD_ENABLED: "{{ applications | get_app_conf(application_id, 'plugins.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.enabled') }}"
NEXTCLOUD_WHITEBOARD_PORT_INTERNAL: "3002"
NEXTCLOUD_WHITEBOARD_JWT: "{{ applications | get_app_conf(application_id, 'credentials.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'_jwt_secret') }}"
NEXTCLOUD_WHITEBOARD_LOCATION: "/whiteboard/"
NEXTCLOUD_WHITEBOARD_URL: "{{ [ NEXTCLOUD_URL, NEXTCLOUD_WHITEBOARD_LOCATION ] | url_join }}"
NEXTCLOUD_WHITEBOARD_SERVICE: "whiteboard"
NEXTCLOUD_WHITEBOARD_CONTAINER: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.name') }}"
NEXTCLOUD_WHITEBOARD_IMAGE: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.image') }}"
NEXTCLOUD_WHITEBOARD_VERSION: "{{ applications | get_app_conf(application_id, 'docker.services.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.version') }}"
NEXTCLOUD_WHITEBOARD_ENABLED: "{{ applications | get_app_conf(application_id, 'plugins.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'.enabled') }}"
NEXTCLOUD_WHITEBOARD_PORT_INTERNAL: "3002"
NEXTCLOUD_WHITEBOARD_JWT: "{{ applications | get_app_conf(application_id, 'credentials.' ~ NEXTCLOUD_WHITEBOARD_SERVICE ~'_jwt_secret') }}"
NEXTCLOUD_WHITEBOARD_LOCATION: "/whiteboard/"
NEXTCLOUD_WHITEBOARD_URL: "{{ [ NEXTCLOUD_URL, NEXTCLOUD_WHITEBOARD_LOCATION ] | url_join }}"
NEXTCLOUD_WHITEBOARD_TMP_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.whiteboard_tmp') }}"
NEXTCLOUD_WHITEBOARD_FRONTCACHE_VOLUME: "{{ applications | get_app_conf(application_id, 'docker.volumes.whiteboard_fontcache') }}"
### Collabora
NEXTCLOUD_COLLABORA_URL: "{{ domains | get_url('web-svc-collabora', WEB_PROTOCOL) }}"
NEXTCLOUD_COLLABORA_URL: "{{ domains | get_url('web-svc-collabora', WEB_PROTOCOL) }}"
## 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)
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_INTERNAL_OCC_COMMAND: "{{ [ NEXTCLOUD_DOCKER_WORK_DIRECTORY, 'occ'] | path_join }}"
NEXTCLOUD_DOCKER_EXEC: "docker exec -u {{ NEXTCLOUD_DOCKER_USER }} {{ NEXTCLOUD_CONTAINER }}" # General execute composition
NEXTCLOUD_DOCKER_EXEC_OCC: "{{ NEXTCLOUD_DOCKER_EXEC }} {{ NEXTCLOUD_INTERNAL_OCC_COMMAND }}" # Execute docker occ command
NEXTCLOUD_INTERNAL_OCC_COMMAND: "{{ [ NEXTCLOUD_DOCKER_WORK_DIRECTORY, 'occ'] | path_join }}"
NEXTCLOUD_DOCKER_EXEC: "docker exec -u {{ NEXTCLOUD_DOCKER_USER }} {{ NEXTCLOUD_CONTAINER }}" # General execute composition
NEXTCLOUD_DOCKER_EXEC_OCC: "{{ NEXTCLOUD_DOCKER_EXEC }} {{ NEXTCLOUD_INTERNAL_OCC_COMMAND }}" # Execute docker occ command

View File

@@ -51,6 +51,9 @@ class TestVariableDefinitions(unittest.TestCase):
# {% set var = ... %} (allow trimmed variants)
self.jinja_set_def = re.compile(r'{%\s*-?\s*set\s+([a-zA-Z_]\w*)\s*=')
# {% set var %} ... {% endset %} (block-style set)
self.jinja_set_block_def = re.compile(r'{%\s*-?\s*set\s+([a-zA-Z_]\w*)\s*-?%}')
# {% for x in ... %} or {% for k, v in ... %} (allow trimmed variants)
self.jinja_for_def = re.compile(
@@ -159,6 +162,10 @@ class TestVariableDefinitions(unittest.TestCase):
for m in self.jinja_set_def.finditer(line):
self.defined.add(m.group(1))
# Count block-style set as a definition, too
for m in self.jinja_set_block_def.finditer(line):
self.defined.add(m.group(1))
for m in self.jinja_for_def.finditer(line):
self.defined.add(m.group(1))
if m.group(2):