Compare commits

...

22 Commits

Author SHA1 Message Date
Copilot 6375921282 add eurooffice (#8052) 2026-06-04 12:42:02 +02:00
Simon L. 792549c904 build(deps): bump golang from 1.26.3-alpine3.23 to 1.26.4-alpine3.23 in /Containers/watchtower (#8254) 2026-06-04 10:07:30 +02:00
Simon L. 50eea8421b build(deps): bump nats from 2.14.1-scratch to 2.14.2-scratch in /Containers/talk (#8253) 2026-06-04 10:07:05 +02:00
Simon L. 9ea710ba0e build(deps): bump caddy from 2.11.3-builder-alpine to 2.11.4-builder-alpine in /Containers/mastercontainer (#8252) 2026-06-04 10:06:29 +02:00
Simon L. 4940ece62a build(deps): bump golang from 1.26.3-alpine3.23 to 1.26.4-alpine3.23 in /Containers/imaginary (#8251) 2026-06-04 10:06:04 +02:00
Simon L. eed6c74e0c build(deps): bump haproxy from 3.3.10-alpine to 3.4.0-alpine in /Containers/docker-socket-proxy (#8250) 2026-06-04 10:05:25 +02:00
Simon L. ee7d639a0e build(deps): bump caddy from 2.11.3-alpine to 2.11.4-alpine in /Containers/apache (#8249) 2026-06-04 10:04:47 +02:00
Simon L. 7fc2a134b7 build(deps): bump collabora/code from 25.04.10.3.1 to 26.04.1.3.1 in /Containers/collabora (#8255) 2026-06-04 10:03:04 +02:00
dependabot[bot] bd1d52b220 build(deps): bump collabora/code in /Containers/collabora
Bumps collabora/code from 25.04.10.3.1 to 26.04.1.3.1.

---
updated-dependencies:
- dependency-name: collabora/code
  dependency-version: 26.04.1.3.1
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-04 04:34:29 +00:00
dependabot[bot] d8e42d2fd8 build(deps): bump golang in /Containers/watchtower
Bumps golang from 1.26.3-alpine3.23 to 1.26.4-alpine3.23.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.4-alpine3.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 21:58:33 +00:00
dependabot[bot] 8b6ff83c19 build(deps): bump nats in /Containers/talk
Bumps nats from 2.14.1-scratch to 2.14.2-scratch.

---
updated-dependencies:
- dependency-name: nats
  dependency-version: 2.14.2-scratch
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 21:58:17 +00:00
dependabot[bot] 82f2e9dca1 build(deps): bump caddy in /Containers/mastercontainer
Bumps caddy from 2.11.3-builder-alpine to 2.11.4-builder-alpine.

---
updated-dependencies:
- dependency-name: caddy
  dependency-version: 2.11.4-builder-alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 21:57:41 +00:00
dependabot[bot] 414ef03706 build(deps): bump golang in /Containers/imaginary
Bumps golang from 1.26.3-alpine3.23 to 1.26.4-alpine3.23.

---
updated-dependencies:
- dependency-name: golang
  dependency-version: 1.26.4-alpine3.23
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 21:57:30 +00:00
dependabot[bot] f668767765 build(deps): bump haproxy in /Containers/docker-socket-proxy
Bumps haproxy from 3.3.10-alpine to 3.4.0-alpine.

---
updated-dependencies:
- dependency-name: haproxy
  dependency-version: 3.4.0-alpine
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 21:57:25 +00:00
dependabot[bot] 2b8de735dc build(deps): bump caddy in /Containers/apache
Bumps caddy from 2.11.3-alpine to 2.11.4-alpine.

---
updated-dependencies:
- dependency-name: caddy
  dependency-version: 2.11.4-alpine
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-03 21:56:07 +00:00
Simon L. 21250f8ea8 talk-recording: adjust listen address back to 0.0.0.0 as talk-recording listen address does not officially support ipv6 yet (#8246) 2026-06-03 13:34:42 +02:00
Simon L. dc69f69e74 talk-recording: adjust listen address back to 0.0.0.0 as talk-recording listen address does not officially support ipv6 yet
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-06-03 13:32:04 +02:00
Simon L. f28b2a7c1e overlay-log: make it a bit less wide
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-06-03 12:59:57 +02:00
Simon L. 1b1a15edba increase to v13.2.0
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-06-03 12:42:21 +02:00
Copilot 1f94bc8af0 aio-interface: extract Nextcloud latest-major upgrade logic to dedicated script and add UI trigger button (#7988)
* Extract Nextcloud major upgrade logic to script and add UI button

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/8cd11b09-5073-4e27-8e59-9afffaf96c1f

Rename sendNotification to execCommandInContainer and reuse for upgrade method

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/88744552-9d64-4de2-9f64-5a98a5e3b200

Add $cmd array validation to execCommandInContainer

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/45d5228c-7834-404e-ba54-90b5c8c207c8

Apply suggestion from @szaimen

Signed-off-by: Simon L. <szaimen@e.mail.de>

Apply suggestion from @szaimen

Signed-off-by: Simon L. <szaimen@e.mail.de>

Apply suggestion from @szaimen

Signed-off-by: Simon L. <szaimen@e.mail.de>

Apply suggestion from @szaimen

Signed-off-by: Simon L. <szaimen@e.mail.de>

Set installLatestMajor when upgrade-to-latest-major button is clicked

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/7b977c85-9b74-4027-a536-152e49a01976

Extract getLatestMajorVersion() to avoid duplicating the version string

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/d5ec921f-8629-4f6e-949a-e8f89f1eb85f

Address PR review comments and hardcode updater channel to stable

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/c40941ff-2bf8-4a57-82be-2a0bd22b19a2

Restore sendNotification(), update cron files, extract getPlainStreamingCallback()

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/a5b6cd86-d278-4771-8a11-976c4a862966

Remove getPlainStreamingCallback, unify on getAddToStreamingResponseBody

Agent-Logs-Url: https://github.com/nextcloud/all-in-one/sessions/15a4b815-076b-469f-95b2-c61df688a28d

Revert "Remove getPlainStreamingCallback, unify on getAddToStreamingResponseBody"

This reverts commit 6846c3a99549703121461f910cc26e6c116e0dc4.

* Refactor creating and using addToStreamingResponseBody()

This way we stick to having one implementation of the function, not three.

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Read streamed output line by line, not via buffer

This way the code doesn't wait for a buffer to be filled, and we don't need to
implement logic ourselves that is provided by a present library already.

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Ensure all HTTP requests are proxied, even with streaming

When requesting a streamed response, Guzzle apparently doesn't use curl, and thus we have to specify the unix socket proxy differently.

We can't specify it when creating the client, though (Guzzle complains).

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Fix syntax errors

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Remove broken code

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Fix readline line from streaming response

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Strip ANSI codes from command output before sending it to the browser

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Run PHP commands as www-data

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Properly compare version numbers

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Fix using memory limits from env

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Fix return type spec

This method always returns a closure, never null.

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Use more general return type

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Avoid psalm complaint

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Fix namespace of return type

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>

* Apply suggestion from @szaimen

Signed-off-by: Simon L. <szaimen@e.mail.de>

---------

Signed-off-by: Pablo Zmdl <pablo@nextcloud.com>
Signed-off-by: Simon L. <szaimen@e.mail.de>
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: Pablo Zmdl <pablo@nextcloud.com>
Co-authored-by: Simon L. <szaimen@e.mail.de>
2026-06-03 12:38:50 +02:00
Simon L. 335db2aac2 try to fix playwright (#8245) 2026-06-02 17:11:46 +02:00
Simon L. f5f19a488f fix playwright
Signed-off-by: Simon L. <szaimen@e.mail.de>
2026-06-02 17:09:45 +02:00
36 changed files with 438 additions and 139 deletions
+1
View File
@@ -31,6 +31,7 @@ updates:
- "/Containers/collabora" - "/Containers/collabora"
- "/Containers/docker-socket-proxy" - "/Containers/docker-socket-proxy"
- "/Containers/domaincheck" - "/Containers/domaincheck"
- "/Containers/eurooffice"
- "/Containers/fulltextsearch" - "/Containers/fulltextsearch"
- "/Containers/imaginary" - "/Containers/imaginary"
- "/Containers/mastercontainer" - "/Containers/mastercontainer"
+1 -1
View File
@@ -32,7 +32,7 @@ jobs:
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with: with:
node-version: lts/* node-version: 24.15.0
- name: Install dependencies - name: Install dependencies
run: cd php/tests && npm ci run: cd php/tests && npm ci
@@ -17,7 +17,7 @@ jobs:
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6 - uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6
with: with:
node-version: lts/* node-version: 24.15.0
- name: Install dependencies - name: Install dependencies
run: cd php/tests && npm ci run: cd php/tests && npm ci
+8 -1
View File
@@ -47,7 +47,14 @@ http://{$APACHE_HOST}.nextcloud-aio:23973, # For Collabora callback and WOPI req
uri strip_prefix /onlyoffice uri strip_prefix /onlyoffice
reverse_proxy {$ONLYOFFICE_HOST}:80 { reverse_proxy {$ONLYOFFICE_HOST}:80 {
header_up X-Forwarded-Host {http.request.hostport}/onlyoffice header_up X-Forwarded-Host {http.request.hostport}/onlyoffice
header_up X-Forwarded-Proto https }
}
# EuroOffice
route /eurooffice/* {
uri strip_prefix /eurooffice
reverse_proxy {$EUROOFFICE_HOST}:80 {
header_up X-Forwarded-Host {http.request.hostport}/eurooffice
} }
} }
+1 -1
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:latest # syntax=docker/dockerfile:latest
FROM caddy:2.11.3-alpine AS caddy FROM caddy:2.11.4-alpine AS caddy
# From https://github.com/docker-library/httpd/blob/master/2.4/alpine/Dockerfile # From https://github.com/docker-library/httpd/blob/master/2.4/alpine/Dockerfile
FROM httpd:2.4.67-alpine3.23 FROM httpd:2.4.67-alpine3.23
+1 -1
View File
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:latest # syntax=docker/dockerfile:latest
# From a file located probably somewhere here: https://github.com/CollaboraOnline/online/blob/master/docker/from-packages/Dockerfile # From a file located probably somewhere here: https://github.com/CollaboraOnline/online/blob/master/docker/from-packages/Dockerfile
FROM collabora/code:25.04.10.3.1 FROM collabora/code:26.04.1.3.1
USER root USER root
ARG DEBIAN_FRONTEND=noninteractive ARG DEBIAN_FRONTEND=noninteractive
+1 -1
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:latest # syntax=docker/dockerfile:latest
FROM haproxy:3.3.10-alpine FROM haproxy:3.4.0-alpine
# hadolint ignore=DL3002 # hadolint ignore=DL3002
USER root USER root
+17
View File
@@ -0,0 +1,17 @@
# syntax=docker/dockerfile:latest
FROM ghcr.io/euro-office/documentserver:v9.3.1-beta.1
# USER root is probably used
COPY --chmod=775 healthcheck.sh /healthcheck.sh
HEALTHCHECK --start-period=60s --retries=9 CMD /healthcheck.sh
LABEL com.centurylinklabs.watchtower.enable="false" \
wud.watch="false" \
dockhand.update="false" \
org.opencontainers.image.title="EuroOffice for Nextcloud AIO" \
org.opencontainers.image.description="EuroOffice Document Server for Nextcloud All-in-One" \
org.opencontainers.image.url="https://github.com/nextcloud/all-in-one" \
org.opencontainers.image.source="https://github.com/nextcloud/all-in-one" \
org.opencontainers.image.vendor="Nextcloud" \
org.opencontainers.image.documentation="https://github.com/nextcloud/all-in-one/blob/main/readme.md"
+7
View File
@@ -0,0 +1,7 @@
#!/bin/bash
if [ "$AIO_LOG_LEVEL" = 'debug' ]; then
set -x
fi
nc -z 127.0.0.1 80 || exit 1
+1 -1
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:latest # syntax=docker/dockerfile:latest
FROM golang:1.26.3-alpine3.23 AS go FROM golang:1.26.4-alpine3.23 AS go
ENV IMAGINARY_HASH=6a274b488759a896aff02f52afee6e50b5e3a3ee ENV IMAGINARY_HASH=6a274b488759a896aff02f52afee6e50b5e3a3ee
+1 -1
View File
@@ -5,7 +5,7 @@ FROM docker:29.5.2-cli AS docker
ARG CADDY_REMOTE_HOST_HASH=e80a9931765a8dbcbb47db415863387f0df0e1b3 ARG CADDY_REMOTE_HOST_HASH=e80a9931765a8dbcbb47db415863387f0df0e1b3
# Caddy is a requirement # Caddy is a requirement
FROM caddy:2.11.3-builder-alpine AS caddy FROM caddy:2.11.4-builder-alpine AS caddy
RUN set -ex; \ RUN set -ex; \
xcaddy build --with github.com/muety/caddy-remote-host@"$CADDY_REMOTE_HOST_HASH"; \ xcaddy build --with github.com/muety/caddy-remote-host@"$CADDY_REMOTE_HOST_HASH"; \
/usr/bin/caddy list-modules /usr/bin/caddy list-modules
@@ -2,4 +2,5 @@
$CONFIG = array ( $CONFIG = array (
'one-click-instance' => true, 'one-click-instance' => true,
'one-click-instance.user-limit' => 100, 'one-click-instance.user-limit' => 100,
'update_channel' => 'stable',
); );
+54 -31
View File
@@ -419,41 +419,12 @@ EOF
# AIO update to latest start # Do not remove or change this line! # AIO update to latest start # Do not remove or change this line!
if [ "$INSTALL_LATEST_MAJOR" = yes ]; then if [ "$INSTALL_LATEST_MAJOR" = yes ]; then
php /var/www/html/occ config:system:set updatedirectory --value="/nc-updater" if ! bash /upgrade-latest-major.sh; then
INSTALLED_AT="$(php /var/www/html/occ config:app:get core installedat)" echo "Upgrade to latest major version failed! Check the output above for details."
if [ -n "${INSTALLED_AT}" ]; then
# Set the installdat to 00 which will allow to skip staging and install the next major directly
# shellcheck disable=SC2001
INSTALLED_AT="$(echo "${INSTALLED_AT}" | sed "s|[0-9][0-9]$|00|")"
php /var/www/html/occ config:app:set core installedat --value="${INSTALLED_AT}"
fi
php /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! php /var/www/html/occ -V || php /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1 exit 1
fi fi
# shellcheck disable=SC2016 # shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')" installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
INSTALLED_MAJOR="${installed_version%%.*}"
IMAGE_MAJOR="${image_version%%.*}"
# If a valid upgrade path, trigger the Nextcloud built-in Updater
if ! [ "$INSTALLED_MAJOR" -gt "$IMAGE_MAJOR" ]; then
php /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! php /var/www/html/occ -V || php /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
# TODO: Add a hint here about what to do / where to look / updater.log?
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1
fi
# shellcheck disable=SC2016
installed_version="$(php -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
fi
php /var/www/html/occ config:system:set updatechecker --type=bool --value=true
php /var/www/html/occ app:enable nextcloud-aio --force
php /var/www/html/occ db:add-missing-columns
php /var/www/html/occ db:add-missing-primary-keys
yes | php /var/www/html/occ db:convert-filecache-bigint
fi fi
# AIO update to latest end # Do not remove or change this line! # AIO update to latest end # Do not remove or change this line!
@@ -896,6 +867,58 @@ else
fi fi
fi fi
# EuroOffice
if [ "$EUROOFFICE_ENABLED" = 'yes' ]; then
# Determine EuroOffice port based on host pattern
if echo "$EUROOFFICE_HOST" | grep -q "nextcloud-.*-eurooffice"; then
EUROOFFICE_PORT=80
else
EUROOFFICE_PORT=443
fi
count=0
while ! nc -z "$EUROOFFICE_HOST" "$EUROOFFICE_PORT" && [ "$count" -lt 90 ]; do
echo "Waiting for EuroOffice to become available..."
count=$((count+5))
sleep 5
done
if [ "$count" -ge 90 ]; then
bash /notify.sh "EuroOffice did not start in time!" "Skipping initialization and disabling eurooffice app."
php /var/www/html/occ app:disable eurooffice
else
# Install or enable EuroOffice app as needed
if ! [ -d "/var/www/html/custom_apps/eurooffice" ]; then
php /var/www/html/occ app:install eurooffice
elif [ "$(php /var/www/html/occ config:app:get eurooffice enabled)" != "yes" ]; then
php /var/www/html/occ app:enable eurooffice
elif [ "$SKIP_UPDATE" != 1 ]; then
php /var/www/html/occ app:update eurooffice
fi
# Set EuroOffice configuration
php /var/www/html/occ config:system:set eurooffice editors_check_interval --value="0" --type=integer
php /var/www/html/occ config:system:set eurooffice jwt_secret --value="$EUROOFFICE_SECRET"
php /var/www/html/occ config:app:set eurooffice jwt_secret --value="$EUROOFFICE_SECRET"
php /var/www/html/occ config:system:set eurooffice jwt_header --value="AuthorizationJwt"
# Adjust the EuroOffice host if using internal pattern
if echo "$EUROOFFICE_HOST" | grep -q "nextcloud-.*-eurooffice"; then
EUROOFFICE_HOST="$NC_DOMAIN/eurooffice"
export EUROOFFICE_HOST
fi
php /var/www/html/occ config:app:set eurooffice DocumentServerUrl --value="https://$EUROOFFICE_HOST"
fi
else
# Remove EuroOffice app if disabled and removal is requested
if [ "$REMOVE_DISABLED_APPS" = yes ] && \
[ -d "/var/www/html/custom_apps/eurooffice" ] && \
[ -n "$EUROOFFICE_SECRET" ] && \
[ "$(php /var/www/html/occ config:system:get eurooffice jwt_secret)" = "$EUROOFFICE_SECRET" ]; then
php /var/www/html/occ app:remove eurooffice
fi
fi
# Talk # Talk
if [ "$TALK_ENABLED" = 'yes' ]; then if [ "$TALK_ENABLED" = 'yes' ]; then
set -x set -x
@@ -0,0 +1,43 @@
#!/bin/bash
PHP_CLI="php"
if [[ "$EUID" = 0 ]]; then
PHP_CLI="sudo -u www-data -E $PHP_CLI"
fi
# shellcheck disable=SC2016
image_version="$($PHP_CLI -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
export IMAGE_MAJOR="${image_version%%.*}"
$PHP_CLI /var/www/html/occ config:system:set updatedirectory --value="/nc-updater"
INSTALLED_AT="$($PHP_CLI /var/www/html/occ config:app:get core installedat)"
if [ -n "${INSTALLED_AT}" ]; then
# Set the installedat to 00 which will allow to skip staging and install the next major directly
# shellcheck disable=SC2001
INSTALLED_AT="$(echo "${INSTALLED_AT}" | sed "s|[0-9][0-9]$|00|")"
$PHP_CLI /var/www/html/occ config:app:set core installedat --value="${INSTALLED_AT}"
fi
$PHP_CLI /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! $PHP_CLI /var/www/html/occ -V || $PHP_CLI /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1
fi
# shellcheck disable=SC2016
installed_version="$($PHP_CLI -r 'require "/var/www/html/version.php"; echo implode(".", $OC_Version);')"
export INSTALLED_MAJOR="${installed_version%%.*}"
# If a valid upgrade path, trigger the Nextcloud built-in Updater
if ! $PHP_CLI -r "version_compare(getenv('INSTALLED_MAJOR'), getenv('IMAGE_MAJOR'), '>') || exit(1);"; then
$PHP_CLI /var/www/html/updater/updater.phar --no-interaction --no-backup
if ! $PHP_CLI /var/www/html/occ -V || $PHP_CLI /var/www/html/occ status | grep maintenance | grep -q 'true'; then
echo "Installation of Nextcloud failed!"
# TODO: Add a hint here about what to do / where to look / updater.log?
touch "$NEXTCLOUD_DATA_DIR/install.failed"
exit 1
fi
fi
$PHP_CLI /var/www/html/occ config:system:set updatechecker --type=bool --value=true
$PHP_CLI /var/www/html/occ app:enable nextcloud-aio --force
$PHP_CLI /var/www/html/occ db:add-missing-columns
$PHP_CLI /var/www/html/occ db:add-missing-primary-keys
yes | $PHP_CLI /var/www/html/occ db:convert-filecache-bigint
+1 -1
View File
@@ -4,4 +4,4 @@ if [ "$AIO_LOG_LEVEL" = 'debug' ]; then
set -x set -x
fi fi
nc -z 127.0.0.1 1234 || nc -z ::1 1234 || exit 1 nc -z 127.0.0.1 1234 || exit 1
+1 -9
View File
@@ -58,21 +58,13 @@ extensionaudio = .m4a
extensionvideo = .mp4" extensionvideo = .mp4"
fi fi
# Detect IPv6 availability to choose the right listen address
RECORDING_LISTEN="0.0.0.0:1234"
if ! grep -q "1" /sys/module/ipv6/parameters/disable 2>/dev/null \
&& ! grep -q "1" /proc/sys/net/ipv6/conf/all/disable_ipv6 2>/dev/null \
&& ! grep -q "1" /proc/sys/net/ipv6/conf/default/disable_ipv6 2>/dev/null; then
RECORDING_LISTEN="[::]:1234"
fi
cat << RECORDING_CONF > "/conf/recording.conf" cat << RECORDING_CONF > "/conf/recording.conf"
[logs] [logs]
# 30 means Warning # 30 means Warning
level = ${TALK_RECORDING_LOG_LEVEL} level = ${TALK_RECORDING_LOG_LEVEL}
[http] [http]
listen = ${RECORDING_LISTEN} listen = 0.0.0.0:1234
[backend] [backend]
allowall = ${ALLOW_ALL} allowall = ${ALLOW_ALL}
+1 -1
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:latest # syntax=docker/dockerfile:latest
FROM nats:2.14.1-scratch AS nats FROM nats:2.14.2-scratch AS nats
FROM eturnal/eturnal:1.12.2-alpine AS eturnal FROM eturnal/eturnal:1.12.2-alpine AS eturnal
FROM strukturag/nextcloud-spreed-signaling:2.1.1 AS signaling FROM strukturag/nextcloud-spreed-signaling:2.1.1 AS signaling
FROM alpine:3.23.4 AS janus FROM alpine:3.23.4 AS janus
+1 -1
View File
@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:latest # syntax=docker/dockerfile:latest
FROM golang:1.26.3-alpine3.23 AS go FROM golang:1.26.4-alpine3.23 AS go
ENV WATCHTOWER_COMMIT_HASH=9d0048403a7242943084bede951f6f966f7691ba ENV WATCHTOWER_COMMIT_HASH=9d0048403a7242943084bede951f6f966f7691ba
+2 -2
View File
@@ -133,7 +133,7 @@ for variable in "${DEPLOYMENTS[@]}"; do
sed -i "/^ spec:/r /tmp/initcontainers.clamav" "$variable" sed -i "/^ spec:/r /tmp/initcontainers.clamav" "$variable"
elif echo "$variable" | grep -q "nextcloud-deployment.yaml"; then elif echo "$variable" | grep -q "nextcloud-deployment.yaml"; then
sed -i "/^ spec:/r /tmp/initcontainers.nextcloud" "$variable" sed -i "/^ spec:/r /tmp/initcontainers.nextcloud" "$variable"
elif echo "$variable" | grep -q "fulltextsearch" || echo "$variable" | grep -q "onlyoffice" || echo "$variable" | grep -q "collabora"; then elif echo "$variable" | grep -q "fulltextsearch" || echo "$variable" | grep -q "onlyoffice" || echo "$variable" | grep -q "eurooffice" || echo "$variable" | grep -q "collabora"; then
sed -i "/^ spec:/r /tmp/initcontainers" "$variable" sed -i "/^ spec:/r /tmp/initcontainers" "$variable"
fi fi
volumeNames="$(grep -A1 mountPath "$variable" | grep -v mountPath | sed 's|.*name: ||' | sed '/^--$/d')" volumeNames="$(grep -A1 mountPath "$variable" | grep -v mountPath | sed 's|.*name: ||' | sed '/^--$/d')"
@@ -499,7 +499,7 @@ cat << EOL > /tmp/security.conf
{{- end }} {{- end }}
EOL EOL
# shellcheck disable=SC1083 # shellcheck disable=SC1083
find ./ \( -not -name '*collabora-deployment.yaml*' -not -name '*apache-deployment.yaml*' -not -name '*onlyoffice-deployment.yaml*' -name "*deployment.yaml" \) -exec sed -i "/^ securityContext:$/r /tmp/security.conf" \{} \; find ./ \( -not -name '*collabora-deployment.yaml*' -not -name '*apache-deployment.yaml*' -not -name '*onlyoffice-deployment.yaml*' -not -name '*eurooffice-deployment.yaml*' -name "*deployment.yaml" \) -exec sed -i "/^ securityContext:$/r /tmp/security.conf" \{} \;
# shellcheck disable=SC1083 # shellcheck disable=SC1083
find ./ -name '*collabora-deployment.yaml*' -exec sed -i "/ADDITIONAL_COLLABORA_OPTIONS_PLACEHOLDER/d" \{} \; find ./ -name '*collabora-deployment.yaml*' -exec sed -i "/ADDITIONAL_COLLABORA_OPTIONS_PLACEHOLDER/d" \{} \;
+50
View File
@@ -6,6 +6,7 @@
"documentation": "https://github.com/nextcloud/all-in-one/discussions/2105", "documentation": "https://github.com/nextcloud/all-in-one/discussions/2105",
"depends_on": [ "depends_on": [
"nextcloud-aio-onlyoffice", "nextcloud-aio-onlyoffice",
"nextcloud-aio-eurooffice",
"nextcloud-aio-collabora", "nextcloud-aio-collabora",
"nextcloud-aio-talk", "nextcloud-aio-talk",
"nextcloud-aio-notify-push", "nextcloud-aio-notify-push",
@@ -47,6 +48,7 @@
"APACHE_PORT=%APACHE_PORT%", "APACHE_PORT=%APACHE_PORT%",
"AIO_LOG_LEVEL=%AIO_LOG_LEVEL%", "AIO_LOG_LEVEL=%AIO_LOG_LEVEL%",
"ONLYOFFICE_HOST=nextcloud-aio-onlyoffice", "ONLYOFFICE_HOST=nextcloud-aio-onlyoffice",
"EUROOFFICE_HOST=nextcloud-aio-eurooffice",
"TZ=%TIMEZONE%", "TZ=%TIMEZONE%",
"APACHE_MAX_SIZE=%APACHE_MAX_SIZE%", "APACHE_MAX_SIZE=%APACHE_MAX_SIZE%",
"APACHE_MAX_TIME=%NEXTCLOUD_MAX_TIME%", "APACHE_MAX_TIME=%NEXTCLOUD_MAX_TIME%",
@@ -223,6 +225,7 @@
"TURN_SECRET=%TURN_SECRET%", "TURN_SECRET=%TURN_SECRET%",
"SIGNALING_SECRET=%SIGNALING_SECRET%", "SIGNALING_SECRET=%SIGNALING_SECRET%",
"ONLYOFFICE_SECRET=%ONLYOFFICE_SECRET%", "ONLYOFFICE_SECRET=%ONLYOFFICE_SECRET%",
"EUROOFFICE_SECRET=%EUROOFFICE_SECRET%",
"AIO_URL=%AIO_URL%", "AIO_URL=%AIO_URL%",
"AIO_LOG_LEVEL=%AIO_LOG_LEVEL%", "AIO_LOG_LEVEL=%AIO_LOG_LEVEL%",
"NC_AIO_VERSION=v%AIO_VERSION%", "NC_AIO_VERSION=v%AIO_VERSION%",
@@ -230,10 +233,12 @@
"CLAMAV_ENABLED=%CLAMAV_ENABLED%", "CLAMAV_ENABLED=%CLAMAV_ENABLED%",
"CLAMAV_HOST=nextcloud-aio-clamav", "CLAMAV_HOST=nextcloud-aio-clamav",
"ONLYOFFICE_ENABLED=%ONLYOFFICE_ENABLED%", "ONLYOFFICE_ENABLED=%ONLYOFFICE_ENABLED%",
"EUROOFFICE_ENABLED=%EUROOFFICE_ENABLED%",
"COLLABORA_ENABLED=%COLLABORA_ENABLED%", "COLLABORA_ENABLED=%COLLABORA_ENABLED%",
"COLLABORA_HOST=nextcloud-aio-collabora", "COLLABORA_HOST=nextcloud-aio-collabora",
"TALK_ENABLED=%TALK_ENABLED%", "TALK_ENABLED=%TALK_ENABLED%",
"ONLYOFFICE_HOST=nextcloud-aio-onlyoffice", "ONLYOFFICE_HOST=nextcloud-aio-onlyoffice",
"EUROOFFICE_HOST=nextcloud-aio-eurooffice",
"UPDATE_NEXTCLOUD_APPS=%UPDATE_NEXTCLOUD_APPS%", "UPDATE_NEXTCLOUD_APPS=%UPDATE_NEXTCLOUD_APPS%",
"TZ=%TIMEZONE%", "TZ=%TIMEZONE%",
"TALK_PORT=%TALK_PORT%", "TALK_PORT=%TALK_PORT%",
@@ -357,6 +362,7 @@
"secrets": [ "secrets": [
"REDIS_PASSWORD", "REDIS_PASSWORD",
"ONLYOFFICE_SECRET", "ONLYOFFICE_SECRET",
"EUROOFFICE_SECRET",
"RECORDING_SECRET" "RECORDING_SECRET"
], ],
"restart": "unless-stopped", "restart": "unless-stopped",
@@ -758,6 +764,50 @@
"NET_RAW" "NET_RAW"
] ]
}, },
{
"container_name": "nextcloud-aio-eurooffice",
"image_tag": "%AIO_CHANNEL%",
"display_name": "EuroOffice",
"image": "ghcr.io/nextcloud-releases/aio-eurooffice",
"init": true,
"healthcheck": {
"start_period": "60s",
"test": "/healthcheck.sh",
"interval": "30s",
"timeout": "30s",
"start_interval": "5s",
"retries": 9
},
"expose": [
"80"
],
"internal_port": "80",
"environment": [
"AIO_LOG_LEVEL=%AIO_LOG_LEVEL%",
"LOG_LEVEL=%AIO_LOG_LEVEL%",
"TZ=%TIMEZONE%",
"JWT_ENABLED=true",
"JWT_HEADER=AuthorizationJwt",
"JWT_SECRET=%EUROOFFICE_SECRET%"
],
"volumes": [
{
"source": "nextcloud_aio_eurooffice",
"destination": "/var/lib/euro-office",
"writeable": true
}
],
"secrets": [
"EUROOFFICE_SECRET"
],
"restart": "unless-stopped",
"profiles": [
"eurooffice"
],
"cap_drop": [
"NET_RAW"
]
},
{ {
"container_name": "nextcloud-aio-imaginary", "container_name": "nextcloud-aio-imaginary",
"image_tag": "%AIO_CHANNEL%", "image_tag": "%AIO_CHANNEL%",
+16 -3
View File
@@ -22,9 +22,11 @@ document.addEventListener("DOMContentLoaded", function () {
// Office suite radio buttons // Office suite radio buttons
const collaboraRadio = document.getElementById('office-collabora'); const collaboraRadio = document.getElementById('office-collabora');
const onlyofficeRadio = document.getElementById('office-onlyoffice'); const onlyofficeRadio = document.getElementById('office-onlyoffice');
const euroofficeRadio = document.getElementById('office-eurooffice');
const noneRadio = document.getElementById('office-none'); const noneRadio = document.getElementById('office-none');
const collaboraHidden = document.getElementById('collabora'); const collaboraHidden = document.getElementById('collabora');
const onlyofficeHidden = document.getElementById('onlyoffice'); const onlyofficeHidden = document.getElementById('onlyoffice');
const euroofficeHidden = document.getElementById('eurooffice');
let initialOfficeSelection = null; let initialOfficeSelection = null;
optionsContainersCheckboxes.forEach(checkbox => { optionsContainersCheckboxes.forEach(checkbox => {
@@ -36,11 +38,13 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
// Store initial office suite selection // Store initial office suite selection
if (collaboraRadio && onlyofficeRadio && noneRadio) { if (collaboraRadio && onlyofficeRadio && euroofficeRadio && noneRadio) {
if (collaboraRadio.checked) { if (collaboraRadio.checked) {
initialOfficeSelection = 'collabora'; initialOfficeSelection = 'collabora';
} else if (onlyofficeRadio.checked) { } else if (onlyofficeRadio.checked) {
initialOfficeSelection = 'onlyoffice'; initialOfficeSelection = 'onlyoffice';
} else if (euroofficeRadio.checked) {
initialOfficeSelection = 'eurooffice';
} else { } else {
initialOfficeSelection = 'none'; initialOfficeSelection = 'none';
} }
@@ -57,20 +61,28 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
// Check office suite changes and sync to hidden inputs // Check office suite changes and sync to hidden inputs
if (collaboraRadio && onlyofficeRadio && noneRadio && collaboraHidden && onlyofficeHidden) { if (collaboraRadio && onlyofficeRadio && euroofficeRadio && noneRadio && collaboraHidden && onlyofficeHidden && euroofficeHidden) {
let currentOfficeSelection = null; let currentOfficeSelection = null;
if (collaboraRadio.checked) { if (collaboraRadio.checked) {
currentOfficeSelection = 'collabora'; currentOfficeSelection = 'collabora';
collaboraHidden.value = 'on'; collaboraHidden.value = 'on';
onlyofficeHidden.value = ''; onlyofficeHidden.value = '';
euroofficeHidden.value = '';
} else if (onlyofficeRadio.checked) { } else if (onlyofficeRadio.checked) {
currentOfficeSelection = 'onlyoffice'; currentOfficeSelection = 'onlyoffice';
collaboraHidden.value = ''; collaboraHidden.value = '';
onlyofficeHidden.value = 'on'; onlyofficeHidden.value = 'on';
euroofficeHidden.value = '';
} else if (euroofficeRadio.checked) {
currentOfficeSelection = 'eurooffice';
collaboraHidden.value = '';
onlyofficeHidden.value = '';
euroofficeHidden.value = 'on';
} else { } else {
currentOfficeSelection = 'none'; currentOfficeSelection = 'none';
collaboraHidden.value = ''; collaboraHidden.value = '';
onlyofficeHidden.value = ''; onlyofficeHidden.value = '';
euroofficeHidden.value = '';
} }
if (currentOfficeSelection !== initialOfficeSelection) { if (currentOfficeSelection !== initialOfficeSelection) {
@@ -144,9 +156,10 @@ document.addEventListener("DOMContentLoaded", function () {
handleTalkVisibility(); // Ensure talk-recording is correctly initialized handleTalkVisibility(); // Ensure talk-recording is correctly initialized
// Add event listeners for office suite radio buttons // Add event listeners for office suite radio buttons
if (collaboraRadio && onlyofficeRadio && noneRadio) { if (collaboraRadio && onlyofficeRadio && euroofficeRadio && noneRadio) {
collaboraRadio.addEventListener('change', checkForOptionContainerChanges); collaboraRadio.addEventListener('change', checkForOptionContainerChanges);
onlyofficeRadio.addEventListener('change', checkForOptionContainerChanges); onlyofficeRadio.addEventListener('change', checkForOptionContainerChanges);
euroofficeRadio.addEventListener('change', checkForOptionContainerChanges);
noneRadio.addEventListener('change', checkForOptionContainerChanges); noneRadio.addEventListener('change', checkForOptionContainerChanges);
} }
+6
View File
@@ -27,6 +27,12 @@ document.addEventListener("DOMContentLoaded", function(event) {
const onlyoffice = document.getElementById("office-onlyoffice"); const onlyoffice = document.getElementById("office-onlyoffice");
onlyoffice.disabled = true; onlyoffice.disabled = true;
// EuroOffice
const eurooffice = document.getElementById("office-eurooffice");
if (eurooffice) {
eurooffice.disabled = true;
}
// Imaginary // Imaginary
let imaginary = document.getElementById("imaginary"); let imaginary = document.getElementById("imaginary");
imaginary.disabled = true; imaginary.disabled = true;
+2
View File
@@ -104,6 +104,7 @@ $app->post('/api/docker/backup-test', AIO\Controller\DockerController::class . '
$app->post('/api/docker/restore', AIO\Controller\DockerController::class . ':StartBackupContainerRestore'); $app->post('/api/docker/restore', AIO\Controller\DockerController::class . ':StartBackupContainerRestore');
$app->post('/api/docker/stop', AIO\Controller\DockerController::class . ':StopContainer'); $app->post('/api/docker/stop', AIO\Controller\DockerController::class . ':StopContainer');
$app->post('/api/docker/backup-reset-location', AIO\Controller\DockerController::class . ':DeleteBorgBackupConfig'); $app->post('/api/docker/backup-reset-location', AIO\Controller\DockerController::class . ':DeleteBorgBackupConfig');
$app->post('/api/docker/nextcloud-upgrade-to-latest-major', AIO\Controller\DockerController::class . ':RunNextcloudUpgradeToLatestMajor');
$app->post('/api/docker/prune', AIO\Controller\DockerController::class . ':SystemPrune'); $app->post('/api/docker/prune', AIO\Controller\DockerController::class . ':SystemPrune');
$app->get('/api/docker/logs', AIO\Controller\DockerController::class . ':GetLogs'); $app->get('/api/docker/logs', AIO\Controller\DockerController::class . ':GetLogs');
$app->post('/api/auth/login', AIO\Controller\LoginController::class . ':TryLogin'); $app->post('/api/auth/login', AIO\Controller\LoginController::class . ':TryLogin');
@@ -152,6 +153,7 @@ $app->get('/containers', function (Request $request, Response $response, array $
'current_channel' => $dockerActionManager->GetCurrentChannel(), 'current_channel' => $dockerActionManager->GetCurrentChannel(),
'is_clamav_enabled' => $configurationManager->isClamavEnabled, 'is_clamav_enabled' => $configurationManager->isClamavEnabled,
'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled, 'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled,
'is_eurooffice_enabled' => $configurationManager->isEuroofficeEnabled,
'is_collabora_enabled' => $configurationManager->isCollaboraEnabled, 'is_collabora_enabled' => $configurationManager->isCollaboraEnabled,
'is_talk_enabled' => $configurationManager->isTalkEnabled, 'is_talk_enabled' => $configurationManager->isTalkEnabled,
'borg_restore_password' => $configurationManager->borgRestorePassword, 'borg_restore_password' => $configurationManager->borgRestorePassword,
+14 -4
View File
@@ -483,7 +483,7 @@ input[type="checkbox"]:disabled:not(:checked) + label {
visibility: hidden; visibility: hidden;
opacity: 0; opacity: 0;
align-self: start; align-self: start;
width: min(700px, calc(100vw - 4rem)); width: min(600px, calc(100vw - 4rem));
height: min(400px, calc(100vh - 14rem)); height: min(400px, calc(100vh - 14rem));
border-radius: var(--border-radius-large); border-radius: var(--border-radius-large);
border: solid thin rgb(192, 192, 192); border: solid thin rgb(192, 192, 192);
@@ -606,13 +606,15 @@ input[type="checkbox"]:disabled:not(:checked) + label {
} }
#office-collabora:checked + .office-card, #office-collabora:checked + .office-card,
#office-onlyoffice:checked + .office-card { #office-onlyoffice:checked + .office-card,
#office-eurooffice:checked + .office-card {
border-color: var(--color-nextcloud-blue); border-color: var(--color-nextcloud-blue);
background: linear-gradient(135deg, rgba(0, 130, 201, 0.08) 0%, rgba(0, 130, 201, 0.02) 100%); background: linear-gradient(135deg, rgba(0, 130, 201, 0.08) 0%, rgba(0, 130, 201, 0.02) 100%);
} }
[data-theme="dark"] #office-collabora:checked + .office-card, [data-theme="dark"] #office-collabora:checked + .office-card,
[data-theme="dark"] #office-onlyoffice:checked + .office-card { [data-theme="dark"] #office-onlyoffice:checked + .office-card,
[data-theme="dark"] #office-eurooffice:checked + .office-card {
background: linear-gradient(135deg, rgba(0, 145, 242, 0.15) 0%, rgba(0, 145, 242, 0.03) 100%); background: linear-gradient(135deg, rgba(0, 145, 242, 0.15) 0%, rgba(0, 145, 242, 0.03) 100%);
} }
@@ -631,13 +633,21 @@ input[type="checkbox"]:disabled:not(:checked) + label {
color: var(--color-main-text); color: var(--color-main-text);
} }
.office-powered-by {
margin: 4px 0 0;
font-size: 13px;
color: var(--color-main-text);
opacity: 0.7;
}
.office-checkmark { .office-checkmark {
flex-shrink: 0; flex-shrink: 0;
display: none; display: none;
} }
#office-collabora:checked + .office-card .office-checkmark, #office-collabora:checked + .office-card .office-checkmark,
#office-onlyoffice:checked + .office-card .office-checkmark { #office-onlyoffice:checked + .office-card .office-checkmark,
#office-eurooffice:checked + .office-card .office-checkmark {
display: block; display: block;
} }
+8
View File
@@ -78,6 +78,10 @@ readonly class ContainerDefinitionFetcher {
if (!$this->configurationManager->isOnlyofficeEnabled) { if (!$this->configurationManager->isOnlyofficeEnabled) {
continue; continue;
} }
} elseif ($entry['container_name'] === 'nextcloud-aio-eurooffice') {
if (!$this->configurationManager->isEuroofficeEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-collabora') { } elseif ($entry['container_name'] === 'nextcloud-aio-collabora') {
if (!$this->configurationManager->isCollaboraEnabled) { if (!$this->configurationManager->isCollaboraEnabled) {
continue; continue;
@@ -194,6 +198,10 @@ readonly class ContainerDefinitionFetcher {
if (!$this->configurationManager->isOnlyofficeEnabled) { if (!$this->configurationManager->isOnlyofficeEnabled) {
continue; continue;
} }
} elseif ($value === 'nextcloud-aio-eurooffice') {
if (!$this->configurationManager->isEuroofficeEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-collabora') { } elseif ($value === 'nextcloud-aio-collabora') {
if (!$this->configurationManager->isCollaboraEnabled) { if (!$this->configurationManager->isCollaboraEnabled) {
continue; continue;
@@ -81,12 +81,19 @@ readonly class ConfigurationController {
if ($officeSuiteChoice === 'collabora') { if ($officeSuiteChoice === 'collabora') {
$this->configurationManager->isCollaboraEnabled = true; $this->configurationManager->isCollaboraEnabled = true;
$this->configurationManager->isOnlyofficeEnabled = false; $this->configurationManager->isOnlyofficeEnabled = false;
$this->configurationManager->isEuroofficeEnabled = false;
} elseif ($officeSuiteChoice === 'onlyoffice') { } elseif ($officeSuiteChoice === 'onlyoffice') {
$this->configurationManager->isCollaboraEnabled = false; $this->configurationManager->isCollaboraEnabled = false;
$this->configurationManager->isOnlyofficeEnabled = true; $this->configurationManager->isOnlyofficeEnabled = true;
$this->configurationManager->isEuroofficeEnabled = false;
} elseif ($officeSuiteChoice === 'eurooffice') {
$this->configurationManager->isCollaboraEnabled = false;
$this->configurationManager->isOnlyofficeEnabled = false;
$this->configurationManager->isEuroofficeEnabled = true;
} else { } else {
$this->configurationManager->isCollaboraEnabled = false; $this->configurationManager->isCollaboraEnabled = false;
$this->configurationManager->isOnlyofficeEnabled = false; $this->configurationManager->isOnlyofficeEnabled = false;
$this->configurationManager->isEuroofficeEnabled = false;
} }
$this->configurationManager->isClamavEnabled = isset($request->getParsedBody()['clamav']); $this->configurationManager->isClamavEnabled = isset($request->getParsedBody()['clamav']);
$this->configurationManager->isTalkEnabled = isset($request->getParsedBody()['talk']); $this->configurationManager->isTalkEnabled = isset($request->getParsedBody()['talk']);
+31 -9
View File
@@ -14,6 +14,7 @@ use Slim\Psr7\NonBufferedBody;
readonly class DockerController { readonly class DockerController {
private const string TOP_CONTAINER = 'nextcloud-aio-apache'; private const string TOP_CONTAINER = 'nextcloud-aio-apache';
private const string LATEST_MAJOR_VERSION = '34';
public function __construct( public function __construct(
private DockerActionManager $dockerActionManager, private DockerActionManager $dockerActionManager,
@@ -221,7 +222,7 @@ readonly class DockerController {
} }
if (isset($request->getParsedBody()['install_latest_major'])) { if (isset($request->getParsedBody()['install_latest_major'])) {
$installLatestMajor = '34'; $installLatestMajor = self::LATEST_MAJOR_VERSION;
} else { } else {
$installLatestMajor = ''; $installLatestMajor = '';
} }
@@ -298,7 +299,7 @@ readonly class DockerController {
} }
if ($addToStreamingResponseBody !== null) { if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody($container, "Stopping container"); $addToStreamingResponseBody("Stopping container", $container);
} }
// Stop itself first and then all the dependencies // Stop itself first and then all the dependencies
@@ -333,14 +334,30 @@ readonly class DockerController {
return $response->withStatus(201)->withHeader('Location', '.'); return $response->withStatus(201)->withHeader('Location', '.');
} }
public function RunNextcloudUpgradeToLatestMajor(Request $request, Response $response, array $args) : Response {
$this->configurationManager->installLatestMajor = self::LATEST_MAJOR_VERSION;
// Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response);
$addToStreamingResponseBody = $this->getAddToStreamingResponseBody($nonbufResp);
$this->dockerActionManager->RunNextcloudUpgradeToLatestMajor($addToStreamingResponseBody);
// We automatically reload after 10s so that the output can be read or copied if necessary
$addToStreamingResponseBody("Automatically reloading the page after 10s.");
sleep(10);
// End streaming response
$this->finalizeStreamingResponse($nonbufResp);
return $nonbufResp;
}
public function SystemPrune(Request $request, Response $response, array $args) : Response { public function SystemPrune(Request $request, Response $response, array $args) : Response {
// Get streaming response start and closure // Get streaming response start and closure
$nonbufResp = $this->startStreamingResponse($response); $nonbufResp = $this->startStreamingResponse($response);
$body = $nonbufResp->getBody(); $body = $nonbufResp->getBody();
$addToStreamingResponseBody = function (string $message) use ($body) : void { $addToStreamingResponseBody = $this->getAddToStreamingResponseBody($nonbufResp);
$body->write("<div>$message</div>");
};
$this->dockerActionManager->SystemPrune($addToStreamingResponseBody); $this->dockerActionManager->SystemPrune($addToStreamingResponseBody);
@@ -401,7 +418,7 @@ readonly class DockerController {
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en" class="overlay-iframe"> <html lang="en" class="overlay-iframe">
<head> <head>
<link rel="stylesheet" href="../../style.css?v9" media="all" /> <link rel="stylesheet" href="../../style.css?v11" media="all" />
<script type="text/javascript" src="../../apply-theme.js?v1"></script> <script type="text/javascript" src="../../apply-theme.js?v1"></script>
<script type="text/javascript" src="../../scroll-into-view.js"></script> <script type="text/javascript" src="../../scroll-into-view.js"></script>
</head> </head>
@@ -426,12 +443,17 @@ readonly class DockerController {
return $nonbufResp; return $nonbufResp;
} }
private function getAddToStreamingResponseBody(Response $nonbufResp) : ?\Closure { private function getAddToStreamingResponseBody(Response $nonbufResp) : \Closure {
// Create a closure to pass around to the code, which should to the logging (because it e.g. decides // Create a closure to pass around to the code, which should to the logging (because it e.g. decides
// if it'll actually pull an image), but which should not need to know anything about the // if it'll actually pull an image), but which should not need to know anything about the
// wanted markup or formatting. // wanted markup or formatting.
$addToStreamingResponseBody = function (Container $container, string $message) use ($nonbufResp) : void { $addToStreamingResponseBody = function (string $message, ?Container $container = null) use ($nonbufResp) : void {
$nonbufResp->getBody()->write("<div>{$container->displayName}: {$message}</div>"); // Strip ANSI codes.
$message = preg_replace('/\e[[][A-Za-z0-9];?[0-9]*m?/', '', $message);
if ($container) {
$message = "{$container->displayName}: {$message}";
}
$nonbufResp->getBody()->write("<div>" . htmlspecialchars("{$message}", ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8') . "</div>");
}; };
return $addToStreamingResponseBody; return $addToStreamingResponseBody;
+6
View File
@@ -99,6 +99,11 @@ class ConfigurationManager
set { $this->set('isOnlyofficeEnabled', $value); } set { $this->set('isOnlyofficeEnabled', $value); }
} }
public bool $isEuroofficeEnabled {
get => $this->get('isEuroofficeEnabled', false);
set { $this->set('isEuroofficeEnabled', $value); }
}
public bool $isCollaboraEnabled { public bool $isCollaboraEnabled {
// Type-cast because old configs could have 1/0 for this key. // Type-cast because old configs could have 1/0 for this key.
get => (bool) $this->get('isCollaboraEnabled', true); get => (bool) $this->get('isCollaboraEnabled', true);
@@ -1087,6 +1092,7 @@ class ConfigurationManager
'CLAMAV_ENABLED' => $this->isClamavEnabled ? 'yes' : '', 'CLAMAV_ENABLED' => $this->isClamavEnabled ? 'yes' : '',
'TALK_RECORDING_ENABLED' => $this->isTalkRecordingEnabled ? 'yes' : '', 'TALK_RECORDING_ENABLED' => $this->isTalkRecordingEnabled ? 'yes' : '',
'ONLYOFFICE_ENABLED' => $this->isOnlyofficeEnabled ? 'yes' : '', 'ONLYOFFICE_ENABLED' => $this->isOnlyofficeEnabled ? 'yes' : '',
'EUROOFFICE_ENABLED' => $this->isEuroofficeEnabled ? 'yes' : '',
'COLLABORA_ENABLED' => $this->isCollaboraEnabled ? 'yes' : '', 'COLLABORA_ENABLED' => $this->isCollaboraEnabled ? 'yes' : '',
'TALK_ENABLED' => $this->isTalkEnabled ? 'yes' : '', 'TALK_ENABLED' => $this->isTalkEnabled ? 'yes' : '',
'UPDATE_NEXTCLOUD_APPS' => ($this->isDailyBackupRunning() && $this->areAutomaticUpdatesEnabled()) ? 'yes' : '', 'UPDATE_NEXTCLOUD_APPS' => ($this->isDailyBackupRunning() && $this->areAutomaticUpdatesEnabled()) ? 'yes' : '',
+77 -42
View File
@@ -12,6 +12,7 @@ use AIO\Data\DataConst;
use AIO\Helper\NetworkHelper; use AIO\Helper\NetworkHelper;
use GuzzleHttp\Client; use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException; use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Utils;
use http\Env\Response; use http\Env\Response;
readonly class DockerActionManager { readonly class DockerActionManager {
@@ -48,7 +49,7 @@ readonly class DockerActionManager {
public function GetContainerRunningState(Container $container): ContainerState { public function GetContainerRunningState(Container $container): ContainerState {
$url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->identifier))); $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->identifier)));
try { try {
$response = $this->guzzleClient->get($url); $response = $this->sendHttpRequest('GET', $url);
} catch (RequestException $e) { } catch (RequestException $e) {
if ($e->getCode() === 404) { if ($e->getCode() === 404) {
return ContainerState::ImageDoesNotExist; return ContainerState::ImageDoesNotExist;
@@ -68,7 +69,7 @@ readonly class DockerActionManager {
public function GetContainerRestartingState(Container $container): ContainerState { public function GetContainerRestartingState(Container $container): ContainerState {
$url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->identifier))); $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->identifier)));
try { try {
$response = $this->guzzleClient->get($url); $response = $this->sendHttpRequest('GET', $url);
} catch (RequestException $e) { } catch (RequestException $e) {
if ($e->getCode() === 404) { if ($e->getCode() === 404) {
return ContainerState::ImageDoesNotExist; return ContainerState::ImageDoesNotExist;
@@ -138,7 +139,7 @@ readonly class DockerActionManager {
public function DeleteContainer(Container $container): void { public function DeleteContainer(Container $container): void {
$url = $this->BuildApiUrl(sprintf('containers/%s?v=true', urlencode($container->identifier))); $url = $this->BuildApiUrl(sprintf('containers/%s?v=true', urlencode($container->identifier)));
try { try {
$this->guzzleClient->delete($url); $this->sendHttpRequest('DELETE', $url);
} catch (RequestException $e) { } catch (RequestException $e) {
if ($e->getCode() !== 404) { if ($e->getCode() !== 404) {
throw $e; throw $e;
@@ -155,7 +156,7 @@ readonly class DockerActionManager {
// Delete the borg cache volume // Delete the borg cache volume
$url = $this->BuildApiUrl('volumes/nextcloud_aio_backup_cache'); $url = $this->BuildApiUrl('volumes/nextcloud_aio_backup_cache');
try { try {
$this->guzzleClient->delete($url); $this->sendHttpRequest('DELETE', $url);
error_log('nextcloud_aio_backup_cache volume deleted successfully.'); error_log('nextcloud_aio_backup_cache volume deleted successfully.');
} catch (RequestException $e) { } catch (RequestException $e) {
if ($e->getCode() !== 404) { if ($e->getCode() !== 404) {
@@ -174,7 +175,7 @@ readonly class DockerActionManager {
urlencode($id), urlencode($id),
$since $since
)); ));
$responseBody = (string)$this->guzzleClient->get($url)->getBody(); $responseBody = (string)$this->sendHttpRequest('GET', $url)->getBody();
$response = ""; $response = "";
$separator = "\r\n"; $separator = "\r\n";
@@ -194,9 +195,9 @@ readonly class DockerActionManager {
$url = $this->BuildApiUrl(sprintf('containers/%s/start', urlencode($container->identifier))); $url = $this->BuildApiUrl(sprintf('containers/%s/start', urlencode($container->identifier)));
try { try {
if ($addToStreamingResponseBody !== null) { if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody($container, "Starting container"); $addToStreamingResponseBody("Starting container", $container);
} }
$this->guzzleClient->post($url); $this->sendHttpRequest('POST', $url);
} catch (RequestException $e) { } catch (RequestException $e) {
throw new \Exception("Could not start container " . $container->identifier . ": " . $e->getResponse()?->getBody()->getContents()); throw new \Exception("Could not start container " . $container->identifier . ": " . $e->getResponse()?->getBody()->getContents());
} }
@@ -215,7 +216,7 @@ readonly class DockerActionManager {
$firstChar = substr($volume->name, 0, 1); $firstChar = substr($volume->name, 0, 1);
if (!in_array($firstChar, $forbiddenChars)) { if (!in_array($firstChar, $forbiddenChars)) {
$this->guzzleClient->request( $this->sendHttpRequest(
'POST', 'POST',
$url, $url,
[ [
@@ -494,7 +495,7 @@ readonly class DockerActionManager {
$url = $this->BuildApiUrl('containers/create?name=' . $container->identifier); $url = $this->BuildApiUrl('containers/create?name=' . $container->identifier);
try { try {
$this->guzzleClient->request( $this->sendHttpRequest(
'POST', 'POST',
$url, $url,
[ [
@@ -551,10 +552,10 @@ readonly class DockerActionManager {
$imageIsThere = true; $imageIsThere = true;
try { try {
if ($addToStreamingResponseBody) { if ($addToStreamingResponseBody) {
$addToStreamingResponseBody($container, "Pulling image"); $addToStreamingResponseBody("Pulling image", $container);
} }
$imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $encodedImageName)); $imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $encodedImageName));
$this->guzzleClient->get($imageUrl)->getBody()->getContents(); $this->sendHttpRequest('GET', $imageUrl)->getBody()->getContents();
} catch (\Throwable $e) { } catch (\Throwable $e) {
$imageIsThere = false; $imageIsThere = false;
} }
@@ -562,7 +563,7 @@ readonly class DockerActionManager {
$maxRetries = 3; $maxRetries = 3;
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) { for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
try { try {
$this->guzzleClient->post($url); $this->sendHttpRequest('POST', $url);
break; break;
} catch (RequestException $e) { } catch (RequestException $e) {
$message = "Could not pull image " . $imageName . " (attempt $attempt/$maxRetries): " . $e->getResponse()?->getBody()->getContents(); $message = "Could not pull image " . $imageName . " (attempt $attempt/$maxRetries): " . $e->getResponse()?->getBody()->getContents();
@@ -647,11 +648,11 @@ readonly class DockerActionManager {
private function GetRepoDigestsOfContainer(string $containerName): ?array { private function GetRepoDigestsOfContainer(string $containerName): ?array {
try { try {
$containerUrl = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName)); $containerUrl = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName));
$containerOutput = json_decode($this->guzzleClient->get($containerUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); $containerOutput = json_decode($this->sendHttpRequest('GET', $containerUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
$imageName = $containerOutput['Image']; $imageName = $containerOutput['Image'];
$imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $imageName)); $imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $imageName));
$imageOutput = json_decode($this->guzzleClient->get($imageUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); $imageOutput = json_decode($this->sendHttpRequest('GET', $imageUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
if (!isset($imageOutput['RepoDigests'])) { if (!isset($imageOutput['RepoDigests'])) {
error_log('RepoDigests is not set of container ' . $containerName); error_log('RepoDigests is not set of container ' . $containerName);
@@ -695,7 +696,7 @@ readonly class DockerActionManager {
$containerName = 'nextcloud-aio-mastercontainer'; $containerName = 'nextcloud-aio-mastercontainer';
$url = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName)); $url = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName));
try { try {
$output = json_decode($this->guzzleClient->get($url)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); $output = json_decode($this->sendHttpRequest('GET', $url)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
$imageNameArray = explode(':', $output['Config']['Image']); $imageNameArray = explode(':', $output['Config']['Image']);
if (count($imageNameArray) === 2) { if (count($imageNameArray) === 2) {
$imageName = $imageNameArray[0]; $imageName = $imageNameArray[0];
@@ -722,7 +723,7 @@ readonly class DockerActionManager {
$containerName = 'nextcloud-aio-mastercontainer'; $containerName = 'nextcloud-aio-mastercontainer';
$url = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName)); $url = $this->BuildApiUrl(sprintf('containers/%s/json', $containerName));
try { try {
$output = json_decode($this->guzzleClient->get($url)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); $output = json_decode($this->sendHttpRequest('GET', $url)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
$tagArray = explode(':', $output['Config']['Image']); $tagArray = explode(':', $output['Config']['Image']);
if (count($tagArray) === 2) { if (count($tagArray) === 2) {
$tag = $tagArray[1]; $tag = $tagArray[1];
@@ -763,26 +764,37 @@ readonly class DockerActionManager {
} }
public function sendNotification(Container $container, string $subject, string $message, string $file = '/notify.sh'): void { public function sendNotification(Container $container, string $subject, string $message, string $file = '/notify.sh'): void {
if ($this->GetContainerStartingState($container) === ContainerState::Running) { $this->execCommandInContainer($container, ['bash', $file, $subject, $message]);
}
public function execCommandInContainer(Container $container, array $cmd, ?\Closure $outputCallback = null): void {
if ($cmd === []) {
throw new \InvalidArgumentException('$cmd must not be empty.');
}
foreach ($cmd as $arg) {
if (!is_string($arg) || $arg === '') {
throw new \InvalidArgumentException('Every element of $cmd must be a non-empty string.');
}
}
if ($this->GetContainerStartingState($container) !== ContainerState::Running) {
return;
}
$containerName = $container->identifier; $containerName = $container->identifier;
// schedule the exec // Create exec instance
$url = $this->BuildApiUrl(sprintf('containers/%s/exec', urlencode($containerName))); $url = $this->BuildApiUrl(sprintf('containers/%s/exec', urlencode($containerName)));
$response = json_decode( $response = json_decode(
$this->guzzleClient->request( $this->sendHttpRequest(
'POST', 'POST',
$url, $url,
[ [
'json' => [ 'json' => [
'AttachStdout' => true, 'AttachStdout' => true,
'AttachStderr' => true,
'Tty' => true, 'Tty' => true,
'Cmd' => [ 'Cmd' => $cmd,
'bash',
$file,
$subject,
$message
],
], ],
] ]
)->getBody()->getContents(), )->getBody()->getContents(),
@@ -791,20 +803,30 @@ readonly class DockerActionManager {
JSON_THROW_ON_ERROR, JSON_THROW_ON_ERROR,
); );
$id = $response['Id']; $execId = $response['Id'];
// start the exec // Start exec
$url = $this->BuildApiUrl(sprintf('exec/%s/start', $id)); $url = $this->BuildApiUrl(sprintf('exec/%s/start', $execId));
$this->guzzleClient->request( $requestOptions = [
'POST',
$url,
[
'json' => [ 'json' => [
'Detach' => false, 'Detach' => false,
'Tty' => true, 'Tty' => true,
], ],
] ];
); if ($outputCallback !== null) {
$requestOptions['stream'] = true;
}
$startResponse = $this->sendHttpRequest('POST', $url, $requestOptions);
if ($outputCallback !== null) {
$body = $startResponse->getBody();
while (!$body->eof()) {
$line = rtrim(Utils::readLine($body), "\r");;
if ($line !== '') {
$outputCallback($line);
}
}
} }
} }
@@ -815,7 +837,7 @@ readonly class DockerActionManager {
); );
try { try {
$this->guzzleClient->request( $this->sendHttpRequest(
'POST', 'POST',
$url, $url,
[ [
@@ -836,7 +858,7 @@ readonly class DockerActionManager {
if ($createNetwork) { if ($createNetwork) {
$url = $this->BuildApiUrl('networks/create'); $url = $this->BuildApiUrl('networks/create');
try { try {
$this->guzzleClient->request( $this->sendHttpRequest(
'POST', 'POST',
$url, $url,
[ [
@@ -865,7 +887,7 @@ readonly class DockerActionManager {
} }
try { try {
$this->guzzleClient->request( $this->sendHttpRequest(
'POST', 'POST',
$url, $url,
[ [
@@ -910,7 +932,7 @@ readonly class DockerActionManager {
} }
$url = $this->BuildApiUrl(sprintf('containers/%s/stop?t=%s', urlencode($container->identifier), $maxShutDownTime)); $url = $this->BuildApiUrl(sprintf('containers/%s/stop?t=%s', urlencode($container->identifier), $maxShutDownTime));
try { try {
$this->guzzleClient->post($url); $this->sendHttpRequest('POST', $url);
} catch (RequestException $e) { } catch (RequestException $e) {
if ($e->getCode() !== 404 && $e->getCode() !== 304) { if ($e->getCode() !== 404 && $e->getCode() !== 304) {
throw $e; throw $e;
@@ -922,7 +944,7 @@ readonly class DockerActionManager {
$containerName = 'nextcloud-aio-borgbackup'; $containerName = 'nextcloud-aio-borgbackup';
$url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($containerName))); $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($containerName)));
try { try {
$response = $this->guzzleClient->get($url); $response = $this->sendHttpRequest('GET', $url);
} catch (RequestException $e) { } catch (RequestException $e) {
if ($e->getCode() === 404) { if ($e->getCode() === 404) {
return -1; return -1;
@@ -944,7 +966,7 @@ readonly class DockerActionManager {
$containerName = 'nextcloud-aio-database'; $containerName = 'nextcloud-aio-database';
$url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($containerName))); $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($containerName)));
try { try {
$response = $this->guzzleClient->get($url); $response = $this->sendHttpRequest('GET', $url);
} catch (RequestException $e) { } catch (RequestException $e) {
if ($e->getCode() === 404) { if ($e->getCode() === 404) {
return -1; return -1;
@@ -984,7 +1006,7 @@ readonly class DockerActionManager {
$imageName = $imageName . ':' . $this->GetCurrentChannel(); $imageName = $imageName . ':' . $this->GetCurrentChannel();
try { try {
$imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $imageName)); $imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $imageName));
$imageOutput = json_decode($this->guzzleClient->get($imageUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR); $imageOutput = json_decode($this->sendHttpRequest('GET', $imageUrl)->getBody()->getContents(), true, 512, JSON_THROW_ON_ERROR);
if (!isset($imageOutput['Created'])) { if (!isset($imageOutput['Created'])) {
error_log('Created is not set of image ' . $imageName); error_log('Created is not set of image ' . $imageName);
@@ -1029,6 +1051,11 @@ readonly class DockerActionManager {
} }
} }
public function RunNextcloudUpgradeToLatestMajor(\Closure $addToStreamingResponseBody): void {
$container = $this->containerDefinitionFetcher->GetContainerById('nextcloud-aio-nextcloud');
$this->execCommandInContainer($container, ['bash', '/upgrade-latest-major.sh'], $addToStreamingResponseBody);
}
public function SystemPrune(?\Closure $addToStreamingResponseBody = null): void { public function SystemPrune(?\Closure $addToStreamingResponseBody = null): void {
$endpoints = [ $endpoints = [
// Remove stopped containers // Remove stopped containers
@@ -1057,7 +1084,7 @@ readonly class DockerActionManager {
} }
try { try {
$response = $this->guzzleClient->post($url); $response = $this->sendHttpRequest('POST', $url);
if ($addToStreamingResponseBody !== null) { if ($addToStreamingResponseBody !== null) {
$data = json_decode((string)$response->getBody(), true); $data = json_decode((string)$response->getBody(), true);
$deleted = 0; $deleted = 0;
@@ -1095,4 +1122,12 @@ readonly class DockerActionManager {
sleep(10); sleep(10);
} }
} }
protected function sendHttpRequest(string $httpMethod, string $url, array $requestOptions = []): \Psr\Http\Message\ResponseInterface {
if (($requestOptions['stream'] ?? null) === true) {
$requestOptions['proxy'] = 'unix:///var/run/docker.sock';
}
return $this->guzzleClient->request($httpMethod, $url, $requestOptions);
}
} }
+7 -2
View File
@@ -27,7 +27,7 @@
<script type="text/javascript" src="timezone.js?v1"></script> <script type="text/javascript" src="timezone.js?v1"></script>
{# js for optional containers and additional containers forms #} {# js for optional containers and additional containers forms #}
<script type="text/javascript" src="containers-form-submit.js?v7"></script> <script type="text/javascript" src="containers-form-submit.js?v8"></script>
{% set hasBackupLocation = borg_backup_host_location or borg_remote_repo %} {% set hasBackupLocation = borg_backup_host_location or borg_remote_repo %}
{% set isAnyRunning = false %} {% set isAnyRunning = false %}
@@ -298,7 +298,12 @@
{% if newMajorVersionString != '' and isAnyRunning == true and isApacheStarting != true %} {% if newMajorVersionString != '' and isAnyRunning == true and isApacheStarting != true %}
<details> <details>
<summary>Note about <strong>Nextcloud Hub {{ newMajorVersionString }}</strong></summary> <summary>Note about <strong>Nextcloud Hub {{ newMajorVersionString }}</strong></summary>
<p>If you haven't upgraded to Nextcloud Hub {{ newMajorVersionString }} yet and want to do that now, feel free to follow <strong><a target="_blank" href="https://github.com/nextcloud/all-in-one/discussions/8223">this documentation</a></strong></p> <p>If you haven't upgraded to Nextcloud Hub {{ newMajorVersionString }} yet and want to do that now, feel free to click the button below. ⚠️ Warning: make sure to create a backup before clicking the button as the update can go wrong and will leave your instance in a broken state!</p>
<form method="POST" action="api/docker/nextcloud-upgrade-to-latest-major" target="overlay-log">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input type="submit" value="Upgrade to Nextcloud Hub {{ newMajorVersionString }}" data-confirm="Upgrade to Nextcloud Hub {{ newMajorVersionString }}? You should consider creating a backup first." />
</form>
</details> </details>
{% endif %} {% endif %}
{% endif %} {% endif %}
+1 -1
View File
@@ -1 +1 @@
13.1.0 13.2.0
@@ -14,6 +14,42 @@
<p>Choose your preferred office suite. Only one can be enabled at a time.</p> <p>Choose your preferred office suite. Only one can be enabled at a time.</p>
{% endif %} {% endif %}
<div class="office-suite-cards"> <div class="office-suite-cards">
<input
type="radio"
id="office-eurooffice"
name="office_suite_choice"
value="eurooffice"
class="office-radio"
{% if is_eurooffice_enabled == true %}
checked="checked"
{% endif %}
>
<label class="office-card{{ isAnyRunning ? ' office-card-disabled' : '' }}" for="office-eurooffice">
<div class="office-card-header">
<div>
<h4>Nextcloud Office</h4>
<p class="office-powered-by">powered by Euro-Office</p>
</div>
<svg class="office-checkmark" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="var(--color-nextcloud-blue)"/>
<path d="M7 12L10.5 15.5L17 9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<ul class="office-features">
<li>Good Nextcloud integration</li>
<li>Open source</li>
<li>Best performance</li>
<li>Limited ODF compatibility</li>
<li>Best Microsoft compatibility</li>
<li>Good security</li>
</ul>
{% if isAnyRunning == false %}
<a href="https://github.com/Euro-Office" target="_blank" class="office-learn-more" data-stop-event-propagation="true">
Learn more
</a>
{% endif %}
</label>
<input type="hidden" id="eurooffice" name="eurooffice" value="" data-initial-state="{% if is_eurooffice_enabled == true %}true{% else %}false{% endif %}">
<input <input
type="radio" type="radio"
id="office-collabora" id="office-collabora"
@@ -26,7 +62,10 @@
> >
<label class="office-card{{ isAnyRunning ? ' office-card-disabled' : '' }}" for="office-collabora"> <label class="office-card{{ isAnyRunning ? ' office-card-disabled' : '' }}" for="office-collabora">
<div class="office-card-header"> <div class="office-card-header">
<div>
<h4>Nextcloud Office</h4> <h4>Nextcloud Office</h4>
<p class="office-powered-by">powered by Collabora Online</p>
</div>
<svg class="office-checkmark" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="office-checkmark" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="var(--color-nextcloud-blue)"/> <circle cx="12" cy="12" r="10" fill="var(--color-nextcloud-blue)"/>
<path d="M7 12L10.5 15.5L17 9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 12L10.5 15.5L17 9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
@@ -36,9 +75,9 @@
<li>Best Nextcloud integration</li> <li>Best Nextcloud integration</li>
<li>Open source</li> <li>Open source</li>
<li>Good performance</li> <li>Good performance</li>
<li>Best security: documents never leave your server</li>
<li>Best ODF compatibility</li> <li>Best ODF compatibility</li>
<li>Best support for legacy files</li> <li>Best support for legacy files</li>
<li>Best security: documents never leave your server</li>
</ul> </ul>
{% if isAnyRunning == false %} {% if isAnyRunning == false %}
<a href="https://www.collaboraoffice.com/code/" target="_blank" class="office-learn-more" data-stop-event-propagation="true"> <a href="https://www.collaboraoffice.com/code/" target="_blank" class="office-learn-more" data-stop-event-propagation="true">
@@ -60,18 +99,22 @@
> >
<label class="office-card{{ isAnyRunning ? ' office-card-disabled' : '' }}" for="office-onlyoffice"> <label class="office-card{{ isAnyRunning ? ' office-card-disabled' : '' }}" for="office-onlyoffice">
<div class="office-card-header"> <div class="office-card-header">
<h4>OnlyOffice</h4> <div>
<h4>ONLYOFFICE</h4>
<p class="office-powered-by">by Ascensio System SIA</p>
</div>
<svg class="office-checkmark" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg class="office-checkmark" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="12" cy="12" r="10" fill="var(--color-nextcloud-blue)"/> <circle cx="12" cy="12" r="10" fill="var(--color-nextcloud-blue)"/>
<path d="M7 12L10.5 15.5L17 9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/> <path d="M7 12L10.5 15.5L17 9" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg> </svg>
</div> </div>
<ul class="office-features"> <ul class="office-features">
<li>Good Nextcloud integration</li> <li>OK Nextcloud integration</li>
<li>Open core</li> <li>Open Core</li>
<li>Best performance</li> <li>Best performance</li>
<li>Best Microsoft compatibility</li>
<li>Limited ODF compatibility</li> <li>Limited ODF compatibility</li>
<li>Best Microsoft compatibility</li>
<li>Good security</li>
</ul> </ul>
{% if isAnyRunning == false %} {% if isAnyRunning == false %}
<a href="https://www.onlyoffice.com/" target="_blank" class="office-learn-more" data-stop-event-propagation="true"> <a href="https://www.onlyoffice.com/" target="_blank" class="office-learn-more" data-stop-event-propagation="true">
@@ -90,7 +133,7 @@
name="office_suite_choice" name="office_suite_choice"
value="" value=""
class="office-radio" class="office-radio"
{% if is_collabora_enabled == false and is_onlyoffice_enabled == false %} {% if is_collabora_enabled == false and is_onlyoffice_enabled == false and is_eurooffice_enabled == false %}
checked="checked" checked="checked"
{% endif %} {% endif %}
> >
@@ -224,7 +267,7 @@
</form> </form>
<p><strong>Minimal system requirements:</strong> When any optional container is enabled, at least 2GB RAM, a dual-core CPU and 40GB system storage are required. When enabling ClamAV, Nextcloud Talk Recording-server or Fulltextsearch, at least 3GB RAM are required. For Talk Recording-server additional 2 vCPUs are required. When enabling everything, at least 5GB RAM and a quad-core CPU are required. Recommended are at least 1GB more RAM than the minimal requirement. For further advice and recommendations see <strong><a target="_blank" href="https://github.com/nextcloud/all-in-one/discussions/1335">this documentation</a></strong></p> <p><strong>Minimal system requirements:</strong> When any optional container is enabled, at least 2GB RAM, a dual-core CPU and 40GB system storage are required. When enabling ClamAV, Nextcloud Talk Recording-server or Fulltextsearch, at least 3GB RAM are required. For Talk Recording-server additional 2 vCPUs are required. When enabling everything, at least 5GB RAM and a quad-core CPU are required. Recommended are at least 1GB more RAM than the minimal requirement. For further advice and recommendations see <strong><a target="_blank" href="https://github.com/nextcloud/all-in-one/discussions/1335">this documentation</a></strong></p>
{% if isAnyRunning == true %} {% if isAnyRunning == true %}
<script type="text/javascript" src="disable-containers.js"></script> <script type="text/javascript" src="disable-containers.js?v1"></script>
{% endif %} {% endif %}
{% if is_collabora_enabled == true and isAnyRunning == false and was_start_button_clicked == true %} {% if is_collabora_enabled == true and isAnyRunning == false and was_start_button_clicked == true %}
+1 -1
View File
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>AIO</title> <title>AIO</title>
<link rel="stylesheet" href="style.css?v10" media="all" /> <link rel="stylesheet" href="style.css?v11" media="all" />
<link rel="icon" href="img/favicon.png"> <link rel="icon" href="img/favicon.png">
<script type="text/javascript" src="forms.js?v2"></script> <script type="text/javascript" src="forms.js?v2"></script>
<script type="text/javascript" src="toggle-dark-mode.js?v2"></script> <script type="text/javascript" src="toggle-dark-mode.js?v2"></script>
+1 -1
View File
@@ -2,7 +2,7 @@
<head> <head>
<title>AIO</title> <title>AIO</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/> <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<link rel="stylesheet" href="style.css?v1"> <link rel="stylesheet" href="style.css?v11">
<link rel="stylesheet" href="logs.css?v1"> <link rel="stylesheet" href="logs.css?v1">
<link rel="icon" href="img/favicon.png"> <link rel="icon" href="img/favicon.png">
<script src="apply-theme.js?v1"></script> <script src="apply-theme.js?v1"></script>
+1
View File
@@ -118,6 +118,7 @@ flowchart TB
subgraph OPT[" 🧩 Optional Built-in Containers (enable in AIO interface) "] subgraph OPT[" 🧩 Optional Built-in Containers (enable in AIO interface) "]
COLLA(["📄 Nextcloud Office"]):::opt COLLA(["📄 Nextcloud Office"]):::opt
OO(["📄 OnlyOffice\nDocument Server"]):::opt OO(["📄 OnlyOffice\nDocument Server"]):::opt
EO(["📄 EuroOffice\nDocument Server"]):::opt
TALK(["🎙️ Talk\nVideo & Voice calls"]):::opt TALK(["🎙️ Talk\nVideo & Voice calls"]):::opt
TALKREC(["🎬 Talk Recording"]):::opt TALKREC(["🎬 Talk Recording"]):::opt
FTS(["🔎 Full-text Search\n(Elasticsearch)"]):::opt FTS(["🔎 Full-text Search\n(Elasticsearch)"]):::opt
+1 -1
View File
@@ -13,7 +13,7 @@ For the below to work, it is important that you have a domain that you point ont
- [ ] Below that you should see a section `Optional addons` which shows a checkbox list with addons that can be enabled or disabled. - [ ] Below that you should see a section `Optional addons` which shows a checkbox list with addons that can be enabled or disabled.
- [ ] Collabora, Imaginary, Talk and Whiteboard should be enabled, the rest disabled - [ ] Collabora, Imaginary, Talk and Whiteboard should be enabled, the rest disabled
- [ ] Unchecking/Checking any of these should insert a button that allows to save the set config - [ ] Unchecking/Checking any of these should insert a button that allows to save the set config
- [ ] Checking OnlyOffice and Collabora at the same time should show a warning that this is not supported and should not saving the new config - [ ] Only one of Collabora, OnlyOffice and EuroOffice should be selectable at the same time
- [ ] Recommended is to uncheck all options now - [ ] Recommended is to uncheck all options now
- [ ] Clicking on the save button should reload the page and activate the new config - [ ] Clicking on the save button should reload the page and activate the new config
- [ ] Clickig on the `Start containers` button should finally reveal a big spinning wheel that should block all elements on the side of being clicked. - [ ] Clickig on the `Start containers` button should finally reveal a big spinning wheel that should block all elements on the side of being clicked.