Compare commits

..

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
57f0aa3250 Use CSS custom property for warning color
Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-02-11 17:12:05 +00:00
copilot-swe-agent[bot]
afe3ec1699 Refactor: Use CSS class instead of inline styles for update warning
Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-02-11 17:11:32 +00:00
copilot-swe-agent[bot]
6297358d48 Add per-container update status indicator in AIO interface
Co-authored-by: szaimen <42591237+szaimen@users.noreply.github.com>
2026-02-11 17:10:53 +00:00
copilot-swe-agent[bot]
bd772e19ca Initial plan 2026-02-11 17:07:10 +00:00
92 changed files with 273 additions and 917 deletions

View File

@@ -13,7 +13,7 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2
with:
php-version: 8.5
php-version: 8.4
extensions: apcu
- name: Run dependency update script
run: |

View File

@@ -30,7 +30,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
php-versions: [ "8.5" ]
php-versions: [ "8.4" ]
name: php-lint

View File

@@ -36,7 +36,7 @@ jobs:
line-length: warning
- name: Install the latest version of uv
uses: astral-sh/setup-uv@eac588ad8def6316056a12d4907a9d4d84ff7a3b # v7.3.0
uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1
- name: Check GitHub actions
run: uvx zizmor --min-severity medium .github/workflows/*.yml

View File

@@ -20,7 +20,7 @@ jobs:
- name: Set up php
uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2
with:
php-version: 8.5
php-version: 8.4
extensions: apcu
coverage: none

View File

@@ -36,11 +36,11 @@ jobs:
- name: Install Playwright Browsers
run: cd php/tests && npx playwright install --with-deps chromium
- name: Set up php 8.5
- name: Set up php 8.4
uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2.36.0
with:
extensions: apcu
php-version: 8.5
php-version: 8.4
coverage: none
ini-file: development
env:

View File

@@ -15,7 +15,7 @@ jobs:
- name: Set up php
uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2
with:
php-version: 8.5
php-version: 8.4
extensions: apcu
coverage: none

View File

@@ -39,7 +39,7 @@ jobs:
- name: Set up php
uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2.36.0
with:
php-version: 8.5
php-version: 8.4
extensions: apcu
coverage: none
ini-file: development

View File

@@ -29,7 +29,7 @@ jobs:
- name: Set up php ${{ matrix.php-versions }}
uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2
with:
php-version: 8.5
php-version: 8.4
extensions: apcu
coverage: none

View File

@@ -58,11 +58,6 @@ http://{$APACHE_HOST}:23973, # For Collabora callback and WOPI requests, see con
reverse_proxy {$WHITEBOARD_HOST}:3002
}
# HaRP (ExApps)
route /exapps/* {
reverse_proxy {$HARP_HOST}:8780
}
# Nextcloud
route {
header Strict-Transport-Security max-age=31536000;

View File

@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:latest
FROM caddy:2.11.1-alpine AS caddy
FROM caddy:2.10.2-alpine AS caddy
# From https://github.com/docker-library/httpd/blob/master/2.4/alpine/Dockerfile
FROM httpd:2.4.66-alpine3.23
@@ -79,8 +79,7 @@ RUN set -ex; \
chmod 777 -R /usr/local/apache2/logs; \
rm -rf /usr/local/apache2/cgi-bin/; \
\
echo "root:$(openssl rand -base64 12)" | chpasswd; \
apk --no-cache del openssl
echo "root:$(openssl rand -base64 12)" | chpasswd
USER 33

View File

@@ -77,10 +77,6 @@ if [ "$BORG_MODE" = backup ]; then
if ! [ -f "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json" ]; then
echo "configuration.json not present. Cannot perform the backup!"
exit 1
elif ! grep -q '"domain"' "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json" \
|| ! grep -q '"wasStartButtonClicked"' "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json"; then
echo "It seems like the configuration.json setup was not done correctly. Something is wrong! (Most likely the provided configuration.json is invalid)"
exit 1
elif ! [ -f "/nextcloud_aio_volumes/nextcloud_aio_nextcloud/config/config.php" ]; then
echo "config.php is missing. Cannot perform backup!"
exit 1
@@ -518,10 +514,6 @@ if [ "$BORG_MODE" = restore ]; then
if [ "$RESTORE_FAILED" = 1 ]; then
exit 1
elif ! grep -q '"domain"' "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json" \
|| ! grep -q '"wasStartButtonClicked"' "/nextcloud_aio_volumes/nextcloud_aio_mastercontainer/data/configuration.json"; then
echo "It seems like the restore of the configuration.json was not done correctly. Something is wrong! (Most likely is the restore archive already incorrect)!"
exit 1
fi
# Inform user

View File

@@ -1,5 +1,5 @@
# syntax=docker/dockerfile:latest
FROM haproxy:3.3.4-alpine
FROM haproxy:3.3.2-alpine
# hadolint ignore=DL3002
USER root

View File

@@ -3,10 +3,10 @@
FROM docker:29.2.1-cli AS docker
# Caddy is a requirement
FROM caddy:2.11.1-alpine AS caddy
FROM caddy:2.10.2-alpine AS caddy
# From https://github.com/docker-library/php/blob/master/8.5/alpine3.23/fpm/Dockerfile
FROM php:8.5.3-fpm-alpine3.23
# From https://github.com/docker-library/php/blob/master/8.4/alpine3.23/fpm/Dockerfile
FROM php:8.4.17-fpm-alpine3.23
EXPOSE 80
EXPOSE 8080

View File

@@ -21,11 +21,6 @@ Listen 8080 https
<FilesMatch "\.php$">
SetHandler "proxy:fcgi://127.0.0.1:9000"
</FilesMatch>
# Disable output buffering to enable streaming responses.
<Proxy "fcgi://127.0.0.1:9000/" flushpackets=on>
</Proxy>
# Master dir
DocumentRoot /var/www/docker-aio/php/public/
<Directory /var/www/docker-aio/php/public/>

View File

@@ -162,14 +162,11 @@ if ! sudo -E -u www-data docker ps --format "{{.Names}}" | grep -q "^nextcloud-a
Using a different name is not supported since mastercontainer updates will not work in that case!
If you are on docker swarm and try to run AIO, see https://github.com/nextcloud/all-in-one#can-i-run-this-with-docker-swarm"
exit 1
elif sudo -E -u www-data docker inspect nextcloud-aio-mastercontainer --format "{{.Config.Image}}" | grep -q '@'; then
print_red "It seems like you used a hash for the mastercontainer image tag. This is not supported!"
exit 1
elif ! sudo -E -u www-data docker volume ls --format "{{.Name}}" | grep -q "^nextcloud_aio_mastercontainer$"; then
print_red "It seems like you did not give the mastercontainer volume the correct name? (The 'nextcloud_aio_mastercontainer' volume was not found.)
Using a different name is not supported since the built-in backup solution will not work in that case!"
exit 1
elif ! sudo -E -u www-data docker inspect nextcloud-aio-mastercontainer | grep -q "nextcloud_aio_mastercontainer"; then
elif ! sudo -E -u www-data docker inspect nextcloud-aio-mastercontainer --format '{{.Mounts}}' | grep -q " nextcloud_aio_mastercontainer "; then
print_red "It seems like you did not attach the 'nextcloud_aio_mastercontainer' volume to the mastercontainer?
This is not supported since the built-in backup solution will not work in that case!"
exit 1

View File

@@ -8,7 +8,7 @@ ENV SOURCE_LOCATION=/usr/src/nextcloud
ENV REDIS_DB_INDEX=0
# AIO settings start # Do not remove or change this line!
ENV NEXTCLOUD_VERSION=32.0.6
ENV NEXTCLOUD_VERSION=32.0.5
ENV AIO_TOKEN=123456
ENV AIO_URL=localhost
# AIO settings end # Do not remove or change this line!

View File

@@ -1,18 +1,14 @@
<?php
if (getenv('REDIS_MODE') !== 'rediscluster') {
if (getenv('REDIS_HOST')) {
$CONFIG = array(
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis' => array(
'host' => getenv('REDIS_HOST'),
'password' => (string) getenv('REDIS_HOST_PASSWORD'),
),
);
if (getenv('REDIS_HOST')) {
$CONFIG['redis']['host'] = (string) getenv('REDIS_HOST');
}
if (getenv('REDIS_HOST_PASSWORD')) {
$CONFIG['redis']['password'] = (string) getenv('REDIS_HOST_PASSWORD');
}
if (getenv('REDIS_PORT')) {
$CONFIG['redis']['port'] = (int) getenv('REDIS_PORT');
}
@@ -21,36 +17,7 @@ if (getenv('REDIS_MODE') !== 'rediscluster') {
$CONFIG['redis']['dbindex'] = (int) getenv('REDIS_DB_INDEX');
}
if (getenv('REDIS_USER_AUTH')) {
if (getenv('REDIS_USER_AUTH') !== false) {
$CONFIG['redis']['user'] = str_replace("&auth[]=", "", getenv('REDIS_USER_AUTH'));
}
} else {
$CONFIG = array(
'memcache.distributed' => '\OC\Memcache\Redis',
'memcache.locking' => '\OC\Memcache\Redis',
'redis.cluster' => array(
'timeout' => 0.0,
'read_timeout' => 0.0,
'failover_mode' => \RedisCluster::FAILOVER_ERROR,
'seeds' => array_values(array_filter(array(
(getenv('REDIS_HOST') && getenv('REDIS_PORT')) ? (getenv('REDIS_HOST') . ':' . (string)getenv('REDIS_PORT')) : null,
(getenv('REDIS_HOST_2') && getenv('REDIS_PORT_2')) ? (getenv('REDIS_HOST_2') . ':' . (string)getenv('REDIS_PORT_2')) : null,
(getenv('REDIS_HOST_3') && getenv('REDIS_PORT_3')) ? (getenv('REDIS_HOST_3') . ':' . (string)getenv('REDIS_PORT_3')) : null,
(getenv('REDIS_HOST_4') && getenv('REDIS_PORT_4')) ? (getenv('REDIS_HOST_4') . ':' . (string)getenv('REDIS_PORT_4')) : null,
(getenv('REDIS_HOST_5') && getenv('REDIS_PORT_5')) ? (getenv('REDIS_HOST_5') . ':' . (string)getenv('REDIS_PORT_5')) : null,
(getenv('REDIS_HOST_6') && getenv('REDIS_PORT_6')) ? (getenv('REDIS_HOST_6') . ':' . (string)getenv('REDIS_PORT_6')) : null,
(getenv('REDIS_HOST_7') && getenv('REDIS_PORT_7')) ? (getenv('REDIS_HOST_7') . ':' . (string)getenv('REDIS_PORT_7')) : null,
(getenv('REDIS_HOST_8') && getenv('REDIS_PORT_8')) ? (getenv('REDIS_HOST_8') . ':' . (string)getenv('REDIS_PORT_8')) : null,
(getenv('REDIS_HOST_9') && getenv('REDIS_PORT_9')) ? (getenv('REDIS_HOST_9') . ':' . (string)getenv('REDIS_PORT_9')) : null,
))),
),
);
if (getenv('REDIS_HOST_PASSWORD')) {
$CONFIG['redis.cluster']['password'] = (string) getenv('REDIS_HOST_PASSWORD');
}
if (getenv('REDIS_USER_AUTH')) {
$CONFIG['redis.cluster']['user'] = str_replace("&auth[]=", "", getenv('REDIS_USER_AUTH'));
}
}

View File

@@ -34,14 +34,4 @@ if (getenv('OBJECTSTORE_S3_BUCKET')) {
if ($sse_c_key) {
$CONFIG['objectstore']['arguments']['sse_c_key'] = $sse_c_key;
}
$requestChecksumValidation = getenv('OBJECTSTORE_S3_REQUEST_CHECKSUM_VALIDATION');
if ($requestChecksumValidation) {
$CONFIG['objectstore']['arguments']['request_checksum_calculation'] = $requestChecksumValidation;
}
$responseChecksumValidation = getenv('OBJECTSTORE_S3_RESPONSE_CHECKSUM_VALIDATION');
if ($responseChecksumValidation) {
$CONFIG['objectstore']['arguments']['response_checksum_validation'] = $responseChecksumValidation;
}
}

View File

@@ -669,12 +669,8 @@ php /var/www/html/occ config:system:set documentation_url.server_logs --value="h
php /var/www/html/occ config:system:set htaccess.RewriteBase --value="/"
php /var/www/html/occ maintenance:update:htaccess
# Handle db persistent settings
if [ "$NEXTCLOUD_PERSIST_DATABASE_CONNECTIONS" = "yes" ]; then
php /var/www/html/occ config:system:set dbpersistent --value=true --type=bool
else
php /var/www/html/occ config:system:set dbpersistent --value=false --type=bool
fi
# Revert dbpersistent setting to check if it fixes too many db connections
php /var/www/html/occ config:system:set dbpersistent --value=false --type=bool
if [ "$DISABLE_BRUTEFORCE_PROTECTION" = yes ]; then
php /var/www/html/occ config:system:set auth.bruteforce.protection.enabled --type=bool --value=false
@@ -1029,13 +1025,13 @@ else
fi
fi
# Docker socket proxy / HaRP
# Docker socket proxy
# app_api is a shipped app
if [ -d "/var/www/html/custom_apps/app_api" ]; then
php /var/www/html/occ app:disable app_api
rm -r "/var/www/html/custom_apps/app_api"
fi
if [ "$DOCKER_SOCKET_PROXY_ENABLED" = 'yes' ] || [ "$HARP_ENABLED" = 'yes' ]; then
if [ "$DOCKER_SOCKET_PROXY_ENABLED" = 'yes' ]; then
if [ "$(php /var/www/html/occ config:app:get app_api enabled)" != "yes" ]; then
php /var/www/html/occ app:enable app_api
fi

View File

@@ -11,6 +11,7 @@ RUN set -ex; \
netcat-openbsd \
tzdata \
bash \
jq \
openssl; \
# Give root a random password
echo "root:$(openssl rand -base64 12)" | chpasswd; \

View File

@@ -3,6 +3,12 @@
if [ -z "$NEXTCLOUD_HOST" ]; then
echo "NEXTCLOUD_HOST needs to be provided. Exiting!"
exit 1
elif [ -z "$POSTGRES_HOST" ]; then
echo "POSTGRES_HOST needs to be provided. Exiting!"
exit 1
elif [ -z "$REDIS_HOST" ]; then
echo "REDIS_HOST needs to be provided. Exiting!"
exit 1
fi
# Only start container if nextcloud is accessible
@@ -22,7 +28,7 @@ elif [ "$CPU_ARCH" != "x86_64" ]; then
fi
# Add warning
if ! [ -f /var/www/html/custom_apps/notify_push/bin/"$CPU_ARCH"/notify_push ]; then
if ! [ -f /nextcloud/custom_apps/notify_push/bin/"$CPU_ARCH"/notify_push ]; then
echo "The notify_push binary was not found."
echo "Most likely is DNS resolution not working correctly."
echo "You can try to fix this by configuring a DNS server globally in dockers daemon.json."
@@ -38,9 +44,52 @@ fi
echo "notify-push was started"
# Set a default value for POSTGRES_PORT
if [ -z "$POSTGRES_PORT" ]; then
POSTGRES_PORT=5432
fi
# Set a default for redis db index
if [ -z "$REDIS_DB_INDEX" ]; then
REDIS_DB_INDEX=0
fi
# Set a default value for REDIS_PORT
if [ -z "$REDIS_PORT" ]; then
REDIS_PORT=6379
fi
# Set a default for db type
if [ -z "$DATABASE_TYPE" ]; then
DATABASE_TYPE=postgres
elif [ "$DATABASE_TYPE" != postgres ] && [ "$DATABASE_TYPE" != mysql ]; then
echo "DB type must be either postgres or mysql"
exit 1
fi
# Use the correct Postgres username
if [ "$POSTGRES_USER" = nextcloud ]; then
POSTGRES_USER="oc_$POSTGRES_USER"
export POSTGRES_USER
fi
# URL-encode passwords
POSTGRES_PASSWORD="$(jq -rn --arg v "$POSTGRES_PASSWORD" '$v|@uri')"
REDIS_HOST_PASSWORD="$(jq -rn --arg v "$REDIS_HOST_PASSWORD" '$v|@uri')"
# Postgres root cert
if [ -f "/nextcloud/data/certificates/POSTGRES" ]; then
CERT_OPTIONS="?sslmode=verify-ca&sslrootcert=/nextcloud/data/certificates/ca-bundle.crt"
# Mysql root cert
elif [ -f "/nextcloud/data/certificates/MYSQL" ]; then
CERT_OPTIONS="?sslmode=verify-ca&ssl-ca=/nextcloud/data/certificates/ca-bundle.crt"
fi
# Set sensitive values as env
export DATABASE_URL="$DATABASE_TYPE://$POSTGRES_USER:$POSTGRES_PASSWORD@$POSTGRES_HOST:$POSTGRES_PORT/$POSTGRES_DB$CERT_OPTIONS"
export REDIS_URL="redis://$REDIS_USER:$REDIS_HOST_PASSWORD@$REDIS_HOST:$REDIS_PORT/$REDIS_DB_INDEX"
# Run it
/var/www/html/custom_apps/notify_push/bin/"$CPU_ARCH"/notify_push \
--port 7867 \
/var/www/html/config/config.php
/nextcloud/custom_apps/notify_push/bin/"$CPU_ARCH"/notify_push \
--database-prefix="oc_" \
--nextcloud-url "https://$NC_DOMAIN" \
--port 7867
exec "$@"

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:latest
# From https://github.com/ONLYOFFICE/Docker-DocumentServer/blob/master/Dockerfile
FROM onlyoffice/documentserver:9.3.0.1
FROM onlyoffice/documentserver:9.2.1.1
# USER root is probably used

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:latest
# From https://github.com/docker-library/postgres/blob/master/17/alpine3.23/Dockerfile
FROM postgres:17.8-alpine
FROM postgres:17.7-alpine
COPY --chmod=775 start.sh /start.sh
COPY --chmod=775 healthcheck.sh /healthcheck.sh

View File

@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:latest
# From https://github.com/redis/docker-library-redis/blob/release/8.2/alpine/Dockerfile
FROM redis:8.6.1-alpine
FROM redis:8.2.3-alpine
COPY --chmod=775 start.sh /start.sh
@@ -10,7 +10,6 @@ RUN set -ex; \
\
# Give root a random password
echo "root:$(openssl rand -base64 12)" | chpasswd; \
apk --no-cache del openssl; \
\
# Get rid of unused binaries
rm -f /usr/local/bin/gosu;

View File

@@ -1,13 +1,13 @@
# syntax=docker/dockerfile:latest
FROM golang:1.26.0-alpine3.23 AS go
ENV WATCHTOWER_COMMIT_HASH=943098a670cb78a620af6499fb94b3ee2c940cf0
ENV WATCHTOWER_COMMIT_HASH=b09b3a5c5e1094b746575a19a7fc0135c8908c9d
RUN set -ex; \
apk upgrade --no-cache -a; \
apk add --no-cache \
build-base; \
go install github.com/nicholas-fedor/watchtower@$WATCHTOWER_COMMIT_HASH # v1.14.2
go install github.com/nicholas-fedor/watchtower@$WATCHTOWER_COMMIT_HASH # v1.14.1
FROM alpine:3.23.3

View File

@@ -1,5 +1,5 @@
## Caddy with geoblocking
This container bundles caddy and auto-configures it for you. It also covers [vaultwarden](https://github.com/nextcloud/all-in-one/tree/main/community-containers/vaultwarden) by listening on `bw.$NC_DOMAIN`, if installed. It also covers [stalwart](https://github.com/nextcloud/all-in-one/tree/main/community-containers/stalwart) by listening on `mail.$NC_DOMAIN`, if installed. It also covers [jellyfin](https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyfin) by listening on `media.$NC_DOMAIN`, if installed. It also covers [lldap](https://github.com/nextcloud/all-in-one/tree/main/community-containers/lldap) by listening on `ldap.$NC_DOMAIN`, if installed. It also covers [nocodb](https://github.com/nextcloud/all-in-one/tree/main/community-containers/nocodb) by listening on `tables.$NC_DOMAIN`, if installed. It also covers [seerr](https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyseerr) by listening on `requests.$NC_DOMAIN`, if installed. It also covers [nextcloud-exporter](https://github.com/nextcloud/all-in-one/tree/main/community-containers/nextcloud-exporter) by listening on `metrics.$NC_DOMAIN`, if installed. It also covers [LocalAI](https://github.com/nextcloud/all-in-one/tree/main/community-containers/local-ai) by listening on `ai.$NC_DOMAIN`, if installed.
This container bundles caddy and auto-configures it for you. It also covers [vaultwarden](https://github.com/nextcloud/all-in-one/tree/main/community-containers/vaultwarden) by listening on `bw.$NC_DOMAIN`, if installed. It also covers [stalwart](https://github.com/nextcloud/all-in-one/tree/main/community-containers/stalwart) by listening on `mail.$NC_DOMAIN`, if installed. It also covers [jellyfin](https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyfin) by listening on `media.$NC_DOMAIN`, if installed. It also covers [lldap](https://github.com/nextcloud/all-in-one/tree/main/community-containers/lldap) by listening on `ldap.$NC_DOMAIN`, if installed. It also covers [nocodb](https://github.com/nextcloud/all-in-one/tree/main/community-containers/nocodb) by listening on `tables.$NC_DOMAIN`, if installed. It also covers [jellyseerr](https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyseerr) by listening on `requests.$NC_DOMAIN`, if installed. It also covers [nextcloud-exporter](https://github.com/nextcloud/all-in-one/tree/main/community-containers/nextcloud-exporter) by listening on `metrics.$NC_DOMAIN`, if installed. It also covers [LocalAI](https://github.com/nextcloud/all-in-one/tree/main/community-containers/local-ai) by listening on `ai.$NC_DOMAIN`, if installed.
### Notes
- This container is incompatible with the [npmplus](https://github.com/nextcloud/all-in-one/tree/main/community-containers/npmplus) community container. So make sure that you do not enable both at the same time!
@@ -12,7 +12,7 @@ This container bundles caddy and auto-configures it for you. It also covers [vau
- If you want to use this with [jellyfin](https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyfin), make sure that you point `media.your-nc-domain.com` to your server using a cname record so that caddy can get a certificate automatically for jellyfin.
- If you want to use this with [lldap](https://github.com/nextcloud/all-in-one/tree/main/community-containers/lldap), make sure that you point `ldap.your-nc-domain.com` to your server using a cname record so that caddy can get a certificate automatically for lldap.
- If you want to use this with [nocodb](https://github.com/nextcloud/all-in-one/tree/main/community-containers/nocodb), make sure that you point `tables.your-nc-domain.com` to your server using a cname record so that caddy can get a certificate automatically for nocodb.
- If you want to use this with [seerr](https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyseerr), make sure that you point `requests.your-nc-domain.com` to your server using a cname record so that caddy can get a certificate automatically for seerr.
- If you want to use this with [jellyseerr](https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyseerr), make sure that you point `requests.your-nc-domain.com` to your server using a cname record so that caddy can get a certificate automatically for jellyseerr.
- If you want to use this with [nextcloud-exporter](https://github.com/nextcloud/all-in-one/tree/main/community-containers/nextcloud-exporter), make sure that you point `metrics.your-nc-domain.com` to your server using a cname record so that caddy can get a certificate automatically for nextcloud-exporter.
- If you want to use this with [local AI](https://github.com/nextcloud/all-in-one/tree/main/community-containers/local-ai), make sure that you point `ai.your-nc-domain.com` to your server using a cname record so that caddy can get a certificate automatically for local AI.
- After the container was started the first time, you should see a new `nextcloud-aio-caddy` folder and inside there an `allowed-countries.txt` file when you open the files app with the default `admin` user. In there you can adjust the allowed country codes for caddy by adding them to the first line, e.g. `IT FR` would allow access from italy and france. Private ip-ranges are always allowed. Additionally, in order to activate this config, you need to get an account at https://dev.maxmind.com/geoip/geolite2-free-geolocation-data and download the `GeoLite2-Country.mmdb` and upload it with this exact name into the `nextcloud-aio-caddy` folder. Afterwards restart all containers from the AIO interface and your new config should be active!

View File

@@ -2,13 +2,13 @@
"aio_services_v1": [
{
"container_name": "nextcloud-aio-jellyseerr",
"display_name": "Seerr",
"display_name": "Jellyseerr",
"documentation": "https://github.com/nextcloud/all-in-one/tree/main/community-containers/jellyseerr",
"image": "ghcr.io/seerr-team/seerr",
"image": "fallenbagel/jellyseerr",
"image_tag": "latest",
"internal_port": "5055",
"restart": "unless-stopped",
"init": true,
"init": false,
"ports": [
{
"ip_binding": "%APACHE_IP_BINDING%",

View File

@@ -1,16 +1,16 @@
## Seerr
This container bundles Seerr and auto-configures it for you.
## Jellyseerr
This container bundles Jellyseerr and auto-configures it for you.
### Notes
- This container is only intended to be used inside home networks as it uses http for its management page by default.
- After adding and starting the container, you can directly visit `http://ip.address.of.server:5055` and access your new Seerr instance, which can be used to manage Plex, Jellyfin, and Emby.
- In order to access your Seerr outside the local network, you have to set up your own reverse proxy. You can set up a reverse proxy following [these instructions](https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md) and [Seerr's reverse proxy documentation.](https://docs.seerr.dev/extending-Seerr/reverse-proxy), OR use the Caddy community container that will automatically configure requests.$NC_DOMAIN to redirect to your Seerr. Note that it is recommended to [enable CSRF protection in Seerr](https://docs.seerr.dev/using-Seerr/settings/general#enable-csrf-protection) for added security if you plan to use Seerr outside the local network, but make sure to read up on it and understand the caveats first.
- If you want to secure the installation with fail2ban, you might want to check out https://github.com/nextcloud/all-in-one/tree/main/community-containers/fail2ban. Note that [enabling the proxy support option in Seerr](https://docs.seerr.dev/using-Seerr/settings/general#enable-proxy-support) is required for this to work properly.
- The config of Seerr will be automatically included in AIO's backup solution!
- After adding and starting the container, you can directly visit `http://ip.address.of.server:5055` and access your new Jellyseerr instance, which can be used to manage Plex, Jellyfin, and Emby.
- In order to access your Jellyseerr outside the local network, you have to set up your own reverse proxy. You can set up a reverse proxy following [these instructions](https://github.com/nextcloud/all-in-one/blob/main/reverse-proxy.md) and [Jellyseerr's reverse proxy documentation.](https://docs.jellyseerr.dev/extending-jellyseerr/reverse-proxy), OR use the Caddy community container that will automatically configure requests.$NC_DOMAIN to redirect to your Jellyseerr. Note that it is recommended to [enable CSRF protection in Jellyseerr](https://docs.jellyseerr.dev/using-jellyseerr/settings/general#enable-csrf-protection) for added security if you plan to use Jellyseerr outside the local network, but make sure to read up on it and understand the caveats first.
- If you want to secure the installation with fail2ban, you might want to check out https://github.com/nextcloud/all-in-one/tree/main/community-containers/fail2ban. Note that [enabling the proxy support option in Jellyseerr](https://docs.jellyseerr.dev/using-jellyseerr/settings/general#enable-proxy-support) is required for this to work properly.
- The config of Jellyseerr will be automatically included in AIO's backup solution!
- See [here](https://github.com/nextcloud/all-in-one/tree/main/community-containers#community-containers) how to add it to the AIO stack.
### Repository
https://github.com/seerr-team/seerr
https://github.com/Fallenbagel/jellyseerr
### Maintainer
https://github.com/Anvil5465

View File

@@ -33,7 +33,6 @@ There is a testing-VM available for the maintainer of AIO that allows for some f
Additionally, there are now E2E tests available that can be run via https://github.com/nextcloud/all-in-one/actions/workflows/playwright.yml
## How to promote builds from develop to beta
1. Verify that GitHub Services are running correctly: https://www.githubstatus.com/
1. Verify that no job is running here: https://github.com/nextcloud-releases/all-in-one/actions/workflows/build_images.yml
2. Go to https://github.com/nextcloud-releases/all-in-one/actions/workflows/promote-to-beta.yml, click on `Run workflow`.

View File

@@ -12,7 +12,7 @@ You can run the containers that are build for AIO with docker-compose. This come
- You lose the AIO interface
- You lose update notifications and automatic updates
- You lose all AIO backup and restore features
- You lose the built-in [Docker Socket Proxy container](https://github.com/nextcloud/docker-socket-proxy#readme) and [HaRP container](https://github.com/nextcloud/HaRP) (needed for [Nextcloud App API](https://github.com/nextcloud/app_api#nextcloud-appapi))
- You lose the built-in [Docker Socket Proxy container](https://github.com/nextcloud/docker-socket-proxy#readme) (needed for [Nextcloud App API](https://github.com/nextcloud/app_api#nextcloud-appapi))
- You lose all community containers: https://github.com/nextcloud/all-in-one/tree/main/community-containers#community-containers
- **You need to know what you are doing, especially when modifying the compose.yaml file**
- For updating, you need to strictly follow the at the bottom described update routine

View File

@@ -27,8 +27,6 @@ OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "next
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-borgbackup"))')"
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-docker-socket-proxy"))')"
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= if contains(["nextcloud-aio-docker-socket-proxy"]) then del(.[index("nextcloud-aio-docker-socket-proxy")]) else . end else . end')"
OUTPUT="$(echo "$OUTPUT" | jq 'del(.services[] | select(.container_name == "nextcloud-aio-harp"))')"
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= if contains(["nextcloud-aio-harp"]) then del(.[index("nextcloud-aio-harp")]) else . end else . end')"
OUTPUT="$(echo "$OUTPUT" | jq '.services[] |= if has("depends_on") then .depends_on |= map({ (.): { "condition": "service_started", "required": false } }) else . end' | jq '.services[] |= if has("depends_on") then .depends_on |= reduce .[] as $item ({}; . + $item) else . end')"
sudo snap install yq
@@ -47,8 +45,6 @@ sed -i 's|- ip_binding: |- |' containers.yml
sed -i '/AIO_TOKEN/d' containers.yml
sed -i '/AIO_URL/d' containers.yml
sed -i '/DOCKER_SOCKET_PROXY_ENABLED/d' containers.yml
sed -i '/HARP_ENABLED/d' containers.yml
sed -i '/HP_SHARED_KEY/d' containers.yml
sed -i '/ADDITIONAL_TRUSTED_PROXY/d' containers.yml
sed -i '/TURN_DOMAIN/d' containers.yml
sed -i '/NC_AIO_VERSION/d' containers.yml

View File

@@ -1,6 +1,6 @@
name: nextcloud-aio-helm-chart
description: A generated Helm Chart for Nextcloud AIO from Skippbox Kompose
version: 12.7.0
version: 12.6.1
apiVersion: v2
keywords:
- latest

View File

@@ -47,8 +47,6 @@ spec:
value: "{{ .Values.APACHE_PORT }}"
- name: COLLABORA_HOST
value: nextcloud-aio-collabora
- name: HARP_HOST
value: nextcloud-aio-harp
- name: NC_DOMAIN
value: "{{ .Values.NC_DOMAIN }}"
- name: NEXTCLOUD_HOST
@@ -63,7 +61,7 @@ spec:
value: "{{ .Values.TIMEZONE }}"
- name: WHITEBOARD_HOST
value: nextcloud-aio-whiteboard
image: ghcr.io/nextcloud-releases/aio-apache:20260218_123804
image: ghcr.io/nextcloud-releases/aio-apache:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -36,7 +36,7 @@ spec:
{{- end }}
initContainers:
- name: init-subpath
image: ghcr.io/nextcloud-releases/aio-alpine:20260218_123804
image: ghcr.io/nextcloud-releases/aio-alpine:20260211_141900
command:
- mkdir
- "-p"
@@ -59,7 +59,7 @@ spec:
value: "{{ .Values.NEXTCLOUD_UPLOAD_LIMIT }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-clamav:20260218_123804
image: ghcr.io/nextcloud-releases/aio-clamav:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -36,9 +36,9 @@ spec:
- name: server_name
value: "{{ .Values.NC_DOMAIN }}"
{{- if contains "--o:support_key=" (join " " (.Values.ADDITIONAL_COLLABORA_OPTIONS | default list)) }}
image: ghcr.io/nextcloud-releases/aio-collabora-online:20260218_123804
image: ghcr.io/nextcloud-releases/aio-collabora-online:20260211_141900
{{- else }}
image: ghcr.io/nextcloud-releases/aio-collabora:20260218_123804
image: ghcr.io/nextcloud-releases/aio-collabora:20260211_141900
{{- end }}
readinessProbe:
exec:

View File

@@ -35,7 +35,7 @@ spec:
{{- end }}
initContainers:
- name: init-subpath
image: ghcr.io/nextcloud-releases/aio-alpine:20260218_123804
image: ghcr.io/nextcloud-releases/aio-alpine:20260211_141900
command:
- mkdir
- "-p"
@@ -64,7 +64,7 @@ spec:
value: nextcloud
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-postgresql:20260218_123804
image: ghcr.io/nextcloud-releases/aio-postgresql:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -24,7 +24,7 @@ spec:
spec:
initContainers:
- name: init-volumes
image: ghcr.io/nextcloud-releases/aio-alpine:20260218_123804
image: ghcr.io/nextcloud-releases/aio-alpine:20260211_141900
command:
- chmod
- "777"
@@ -54,7 +54,7 @@ spec:
value: basic
- name: xpack.security.enabled
value: "false"
image: ghcr.io/nextcloud-releases/aio-fulltextsearch:20260218_123804
image: ghcr.io/nextcloud-releases/aio-fulltextsearch:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -38,7 +38,7 @@ spec:
value: "{{ .Values.IMAGINARY_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-imaginary:20260218_123804
image: ghcr.io/nextcloud-releases/aio-imaginary:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -38,7 +38,7 @@ spec:
# AIO settings start # Do not remove or change this line!
initContainers:
- name: init-volumes
image: ghcr.io/nextcloud-releases/aio-alpine:20260218_123804
image: ghcr.io/nextcloud-releases/aio-alpine:20260211_141900
command:
- chmod
- "777"
@@ -190,7 +190,7 @@ spec:
value: "{{ .Values.WHITEBOARD_ENABLED }}"
- name: WHITEBOARD_SECRET
value: "{{ .Values.WHITEBOARD_SECRET }}"
image: ghcr.io/nextcloud-releases/aio-nextcloud:20260218_123804
image: ghcr.io/nextcloud-releases/aio-nextcloud:20260211_141900
{{- if eq (.Values.RPSS_ENABLED | default "no") "yes" }} # AIO-config - do not change this comment!
securityContext:
# The items below only work in container context

View File

@@ -35,11 +35,29 @@ spec:
{{- end }}
containers:
- env:
- name: NC_DOMAIN
value: "{{ .Values.NC_DOMAIN }}"
- name: NEXTCLOUD_HOST
value: nextcloud-aio-nextcloud
- name: POSTGRES_DB
value: nextcloud_database
- name: POSTGRES_HOST
value: nextcloud-aio-database
- name: POSTGRES_PASSWORD
value: "{{ .Values.DATABASE_PASSWORD }}"
- name: POSTGRES_PORT
value: "5432"
- name: POSTGRES_USER
value: nextcloud
- name: REDIS_HOST
value: nextcloud-aio-redis
- name: REDIS_HOST_PASSWORD
value: "{{ .Values.REDIS_PASSWORD }}"
- name: REDIS_PORT
value: "6379"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-notify-push:20260218_123804
image: ghcr.io/nextcloud-releases/aio-notify-push:20260211_141900
readinessProbe:
exec:
command:
@@ -68,7 +86,7 @@ spec:
drop: ["NET_RAW"]
{{- end }}
volumeMounts:
- mountPath: /var/www/html
- mountPath: /nextcloud
name: nextcloud-aio-nextcloud
readOnly: true
volumes:

View File

@@ -24,7 +24,7 @@ spec:
spec:
initContainers:
- name: init-volumes
image: ghcr.io/nextcloud-releases/aio-alpine:20260218_123804
image: ghcr.io/nextcloud-releases/aio-alpine:20260211_141900
command:
- chmod
- "777"
@@ -42,7 +42,7 @@ spec:
value: "{{ .Values.ONLYOFFICE_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-onlyoffice:20260218_123804
image: ghcr.io/nextcloud-releases/aio-onlyoffice:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -39,7 +39,7 @@ spec:
value: "{{ .Values.REDIS_PASSWORD }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-redis:20260218_123804
image: ghcr.io/nextcloud-releases/aio-redis:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -52,7 +52,7 @@ spec:
value: "{{ .Values.TURN_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-talk:20260218_123804
image: ghcr.io/nextcloud-releases/aio-talk:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -44,7 +44,7 @@ spec:
value: "{{ .Values.RECORDING_SECRET }}"
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-talk-recording:20260218_123804
image: ghcr.io/nextcloud-releases/aio-talk-recording:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -50,7 +50,7 @@ spec:
value: redis
- name: TZ
value: "{{ .Values.TIMEZONE }}"
image: ghcr.io/nextcloud-releases/aio-whiteboard:20260218_123804
image: ghcr.io/nextcloud-releases/aio-whiteboard:20260211_141900
readinessProbe:
exec:
command:

View File

@@ -418,9 +418,8 @@ sed -i 's|= |: |' /tmp/sample.conf
sed -i '/^NEXTCLOUD_DATADIR/d' /tmp/sample.conf
sed -i '/^APACHE_IP_BINDING/d' /tmp/sample.conf
sed -i '/^NEXTCLOUD_MOUNT/d' /tmp/sample.conf
sed -i 's/ yes / "yes" /' /tmp/sample.conf
sed -i 's/ no / "no" /' /tmp/sample.conf
sed -i 's/"no" authentication/no authentication/' /tmp/sample.conf
sed -i '/_ENABLED.*/s/ yes / "yes" /' /tmp/sample.conf
sed -i '/_ENABLED.*/s/ no / "no" /' /tmp/sample.conf
sed -i 's|^NEXTCLOUD_TRUSTED_CACERTS_DIR: .*|NEXTCLOUD_TRUSTED_CACERTS_DIR: # Setting this to any value allows to automatically import root certificates into the Nextcloud container|' /tmp/sample.conf
sed -i 's|17179869184|"17179869184"|' /tmp/sample.conf
# shellcheck disable=SC2129

View File

@@ -26,7 +26,7 @@ APACHE_PORT: 443 # Changing this to a different value than 443 will all
ADDITIONAL_COLLABORA_OPTIONS: ['--o:security.seccomp=true'] # You can add additional collabora options here by using the array syntax.
COLLABORA_DICTIONARIES: de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru # You can change this in order to enable other dictionaries for collabora
FULLTEXTSEARCH_JAVA_OPTIONS: -Xms512M -Xmx512M # Allows to adjust the fulltextsearch java options.
INSTALL_LATEST_MAJOR: "no" # Setting this to "yes" will install the latest Major Nextcloud version upon the first installation
INSTALL_LATEST_MAJOR: no # Setting this to yes will install the latest Major Nextcloud version upon the first installation
NEXTCLOUD_ADDITIONAL_APKS: imagemagick # This allows to add additional packages to the Nextcloud container permanently. Default is imagemagick but can be overwritten by modifying this value.
NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS: imagick # This allows to add additional php extensions to the Nextcloud container permanently. Default is imagick but can be overwritten by modifying this value.
NEXTCLOUD_MAX_TIME: 3600 # This allows to change the upload time limit of the Nextcloud container
@@ -34,9 +34,9 @@ NEXTCLOUD_MEMORY_LIMIT: 512M # This allows to change the PHP memory lim
NEXTCLOUD_STARTUP_APPS: deck twofactor_totp tasks calendar contacts notes # Allows to modify the Nextcloud apps that are installed on starting AIO the first time
NEXTCLOUD_TRUSTED_CACERTS_DIR: # Setting this to any value allows to automatically import root certificates into the Nextcloud container
NEXTCLOUD_UPLOAD_LIMIT: 16G # This allows to change the upload limit of the Nextcloud container
REMOVE_DISABLED_APPS: "yes" # Setting this to "no" keep Nextcloud apps that are disabled via their switch and not uninstall them if they should be installed in Nextcloud.
REMOVE_DISABLED_APPS: yes # Setting this to no keep Nextcloud apps that are disabled via their switch and not uninstall them if they should be installed in Nextcloud.
TALK_PORT: 3478 # This allows to adjust the port that the talk container is using. It should be set to something higher than 1024! Otherwise it might not work!
UPDATE_NEXTCLOUD_APPS: "no" # When setting to "yes" (with quotes), it will automatically update all installed Nextcloud apps upon container startup on saturdays.
UPDATE_NEXTCLOUD_APPS: no # When setting to yes (with quotes), it will automatically update all installed Nextcloud apps upon container startup on saturdays.
STORAGE_CLASS: # By setting this, you can adjust the storage class for your volumes. This should be a fast storage like SSD backed storage! This storage class must provide RWX and RWO volumes (ReadWriteMany and ReadWriteOnce).
STORAGE_CLASS_DATA: # Allows to set a dedicated storage class for the Nextcloud data volume. This can be a bit slower storage than the one above. This storage class must provide RWX volumes (ReadWriteMany). ⚠️ Warning: only set this for new installations, not existing ones!

View File

@@ -5,7 +5,7 @@
}
},
"require": {
"php": "8.5.*",
"php": "8.4.*",
"ext-json": "*",
"ext-sodium": "*",
"ext-curl": "*",
@@ -16,8 +16,7 @@
"http-interop/http-factory-guzzle": "^1.2",
"slim/twig-view": "^3.3",
"slim/csrf": "^1.3",
"ext-apcu": "*",
"slim/psr7": "^1.8"
"ext-apcu": "*"
},
"require-dev": {
"sserbin/twig-linter": "@dev",
@@ -34,6 +33,6 @@
"psalm:strict": "psalm --threads=1 --show-info=true",
"lint": "php -l src/*.php src/**/*.php public/index.php",
"lint:twig": "twig-linter lint ./templates",
"php-deprecation-detector": "phpdd scan -n -t 8.5 src/*.php src/**/*.php public/index.php"
"php-deprecation-detector": "phpdd scan -n -t 8.4 src/*.php src/**/*.php public/index.php"
}
}

173
php/composer.lock generated
View File

@@ -4,64 +4,8 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "e49cef2ac29c2198aececcb842fce2fe",
"content-hash": "19598625395cc28e64f15d2719f8f98f",
"packages": [
{
"name": "fig/http-message-util",
"version": "1.1.5",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-message-util.git",
"reference": "9d94dc0154230ac39e5bf89398b324a86f63f765"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/http-message-util/zipball/9d94dc0154230ac39e5bf89398b324a86f63f765",
"reference": "9d94dc0154230ac39e5bf89398b324a86f63f765",
"shasum": ""
},
"require": {
"php": "^5.3 || ^7.0 || ^8.0"
},
"suggest": {
"psr/http-message": "The package containing the PSR-7 interfaces"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.1.x-dev"
}
},
"autoload": {
"psr-4": {
"Fig\\Http\\Message\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "PHP-FIG",
"homepage": "https://www.php-fig.org/"
}
],
"description": "Utility classes and constants for use with PSR-7 (psr/http-message)",
"keywords": [
"http",
"http-message",
"psr",
"psr-7",
"request",
"response"
],
"support": {
"issues": "https://github.com/php-fig/http-message-util/issues",
"source": "https://github.com/php-fig/http-message-util/tree/1.1.5"
},
"time": "2020-11-24T22:02:12+00:00"
},
{
"name": "guzzlehttp/guzzle",
"version": "7.10.0",
@@ -447,16 +391,16 @@
},
{
"name": "laravel/serializable-closure",
"version": "v2.0.10",
"version": "v2.0.9",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669"
"reference": "8f631589ab07b7b52fead814965f5a800459cb3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/870fc81d2f879903dfc5b60bf8a0f94a1609e669",
"reference": "870fc81d2f879903dfc5b60bf8a0f94a1609e669",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/8f631589ab07b7b52fead814965f5a800459cb3e",
"reference": "8f631589ab07b7b52fead814965f5a800459cb3e",
"shasum": ""
},
"require": {
@@ -504,7 +448,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2026-02-20T19:59:49+00:00"
"time": "2026-02-03T06:55:34+00:00"
},
{
"name": "nikic/fast-route",
@@ -1202,85 +1146,6 @@
},
"time": "2025-11-02T14:58:28+00:00"
},
{
"name": "slim/psr7",
"version": "1.8.0",
"source": {
"type": "git",
"url": "https://github.com/slimphp/Slim-Psr7.git",
"reference": "76e7e3b1cdfd583e9035c4c966c08e01e45ce959"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/slimphp/Slim-Psr7/zipball/76e7e3b1cdfd583e9035c4c966c08e01e45ce959",
"reference": "76e7e3b1cdfd583e9035c4c966c08e01e45ce959",
"shasum": ""
},
"require": {
"fig/http-message-util": "^1.1.5",
"php": "^8.0",
"psr/http-factory": "^1.1",
"psr/http-message": "^1.0 || ^2.0",
"ralouphie/getallheaders": "^3.0"
},
"provide": {
"psr/http-factory-implementation": "^1.0",
"psr/http-message-implementation": "^1.0 || ^2.0"
},
"require-dev": {
"adriansuter/php-autoload-override": "^1.5|| ^2.0",
"ext-json": "*",
"http-interop/http-factory-tests": "^1.0 || ^2.0",
"php-http/psr7-integration-tests": "^1.5",
"phpstan/phpstan": "^2.1",
"phpunit/phpunit": "^9.6 || ^10",
"squizlabs/php_codesniffer": "^3.13"
},
"type": "library",
"autoload": {
"psr-4": {
"Slim\\Psr7\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Josh Lockhart",
"email": "hello@joshlockhart.com",
"homepage": "https://joshlockhart.com"
},
{
"name": "Andrew Smith",
"email": "a.smith@silentworks.co.uk",
"homepage": "https://silentworks.co.uk"
},
{
"name": "Rob Allen",
"email": "rob@akrabat.com",
"homepage": "https://akrabat.com"
},
{
"name": "Pierre Berube",
"email": "pierre@lgse.com",
"homepage": "https://www.lgse.com"
}
],
"description": "Strict PSR-7 implementation",
"homepage": "https://www.slimframework.com",
"keywords": [
"http",
"psr-7",
"psr7"
],
"support": {
"issues": "https://github.com/slimphp/Slim-Psr7/issues",
"source": "https://github.com/slimphp/Slim-Psr7/tree/1.8.0"
},
"time": "2025-11-02T17:51:19+00:00"
},
{
"name": "slim/slim",
"version": "4.15.1",
@@ -3428,16 +3293,16 @@
},
{
"name": "netresearch/jsonmapper",
"version": "v5.0.1",
"version": "v5.0.0",
"source": {
"type": "git",
"url": "https://github.com/cweiske/jsonmapper.git",
"reference": "980674efdda65913492d29a8fd51c82270dd37bb"
"reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/980674efdda65913492d29a8fd51c82270dd37bb",
"reference": "980674efdda65913492d29a8fd51c82270dd37bb",
"url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
"reference": "8c64d8d444a5d764c641ebe97e0e3bc72b25bf6c",
"shasum": ""
},
"require": {
@@ -3473,9 +3338,9 @@
"support": {
"email": "cweiske@cweiske.de",
"issues": "https://github.com/cweiske/jsonmapper/issues",
"source": "https://github.com/cweiske/jsonmapper/tree/v5.0.1"
"source": "https://github.com/cweiske/jsonmapper/tree/v5.0.0"
},
"time": "2026-02-22T16:28:03+00:00"
"time": "2024-09-08T10:20:00+00:00"
},
{
"name": "nikic/php-parser",
@@ -4883,16 +4748,16 @@
},
{
"name": "webmozart/assert",
"version": "2.1.5",
"version": "2.1.2",
"source": {
"type": "git",
"url": "https://github.com/webmozarts/assert.git",
"reference": "79155f94852fa27e2f73b459f6503f5e87e2c188"
"reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/79155f94852fa27e2f73b459f6503f5e87e2c188",
"reference": "79155f94852fa27e2f73b459f6503f5e87e2c188",
"url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649",
"reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649",
"shasum": ""
},
"require": {
@@ -4939,9 +4804,9 @@
],
"support": {
"issues": "https://github.com/webmozarts/assert/issues",
"source": "https://github.com/webmozarts/assert/tree/2.1.5"
"source": "https://github.com/webmozarts/assert/tree/2.1.2"
},
"time": "2026-02-18T14:09:36+00:00"
"time": "2026-01-13T14:02:24+00:00"
}
],
"aliases": [],
@@ -4953,7 +4818,7 @@
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
"php": "8.5.*",
"php": "8.4.*",
"ext-json": "*",
"ext-sodium": "*",
"ext-curl": "*",

View File

@@ -47,7 +47,7 @@
},
"display_name": {
"type": "string",
"pattern": "^[()A-Za-z &0-9-]+$"
"pattern": "^[()A-Za-z 0-9-]+$"
},
"environment": {
"type": "array",

View File

@@ -10,10 +10,9 @@
"nextcloud-aio-talk",
"nextcloud-aio-notify-push",
"nextcloud-aio-whiteboard",
"nextcloud-aio-harp",
"nextcloud-aio-nextcloud"
],
"display_name": "Apache & Caddy",
"display_name": "Apache",
"image": "ghcr.io/nextcloud-releases/aio-apache",
"user": "33",
"init": true,
@@ -50,8 +49,7 @@
"APACHE_MAX_SIZE=%APACHE_MAX_SIZE%",
"APACHE_MAX_TIME=%NEXTCLOUD_MAX_TIME%",
"NOTIFY_PUSH_HOST=nextcloud-aio-notify-push",
"WHITEBOARD_HOST=nextcloud-aio-whiteboard",
"HARP_HOST=nextcloud-aio-harp"
"WHITEBOARD_HOST=nextcloud-aio-whiteboard"
],
"volumes": [
{
@@ -85,7 +83,7 @@
{
"container_name": "nextcloud-aio-database",
"image_tag": "%AIO_CHANNEL%",
"display_name": "PostgreSQL",
"display_name": "Database",
"image": "ghcr.io/nextcloud-releases/aio-postgresql",
"user": "999",
"init": true,
@@ -174,8 +172,7 @@
"SIGNALING_SECRET",
"FULLTEXTSEARCH_PASSWORD",
"IMAGINARY_SECRET",
"WHITEBOARD_SECRET",
"HP_SHARED_KEY"
"WHITEBOARD_SECRET"
],
"volumes": [
{
@@ -261,9 +258,7 @@
"THIS_IS_AIO=true",
"IMAGINARY_SECRET=%IMAGINARY_SECRET%",
"WHITEBOARD_SECRET=%WHITEBOARD_SECRET%",
"WHITEBOARD_ENABLED=%WHITEBOARD_ENABLED%",
"HARP_ENABLED=%HARP_ENABLED%",
"HP_SHARED_KEY=%HP_SHARED_KEY%"
"WHITEBOARD_ENABLED=%WHITEBOARD_ENABLED%"
],
"stop_grace_period": 600,
"restart": "unless-stopped",
@@ -281,7 +276,7 @@
{
"container_name": "nextcloud-aio-notify-push",
"image_tag": "%AIO_CHANNEL%",
"display_name": "Client Push",
"display_name": "Notify Push",
"image": "ghcr.io/nextcloud-releases/aio-notify-push",
"user": "33",
"init": true,
@@ -304,13 +299,22 @@
"volumes": [
{
"source": "nextcloud_aio_nextcloud",
"destination": "/var/www/html",
"destination": "/nextcloud",
"writeable": false
}
],
"environment": [
"NC_DOMAIN=%NC_DOMAIN%",
"NEXTCLOUD_HOST=nextcloud-aio-nextcloud",
"TZ=%TIMEZONE%"
"TZ=%TIMEZONE%",
"REDIS_HOST=nextcloud-aio-redis",
"REDIS_PORT=6379",
"REDIS_HOST_PASSWORD=%REDIS_PASSWORD%",
"POSTGRES_HOST=nextcloud-aio-database",
"POSTGRES_PORT=5432",
"POSTGRES_PASSWORD=%DATABASE_PASSWORD%",
"POSTGRES_DB=nextcloud_database",
"POSTGRES_USER=nextcloud"
],
"restart": "unless-stopped",
"read_only": true,
@@ -363,7 +367,7 @@
"container_name": "nextcloud-aio-collabora",
"image_tag": "%AIO_CHANNEL%",
"documentation": "https://github.com/nextcloud/all-in-one/discussions/1358",
"display_name": "Nextcloud Office",
"display_name": "Collabora",
"image": "ghcr.io/nextcloud-releases/aio-collabora",
"init": true,
"healthcheck": {
@@ -409,7 +413,7 @@
"container_name": "nextcloud-aio-talk",
"image_tag": "%AIO_CHANNEL%",
"documentation": "https://github.com/nextcloud/all-in-one/discussions/1358",
"display_name": "Nextcloud Talk",
"display_name": "Talk",
"image": "ghcr.io/nextcloud-releases/aio-talk",
"user": "1000",
"init": true,
@@ -471,7 +475,7 @@
{
"container_name": "nextcloud-aio-talk-recording",
"image_tag": "%AIO_CHANNEL%",
"display_name": "Nextcloud Talk Recording",
"display_name": "Talk Recording",
"image": "ghcr.io/nextcloud-releases/aio-talk-recording",
"user": "122",
"init": true,
@@ -820,7 +824,7 @@
{
"container_name": "nextcloud-aio-docker-socket-proxy",
"image_tag": "%AIO_CHANNEL%",
"display_name": "Docker Socket Proxy (deprecated)",
"display_name": "Docker Socket Proxy",
"image": "ghcr.io/nextcloud-releases/aio-docker-socket-proxy",
"init": true,
"internal_port": "2375",
@@ -843,52 +847,10 @@
"NET_RAW"
]
},
{
"container_name": "nextcloud-aio-harp",
"image_tag": "release",
"display_name": "HaRP",
"image": "ghcr.io/nextcloud/nextcloud-appapi-harp",
"init": true,
"internal_port": "8780",
"expose": [
"8780"
],
"environment": [
"HP_SHARED_KEY=%HP_SHARED_KEY%",
"NC_INSTANCE_URL=https://%NC_DOMAIN%",
"HP_LOG_LEVEL=warning",
"HP_FRP_DISABLE_TLS=true",
"TZ=%TIMEZONE%"
],
"secrets": [
"HP_SHARED_KEY"
],
"volumes": [
{
"source": "%WATCHTOWER_DOCKER_SOCKET_PATH%",
"destination": "/var/run/docker.sock",
"writeable": false
},
{
"source": "nextcloud_aio_harp",
"destination": "/certs",
"writeable": true
}
],
"restart": "unless-stopped",
"read_only": true,
"tmpfs": [
"/tmp",
"/run/harp"
],
"cap_drop": [
"NET_RAW"
]
},
{
"container_name": "nextcloud-aio-whiteboard",
"image_tag": "%AIO_CHANNEL%",
"display_name": "Nextcloud Whiteboard",
"display_name": "Whiteboard",
"image": "ghcr.io/nextcloud-releases/aio-whiteboard",
"user": "65534",
"init": true,

View File

@@ -1,10 +1,6 @@
<?php
declare(strict_types=1);
$domain = '';
if (isset($_GET['domain']) && is_string($_GET['domain'])) {
$domain = $_GET['domain'];
}
$domain = $_GET['domain'] ?? '';
if (!str_contains($domain, '.')) {
http_response_code(400);

View File

@@ -1,2 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<files psalm-version="6.15.1@28dc127af1b5aecd52314f6f645bafc10d0e11f9"/>
<files psalm-version="6.14.3@d0b040a91f280f071c1abcb1b77ce3822058725a"/>

View File

@@ -1,9 +1,4 @@
document.addEventListener("DOMContentLoaded", function () {
// Don't run if the expected form isn't present.
if (document.getElementById('options-form') === null) {
return;
}
// Hide submit button initially
const optionsFormSubmit = document.querySelectorAll(".options-form-submit");
optionsFormSubmit.forEach(element => {
@@ -121,26 +116,13 @@ document.addEventListener("DOMContentLoaded", function () {
function handleDockerSocketProxyWarning() {
if (document.getElementById("docker-socket-proxy").checked) {
// TODO: remove the line below and uncomment the lines further down once https://github.com/nextcloud/app_api/pull/800 is included
alert('⚠️ Warning! Enabling this container comes with possible Security problems since you are exposing the docker socket and all its privileges to the Nextcloud container. Enable this only if you are sure what you are doing!');
// alert('⚠️ The docker socket proxy container is deprecated. Please use the HaRP (High-availability Reverse Proxy for Nextcloud ExApps) instead!');
// document.getElementById("docker-socket-proxy").checked = false
}
}
function handleHarpWarning() {
if (document.getElementById("harp").checked) {
alert('⚠️ Warning! Enabling this container comes with possible Security problems since you are exposing the docker socket and all its privileges to the HaRP container. Enable this only if you are sure what you are doing!');
document.getElementById("docker-socket-proxy").checked = false
}
}
// Initialize event listeners for specific behaviors
document.getElementById("talk").addEventListener('change', handleTalkVisibility);
document.getElementById("docker-socket-proxy").addEventListener('change', handleDockerSocketProxyWarning);
if (document.getElementById("harp")) {
document.getElementById("harp").addEventListener('change', handleHarpWarning);
}
// Initialize talk-recording visibility on page load
handleTalkVisibility(); // Ensure talk-recording is correctly initialized

View File

@@ -1,7 +0,0 @@
document.addEventListener("DOMContentLoaded", function(event) {
// HaRP
let harp = document.getElementById("harp");
if (harp) {
harp.disabled = true;
}
});

View File

@@ -70,24 +70,15 @@ function showPassword(id) {
}
form.onsubmit = submit;
console.info(form);
}
function initForms() {
const forms = document.querySelectorAll('form.xhr')
console.info("Making " + forms.length + " form(s) use XHR.");
for (const form of forms) {
initForm(form);
}
const overlayLogForms = document.querySelectorAll('form[target="overlay-log"]')
for (const form of overlayLogForms) {
form.onsubmit = function() {
enableSpinner();
document.getElementById('overlay-log')?.classList.add('visible');
// Reload the page after the response was fully loaded into the iframe.
document.querySelector('iframe[name="overlay-log"]').addEventListener('load', () => {
location.reload();
});
};
}
}
if (document.readyState === 'loading') {

View File

@@ -11,7 +11,6 @@ ini_set('max_execution_time', '7200');
ini_set('log_errors_max_len', '0');
use DI\Container;
use DI\NotFoundException;
use Slim\Csrf\Guard;
use Slim\Factory\AppFactory;
use Slim\Views\Twig;
@@ -137,7 +136,6 @@ $app->get('/containers', function (Request $request, Response $response, array $
'is_nvidia_gpu_enabled' => $configurationManager->enableNvidiaGpu,
'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled,
'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled,
'is_harp_enabled' => $configurationManager->isHarpEnabled,
'is_whiteboard_enabled' => $configurationManager->isWhiteboardEnabled,
'community_containers' => $configurationManager->listAvailableCommunityContainers(),
'community_containers_enabled' => $configurationManager->aioCommunityContainers,
@@ -172,15 +170,6 @@ $app->get('/setup', function (Request $request, Response $response, array $args)
]
);
});
$app->get('/log', function (Request $request, Response $response, array $args) use ($container) {
$params = $request->getQueryParams();
$id = $params['id'] ?? '';
if (!str_starts_with($id, 'nextcloud-aio-')) {
throw new DI\NotFoundException();
}
$view = Twig::fromRequest($request);
return $view->render($response, 'log.twig', ['id' => $id]);
});
// Auth Redirector
$app->get('/', function (\Psr\Http\Message\RequestInterface $request, Response $response, array $args) use ($container) {
@@ -208,13 +197,4 @@ $app->get('/', function (\Psr\Http\Message\RequestInterface $request, Response $
$errorMiddleware = $app->addErrorMiddleware(false, true, true);
// Set a custom Not Found handler, which doesn't pollute the app output with 404 errors.
$errorMiddleware->setErrorHandler(
\Slim\Exception\HttpNotFoundException::class,
function (Request $request, Throwable $exception, bool $displayErrorDetails) use ($app) {
$response = $app->getResponseFactory()->createResponse();
$response->getBody()->write('Not Found');
return $response->withStatus(404);
});
$app->run();

View File

@@ -1,142 +0,0 @@
class LogViewer {
// Configure the interval in seconds for autoloading log data.
autoloadIntervalSec = 5;
// Set to true to see some debug log statements in the browser console.
debugLog = false;
// Don't touch these, please.
containerId;
apiBaseUrl = 'api/docker/logs';
autoloadIntervalId = null;
logElem;
lastLogTimestamp = '';
autoloadingDisabledFromButton = false;
loaderElem;
dataLoadingLock;
constructor() {
const id = document.body.dataset.containerId;
if (typeof(id) !== 'string' || !id.startsWith('nextcloud-aio-')) {
throw new Exception('Invalid container ID');
}
this.containerId = id;
this.logElem = document.querySelector('pre');
this.loaderElem = document.querySelector('.loader');
this.initAutoloadingControls();
// Enable automatic log data loading.
this.startAutoloading();
}
startAutoloading() {
// Load log data immediately.
this.loadAndAppendLogData();
// Load new log data repeatedly.
this.debug("Starting autoloading");
this.autoloadIntervalId = setInterval(() => {
if (this.isAutoloadingEnabled()) {
this.loadAndAppendLogData();
}
}, 5000);
}
stopAutoloading() {
this.debug("Stopping autoloading");
clearInterval(this.autoloadIntervalId);
this.autoloadIntervalId = null;
}
isAutoloadingEnabled() {
return !!this.autoloadIntervalId;
}
getUrl() {
return `${this.apiBaseUrl}?id=${this.containerId}&since=${this.lastLogTimestamp}`;
}
debug(...args) {
if (this.debugLog) {
console.debug('LogViewer:', ...args);
}
}
// Load log data and append it to the DOM.
loadAndAppendLogData() {
if (this.dataLoadingLock) {
this.debug("Another log data loading request is still running, cancelling this request");
return;
}
this.debug("Loading new log data");
this.dataLoadingLock = true;
this.loaderElem.classList.remove('hidden');
fetch(this.getUrl())
.then((response) => {
if (!response.ok) {
throw new Error("Error while fetching log data!");
}
return response;
})
.then((response) => response.text())
.then((text) => {
text = text.trim();
if (text.length === 0) {
this.debug("Received no new log data from server");
return;
}
this.debug("Received", Math.round(text.length / 1024), "KB of new log data from server");
this.logElem.append(text + "\n");
this.scrollToBottom();
this.lastLogTimestamp = text.split("\n").at(-1)?.split(' ')[0] ?? '';
})
.finally(() => {
this.dataLoadingLock = false;
this.loaderElem.classList.add('hidden');
this.debug("Finished log data loading");
})
.catch((err) => console.error(err));
}
scrollToBottom() {
window.scrollTo(0, document.body.scrollHeight);
}
initAutoloadingControls() {
// Provide a button that allows to manually disable the autoloading.
const button = document.getElementById('autoloading-control');
const statusElem = document.getElementById('autoloading-status');
if (!button) {
return;
}
button.addEventListener('click', (event) => {
event.preventDefault();
if (this.isAutoloadingEnabled()) {
this.stopAutoloading();
statusElem.textContent = 'disabled';
button.textContent = 'Enable';
this.autoloadingDisabledFromButton = true;
} else {
this.startAutoloading();
statusElem.textContent = 'enabled';
button.textContent = 'Disable';
this.autoloadingDisabledFromButton = false;
}
});
// Load new data immediately if the window gets visible to the user again (unless autoloading has been
// disabled).
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'visible') {
this.debug("Window became visible");
if (!this.autoloadingDisabledFromButton) {
this.startAutoloading();
}
} else {
this.debug("Window became hidden");
this.stopAutoloading();
}
});
}
}
document.addEventListener("DOMContentLoaded", () => {
new LogViewer();
});

View File

@@ -11,6 +11,7 @@
--color-error-text: #c20505;
--color-success: #46ba61;
--color-running: #ffd000;
--color-warning: #ff6b00;
--color-primary-element: #00679e;
--color-primary-element-hover: #005a8a;
--color-primary-element-text: #ffffff;
@@ -174,6 +175,11 @@ span.running {
background-color: var(--color-running);
}
.container-update-warning {
color: var(--color-warning);
font-weight: bold;
}
div.toast.success {
border-left-color: var(--color-success);
}
@@ -468,29 +474,7 @@ input[type="checkbox"]:disabled:not(:checked) + label {
}
#overlay.loading {
display: grid;
justify-items: center;
row-gap: 2rem;
}
#overlay #overlay-log.visible {
visibility: visible;
opacity: 1;
transition: opacity 1s ease-in;
}
#overlay #overlay-log {
visibility: hidden;
opacity: 0;
align-self: start;
width: 20%;
height: 7rem;
border-radius: var(--border-radius-large);
border: solid thin rgb(192, 192, 192);
}
.overlay-iframe {
padding: 1rem;
display: block;
}
.loader {
@@ -501,7 +485,9 @@ input[type="checkbox"]:disabled:not(:checked) + label {
height: 120px;
-webkit-animation: spin 2s linear infinite; /* Safari */
animation: spin 2s linear infinite;
align-self: end;
position: absolute;
top: calc(50% - 60px);
left: calc(50% - 60px);
}
/* Safari */
@@ -725,4 +711,4 @@ input[type="checkbox"]:disabled:not(:checked) + label {
.office-suite-cards {
grid-template-columns: 1fr;
}
}
}

View File

@@ -2,26 +2,28 @@
function toggleTheme() {
const currentTheme = document.documentElement.getAttribute('data-theme');
const newTheme = (currentTheme === 'dark') ? '' : 'dark'; // Toggle between no theme and dark theme
setThemeToDOM(newTheme);
document.documentElement.setAttribute('data-theme', newTheme);
localStorage.setItem('theme', newTheme);
// Change the icon based on the current theme
setThemeIcon(newTheme);
const themeIcon = document.getElementById('theme-icon');
themeIcon.textContent = newTheme === 'dark' ? '☀️' : '🌙'; // Switch between moon and sun icons
}
function setThemeToDOM(value) {
// Set the theme to the root document and all possible iframe documents (so they can adapt their styling, too).
const documents = [document, Array.from(document.querySelectorAll('iframe')).map((iframe) => iframe.contentDocument)].flat()
documents.forEach((doc) => doc.documentElement.setAttribute('data-theme', value));
}
function getSavedTheme() {
return localStorage.getItem('theme') ?? '';
// Function to immediately apply saved theme without icon update
function applySavedThemeImmediately() {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.documentElement.setAttribute('data-theme', 'dark');
} else {
document.documentElement.removeAttribute('data-theme'); // Default to light theme
}
}
// Function to apply theme-icon update
function setThemeIcon(theme) {
if (theme === 'dark') {
function setThemeIcon() {
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
document.getElementById('theme-icon').textContent = '☀️'; // Sun icon for dark mode
} else {
document.getElementById('theme-icon').textContent = '🌙'; // Moon icon for light mode
@@ -29,7 +31,7 @@ function setThemeIcon(theme) {
}
// Immediately apply the saved theme to avoid flickering
setThemeToDOM(getSavedTheme());
applySavedThemeImmediately();
// Apply theme when the page loads
document.addEventListener('DOMContentLoaded', () => setThemeIcon(getSavedTheme()));
document.addEventListener('DOMContentLoaded', setThemeIcon);

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Auth;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Auth;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Container;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO;
@@ -91,10 +90,6 @@ readonly class ContainerDefinitionFetcher {
if (!$this->configurationManager->isDockerSocketProxyEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-harp') {
if (!$this->configurationManager->isHarpEnabled) {
continue;
}
} elseif ($entry['container_name'] === 'nextcloud-aio-whiteboard') {
if (!$this->configurationManager->isWhiteboardEnabled) {
continue;
@@ -204,10 +199,6 @@ readonly class ContainerDefinitionFetcher {
if (!$this->configurationManager->isDockerSocketProxyEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-harp') {
if (!$this->configurationManager->isHarpEnabled) {
continue;
}
} elseif ($value === 'nextcloud-aio-whiteboard') {
if (!$this->configurationManager->isWhiteboardEnabled) {
continue;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Controller;
@@ -96,7 +95,6 @@ readonly class ConfigurationController {
$this->configurationManager->isImaginaryEnabled = isset($request->getParsedBody()['imaginary']);
$this->configurationManager->isFulltextsearchEnabled = isset($request->getParsedBody()['fulltextsearch']);
$this->configurationManager->isDockerSocketProxyEnabled = isset($request->getParsedBody()['docker-socket-proxy']);
$this->configurationManager->isHarpEnabled = isset($request->getParsedBody()['harp']);
$this->configurationManager->isWhiteboardEnabled = isset($request->getParsedBody()['whiteboard']);
}

View File

@@ -1,16 +1,13 @@
<?php
declare(strict_types=1);
namespace AIO\Controller;
use AIO\Container\Container;
use AIO\Container\ContainerState;
use AIO\ContainerDefinitionFetcher;
use AIO\Docker\DockerActionManager;
use Psr\Http\Message\ResponseInterface as Response;
use Psr\Http\Message\ServerRequestInterface as Request;
use AIO\Data\ConfigurationManager;
use Slim\Psr7\NonBufferedBody;
readonly class DockerController {
private const string TOP_CONTAINER = 'nextcloud-aio-apache';
@@ -22,12 +19,12 @@ readonly class DockerController {
) {
}
private function PerformRecursiveContainerStart(string $id, bool $pullImage = true, ?\Closure $addToStreamingResponseBody = null) : void {
private function PerformRecursiveContainerStart(string $id, bool $pullImage = true) : void {
$container = $this->containerDefinitionFetcher->GetContainerById($id);
// Start all dependencies first and then itself
foreach($container->dependsOn as $dependency) {
$this->PerformRecursiveContainerStart($dependency, $pullImage, $addToStreamingResponseBody);
$this->PerformRecursiveContainerStart($dependency, $pullImage);
}
// Don't start if container is already running
@@ -39,9 +36,9 @@ readonly class DockerController {
$this->dockerActionManager->DeleteContainer($container);
$this->dockerActionManager->CreateVolumes($container);
$this->dockerActionManager->PullImage($container, $pullImage, $addToStreamingResponseBody);
$this->dockerActionManager->PullImage($container, $pullImage);
$this->dockerActionManager->CreateContainer($container);
$this->dockerActionManager->StartContainer($container, $addToStreamingResponseBody);
$this->dockerActionManager->StartContainer($container);
$this->dockerActionManager->ConnectContainerToNetwork($container);
}
@@ -71,8 +68,7 @@ readonly class DockerController {
$id = $requestParams['id'];
}
if (str_starts_with($id, 'nextcloud-aio-')) {
$since = $this->getTimestampForDockerLogsApiSince($requestParams['since'] ?? '');
$logs = $this->dockerActionManager->GetLogs($id, $since);
$logs = $this->dockerActionManager->GetLogs($id);
} else {
$logs = 'Container not found.';
}
@@ -182,7 +178,7 @@ readonly class DockerController {
}
if (isset($request->getParsedBody()['install_latest_major'])) {
$installLatestMajor = '33';
$installLatestMajor = '32';
} else {
$installLatestMajor = '';
}
@@ -201,37 +197,17 @@ readonly class DockerController {
if ($pullImage === false) {
error_log('WARNING: Not pulling container images. Instead, using local ones.');
}
$nonbufResp = $response
->withBody(new NonBufferedBody())
->withHeader('Content-Type', 'text/html; charset=utf-8')
->withHeader('X-Accel-Buffering', 'no')
->withHeader('Cache-Control', 'no-cache');
// Text written into this body is immediately sent to the client, without waiting for later content.
$streamingResponseBody = $nonbufResp->getBody();
$streamingResponseBody->write($this->getStreamingResponseHtmlStart());
// 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
// wanted markup or formatting.
$addToStreamingResponseBody = function (Container $container, string $message) use ($streamingResponseBody) : void {
$streamingResponseBody->write("<div>{$container->displayName}: {$message}</div>");
};
// Start container
$this->startTopContainer($pullImage, $addToStreamingResponseBody);
$this->startTopContainer($pullImage);
// Clear apcu cache in order to check if container updates are available
// Temporarily disabled as it leads much faster to docker rate limits
// apcu_clear_cache();
$streamingResponseBody->write($this->getStreamingResponseHtmlEnd());
return $nonbufResp;
return $response->withStatus(201)->withHeader('Location', '.');
}
public function startTopContainer(bool $pullImage, ?\Closure $addToStreamingResponseBody = null) : void {
public function startTopContainer(bool $pullImage) : void {
$this->configurationManager->aioToken = bin2hex(random_bytes(24));
// Stop domaincheck since apache would not be able to start otherwise
@@ -239,7 +215,7 @@ readonly class DockerController {
$id = self::TOP_CONTAINER;
$this->PerformRecursiveContainerStart($id, $pullImage, $addToStreamingResponseBody);
$this->PerformRecursiveContainerStart($id, $pullImage);
}
public function StartWatchtowerContainer(Request $request, Response $response, array $args) : Response {
@@ -331,65 +307,4 @@ readonly class DockerController {
$id = 'nextcloud-aio-domaincheck';
$this->PerformRecursiveContainerStop($id);
}
private function getStreamingResponseHtmlStart() : string {
return <<<END
<!DOCTYPE html>
<html lang="en" class="overlay-iframe">
<head>
<link rel="stylesheet" href="../../style.css?v8" media="all" />
<script>
const observer = new MutationObserver((records) => {
const node = records[0]?.addedNodes[0];
// Text nodes also appear here but can't be scrolled to, so we have to check for the
// function being present.
if (node && typeof(node.scrollIntoView) === 'function') {
node.scrollIntoView();
}
});
observer.observe(document, {childList: true, subtree: true});
</script>
</head>
<body>
END;
}
private function getStreamingResponseHtmlEnd() : string {
return "\n </body>\n</html>";
}
private function getTimestampForDockerLogsApiSince(string $input) : string
{
if ($input === '') {
return '';
}
// We expect an RFC3339Nano string with Timezone UTC here, as docker will put out.
// Unfortunately PHP doesn't support this format with nanoseconds, so we have to help
// ourselves a little bit.
// First we split off the nanoseconds.
preg_match('/^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2})\.(\d{9}).*/', $input, $match);
if (count($match) !== 3) {
// The input doesn't match our expectations, it might be manipulated, we ignore it.
return '';
}
$datetime = \DateTimeImmutable::createFromFormat("Y-m-d\\TH:i:s", $match[1]);
$nanoseconds = $match[2];
if ($datetime === false) {
// Input was not parseable, it might be manipulated, we ignore it.
return '';
}
// Format the datetime as unix timestamp.
$timestamp = $datetime->format('U');
// Increase the nanoseconds by 1, so we don't get the line with exactly the original datetime again.
$nanoseconds = strval(intval($nanoseconds) + 1);
// Now append the nanoseconds to the timestamp-string.
return "{$timestamp}.{$nanoseconds}";
}
}

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Controller;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Data;
@@ -30,11 +29,6 @@ class ConfigurationManager
set { $this->set('isDockerSocketProxyEnabled', $value); }
}
public bool $isHarpEnabled {
get => $this->get('isHarpEnabled', false);
set { $this->set('isHarpEnabled', $value); }
}
public bool $isWhiteboardEnabled {
// Type-cast because old configs could have 1/0 for this key.
get => (bool) $this->get('isWhiteboardEnabled', true);
@@ -120,7 +114,7 @@ class ConfigurationManager
// Type-cast because old configs could have 1/0 for this key.
get => (bool) $this->get('isFulltextsearchEnabled', false);
// Elasticsearch does not work on kernels without seccomp anymore. See https://github.com/nextcloud/all-in-one/discussions/5768
set { $this->set('isFulltextsearchEnabled', (!$this->collaboraSeccompDisabled && $value)); }
set { $this->set('isFulltextsearchEnabled', ($this->collaboraSeccompDisabled && $value)); }
}
public string $domain {
@@ -1040,7 +1034,6 @@ class ConfigurationManager
'IMAGINARY_ENABLED' => $this->isImaginaryEnabled ? 'yes' : '',
'FULLTEXTSEARCH_ENABLED' => $this->isFulltextsearchEnabled ? 'yes' : '',
'DOCKER_SOCKET_PROXY_ENABLED' => $this->isDockerSocketProxyEnabled ? 'yes' : '',
'HARP_ENABLED' => $this->isHarpEnabled ? 'yes' : '',
'NEXTCLOUD_UPLOAD_LIMIT' => $this->nextcloudUploadLimit,
'NEXTCLOUD_MEMORY_LIMIT' => $this->nextcloudMemoryLimit,
'NEXTCLOUD_MAX_TIME' => $this->nextcloudMaxTime,

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Data;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Data;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Docker;
@@ -145,12 +144,11 @@ readonly class DockerActionManager {
}
}
public function GetLogs(string $id, string $since = ''): string {
public function GetLogs(string $id): string {
$url = $this->BuildApiUrl(
sprintf(
'containers/%s/logs?stdout=true&stderr=true&timestamps=true&since=%s',
urlencode($id),
$since
'containers/%s/logs?stdout=true&stderr=true&timestamps=true',
urlencode($id)
));
$responseBody = (string)$this->guzzleClient->get($url)->getBody();
@@ -167,12 +165,9 @@ readonly class DockerActionManager {
return $response;
}
public function StartContainer(Container $container, ?\Closure $addToStreamingResponseBody = null): void {
public function StartContainer(Container $container): void {
$url = $this->BuildApiUrl(sprintf('containers/%s/start', urlencode($container->identifier)));
try {
if ($addToStreamingResponseBody !== null) {
$addToStreamingResponseBody($container, "Starting container");
}
$this->guzzleClient->post($url);
} catch (RequestException $e) {
throw new \Exception("Could not start container " . $container->identifier . ": " . $e->getResponse()?->getBody()->getContents());
@@ -477,7 +472,8 @@ readonly class DockerActionManager {
}
}
public function PullImage(Container $container, bool $pullImage = true, ?\Closure $addToStreamingResponseBody = null): void {
public function PullImage(Container $container, bool $pullImage = true): void {
// Skip database image pull if the last shutdown was not clean
if ($container->identifier === 'nextcloud-aio-database') {
if ($this->GetDatabasecontainerExitCode() > 0) {
@@ -505,9 +501,6 @@ readonly class DockerActionManager {
$url = $this->BuildApiUrl(sprintf('images/create?fromImage=%s', $encodedImageName));
$imageIsThere = true;
try {
if ($addToStreamingResponseBody) {
$addToStreamingResponseBody($container, "Pulling image");
}
$imageUrl = $this->BuildApiUrl(sprintf('images/%s/json', $encodedImageName));
$this->guzzleClient->get($imageUrl)->getBody()->getContents();
} catch (\Throwable $e) {
@@ -655,8 +648,8 @@ readonly class DockerActionManager {
if (count($imageNameArray) === 2) {
$imageName = $imageNameArray[0];
} else {
error_log("Unexpected image name was found when getting the current image name of the mastercontainer. You probably did not follow the documentation correctly. Changing the image name to the default 'ghcr.io/nextcloud-releases/all-in-one'.");
$imageName = 'ghcr.io/nextcloud-releases/all-in-one';
error_log("No tag was found when getting the current channel. You probably did not follow the documentation correctly. Changing the imageName to the default " . $output['Config']['Image']);
$imageName = $output['Config']['Image'];
}
apcu_add($cacheKey, $imageName);
return $imageName;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Docker;

View File

@@ -1,5 +1,5 @@
<?php
declare(strict_types=1);
namespace AIO\Docker;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Middleware;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Twig;

View File

@@ -1,5 +1,4 @@
<?php
declare(strict_types=1);
namespace AIO\Twig;

View File

@@ -4,21 +4,21 @@
{% if c.GetStartingState().value == 'starting' %}
<span class="status running"></span>
{{ c.displayName }}
(<a href="log?id={{ c.identifier }}" target="_blank">Starting</a>)
(<a href="api/docker/logs?id={{ c.identifier }}" target="_blank">Starting</a>)
{% elseif c.GetRunningState().value == 'running' %}
<span class="status success"></span>
{{ c.displayName }}
(<a href="log?id={{ c.identifier }}" target="_blank">Running</a>)
(<a href="api/docker/logs?id={{ c.identifier }}" target="_blank">Running</a>)
{% else %}
<span class="status error"></span>
{{ c.displayName }}
(<a href="log?id={{ c.identifier }}" target="_blank">Stopped</a>)
(<a href="api/docker/logs?id={{ c.identifier }}" target="_blank">Stopped</a>)
{% endif %}
{% if c.documentation != '' %}
(<a target="_blank" href="{{ c.documentation }}">docs</a>)
{% endif %}
{% if c.GetUpdateState().value == 'different' %}
⚠️&nbsp;Update&nbsp;available
<span class="container-update-warning">⚠️ Update available</span>
{% endif %}
</span>
{% if c.GetUiSecret() != '' %}
@@ -27,4 +27,4 @@
<input type="text" value="{{ c.GetUiSecret() }}" readonly>
</details>
{% endif %}
</li>
</li>

View File

@@ -27,7 +27,7 @@
<script type="text/javascript" src="timezone.js"></script>
{# js for optional containers and additional containers forms #}
<script type="text/javascript" src="containers-form-submit.js?v6"></script>
<script type="text/javascript" src="containers-form-submit.js?v5"></script>
{% set hasBackupLocation = borg_backup_host_location or borg_remote_repo %}
{% set isAnyRunning = false %}
@@ -37,8 +37,7 @@
{% set isBackupOrRestoreRunning = false %}
{% set isApacheStarting = false %}
{# Setting newMajorVersion to '' will hide corresponding options/elements, can be set to an integer like 26 in order to show corresponding elements. If set, also increase installLatestMajor in https://github.com/nextcloud/all-in-one/blob/main/php/src/Controller/DockerController.php #}
{% set newMajorVersionString = '26 Winter' %}
{% set oldMajorVersionString = '25 Autumn' %}
{% set newMajorVersionString = '' %}
{% if is_backup_container_running == true %}
{% if borg_backup_mode == 'backup' or borg_backup_mode == 'restore' %}
@@ -65,11 +64,11 @@
{% endfor %}
{% if is_daily_backup_running == true %}
<p><span class="status running"></span> Daily backup currently running. (<a href="log?id=nextcloud-aio-mastercontainer" target="_blank">Mastercontainer logs</a>) (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Borg backup container logs</a>)</p>
<p><span class="status running"></span> Daily backup currently running. (<a href="api/docker/logs?id=nextcloud-aio-mastercontainer" target="_blank">Mastercontainer logs</a>) (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Borg backup container logs</a>)</p>
{% if automatic_updates == true %}
<p>This will update your containers, the mastercontainer and, on Saturdays, your Nextcloud apps if the backup is successful.</p>
{% if is_mastercontainer_update_available == true %}
<p>When the mastercontainer is updated it will restart, making it unavailable for a moment. (<a href="log?id=nextcloud-aio-watchtower" target="_blank">Logs</a>)</p>
<p>When the mastercontainer is updated it will restart, making it unavailable for a moment. (<a href="api/docker/logs?id=nextcloud-aio-watchtower" target="_blank">Logs</a>)</p>
{% endif %}
{% endif %}
{% if has_update_available == false %}
@@ -80,7 +79,7 @@
<p><a href="" class="button reload">Reload ↻</a></p>
<p>If the daily backup is stuck somehow, you can unstick it by running <strong>sudo docker exec nextcloud-aio-mastercontainer rm /mnt/docker-aio-config/data/daily_backup_running</strong> and afterwards reloading this interface.</p>
{% elseif isWatchtowerRunning == true %}
<p><span class="status running"></span> Mastercontainer update currently running. Once the update is complete the mastercontainer will restart, making it unavailable for a moment. Please wait until it's done. (<a href="log?id=nextcloud-aio-watchtower" target="_blank">Logs</a>)</p>
<p><span class="status running"></span> Mastercontainer update currently running. Once the update is complete the mastercontainer will restart, making it unavailable for a moment. Please wait until it's done. (<a href="api/docker/logs?id=nextcloud-aio-watchtower" target="_blank">Logs</a>)</p>
<p><a href="" class="button reload">Reload ↻</a></p>
{% else %}
{% if is_backup_container_running == false and domain == "" %}
@@ -142,7 +141,7 @@
{% if hasBackupLocation %}
{% if borg_backup_mode in ['test', 'check'] %}
{% if backup_exit_code > 0 %}
<p><span class="status error"></span> Last {{ borg_backup_mode }} failed! (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><span class="status error"></span> Last {{ borg_backup_mode }} failed! (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
{% if borg_backup_mode == 'test' %}
<p>Please adjust the path and/or the encryption password in order to make it work!</p>
{% elseif borg_backup_mode == 'check' %}
@@ -158,7 +157,7 @@
</details>
{% endif %}
{% elseif backup_exit_code == 0 %}
<p><span class="status success"></span> Last {{ borg_backup_mode }} successful! (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><span class="status success"></span> Last {{ borg_backup_mode }} successful! (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
{% if borg_backup_mode == 'test' %}
<p>Feel free to check the integrity of the backup archive below before starting the restore process in order to make ensure that the restore will work. This can take a long time though depending on the size of the backup archive and is thus not required.</p>
<form method="POST" action="api/docker/backup-check" class="xhr">
@@ -183,7 +182,7 @@
{% endif %}
{% elseif borg_backup_mode == 'restore' %}
{% if backup_exit_code > 0 %}
<p><span class="status error"></span> Last restore failed! (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><span class="status error"></span> Last restore failed! (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p>The restore process has unexpectedly failed! Please adjust the path and encryption password, test it and try to restore again!</p>
{% endif %}
{% endif %}
@@ -228,14 +227,14 @@
{% if was_start_button_clicked == true %}
{% if current_channel starts with 'latest' or current_channel starts with 'beta' or current_channel starts with 'develop' %}
<p>You are running the <a target="_blank" href="https://github.com/nextcloud/all-in-one#how-to-switch-the-channel"><strong>{{ current_channel }}</strong></a> channel. (<a href="log?id=nextcloud-aio-mastercontainer" target="_blank">Logs</a>)</p>
<p>You are running the <a target="_blank" href="https://github.com/nextcloud/all-in-one#how-to-switch-the-channel"><strong>{{ current_channel }}</strong></a> channel. (<a href="api/docker/logs?id=nextcloud-aio-mastercontainer" target="_blank">Logs</a>)</p>
{% else %}
<p>No channel was found. This means that AIO is not able to update itself and its component and will also not be able to report about updates. Updates need to be done externally.</p>
{% endif %}
{% endif %}
{% if is_backup_container_running == true %}
<p><span class="status running"></span> Backup container is currently running: {{ borg_backup_mode }} (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><span class="status running"></span> Backup container is currently running: {{ borg_backup_mode }} (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><a href="" class="button reload">Reload ↻</a></p>
{% endif %}
@@ -298,7 +297,7 @@
{% if newMajorVersionString != '' and isAnyRunning == true and isApacheStarting != true %}
<details>
<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/7523">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 follow <strong><a target="_blank" href="https://github.com/nextcloud/all-in-one/discussions/6865">this documentation</a></strong></p>
</details>
{% endif %}
{% endif %}
@@ -339,24 +338,24 @@
</form>
{% else %}
{% if was_start_button_clicked == false %}
<form method="POST" action="api/docker/start" target="overlay-log">
<form method="POST" action="api/docker/start" class="xhr">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input id="base_path" type="hidden" name="base_path" value="">
{% if newMajorVersionString != '' %}
<input type="checkbox" id="install_latest_major" name="install_latest_major"><label for="install_latest_major">Install Nextcloud Hub {{ newMajorVersionString }} (if unchecked, Nextcloud Hub {{ oldMajorVersionString }} will get installed)</label><br>
<input type="checkbox" id="install_latest_major" name="install_latest_major"><label for="install_latest_major">Install Nextcloud Hub {{ newMajorVersionString }} (if unchecked, Nextcloud Hub 10 will get installed)</label><br>
{% endif %}
<input type="submit" value="Download and start containers" />
</form>
{% elseif has_update_available == false %}
<form method="POST" action="api/docker/start" target="overlay-log">
<form method="POST" action="api/docker/start" class="xhr">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input id="base_path" type="hidden" name="base_path" value="">
<input type="submit" value="Start containers" />
</form>
{% else %}
<form method="POST" action="api/docker/start" target="overlay-log">
<form method="POST" action="api/docker/start" class="xhr">
<input type="hidden" name="{{csrf.keys.name}}" value="{{csrf.name}}">
<input type="hidden" name="{{csrf.keys.value}}" value="{{csrf.value}}">
<input id="base_path" type="hidden" name="base_path" value="">
@@ -401,7 +400,7 @@
{% if is_backup_container_running == false %}
<h2>Backup and restore</h2>
{% if backup_exit_code > 0 %}
<p><span class="status error"></span> Last {{ borg_backup_mode }} failed! (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><span class="status error"></span> Last {{ borg_backup_mode }} failed! (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
{% if borg_backup_mode == "check" %}
<p>The backup check was not successful. This might indicate a corrupt archive (look at the logs). If that should be the case, you can try to fix it by following <a target="_blank" href="https://borgbackup.readthedocs.io/en/stable/faq.html#i-get-an-integrityerror-or-similar-what-now"><strong>this documentation</strong></a></p>
<details>
@@ -435,9 +434,9 @@
{% endif %}
{% elseif backup_exit_code == 0 %}
{% if borg_backup_mode == "backup" %}
<p><span class="status success"></span> Last {{ borg_backup_mode }} successful on {{ last_backup_time }} UTC! (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><span class="status success"></span> Last {{ borg_backup_mode }} successful on {{ last_backup_time }} UTC! (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
{% else %}
<p><span class="status success"></span> Last {{ borg_backup_mode }} successful! (<a href="log?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
<p><span class="status success"></span> Last {{ borg_backup_mode }} successful! (<a href="api/docker/logs?id=nextcloud-aio-borgbackup" target="_blank">Logs</a>)</p>
{% endif %}
{% endif %}
{% endif %}

View File

@@ -1 +1 @@
12.8.0
12.6.1

View File

@@ -72,8 +72,8 @@
<li>Good Nextcloud integration</li>
<li>Open core</li>
<li>Best performance</li>
<li>Best Microsoft compatibility</li>
<li>Limited ODF compatibility</li>
<li>Best Microsoft compatibility</li>
</ul>
{% if isAnyRunning == false %}
<a href="https://www.onlyoffice.com/" target="_blank" class="office-learn-more" onclick="event.stopPropagation();">
@@ -196,24 +196,8 @@
data-initial-state="false"
{% endif %}
>
<label for="docker-socket-proxy">Docker Socket Proxy (needed for <a target="_blank" href="https://github.com/cloud-py-api/app_api#nextcloud-appapi">Nextcloud App API</a>) ⚠️ The docker socket proxy container is deprecated. Please use the HaRP (High-availability Reverse Proxy for Nextcloud ExApps) instead!</label>
<label for="docker-socket-proxy">Docker Socket Proxy (needed for <a target="_blank" href="https://github.com/cloud-py-api/app_api#nextcloud-appapi">Nextcloud App API</a>)</label>
</p>
{#
<p>
<input
type="checkbox"
id="harp"
name="harp"
{% if is_harp_enabled == true %}
checked="checked"
data-initial-state="true"
{% else %}
data-initial-state="false"
{% endif %}
>
<label for="harp">HaRP (<a target="_blank" href="https://github.com/nextcloud/HaRP">High-availability Reverse Proxy</a> for Nextcloud ExApps)</label>
</p>
#}
<p>
<input
type="checkbox"
@@ -234,7 +218,6 @@
{% if isAnyRunning == true %}
<script type="text/javascript" src="disable-clamav.js"></script>
<script type="text/javascript" src="disable-docker-socket-proxy.js"></script>
<script type="text/javascript" src="disable-harp.js"></script>
<script type="text/javascript" src="disable-talk.js"></script>
<script type="text/javascript" src="disable-collabora.js?v2"></script>
<script type="text/javascript" src="disable-onlyoffice.js?v2"></script>

View File

@@ -1,10 +1,10 @@
<html>
<head>
<title>AIO</title>
<link rel="stylesheet" href="style.css?v8" media="all" />
<link rel="stylesheet" href="style.css?v7" media="all" />
<link rel="icon" href="img/favicon.png">
<script type="text/javascript" src="forms.js?v1"></script>
<script type="text/javascript" src="toggle-dark-mode.js?v1"></script>
<script type="text/javascript" src="forms.js"></script>
<script type="text/javascript" src="toggle-dark-mode.js"></script>
</head>
<body>
@@ -13,7 +13,6 @@
</div>
<div id="overlay">
<div class="loader"></div>
<iframe name="overlay-log" id="overlay-log"></iframe>
</div>
<button id="theme-toggle" onclick="toggleTheme()">
<span id="theme-icon"></span>

View File

@@ -1,59 +0,0 @@
<html lang="en">
<head>
<link rel="stylesheet" href="style.css">
<style>
body {
padding: 1rem;
}
#floating-box {
position: fixed;
top: 1rem;
right: 1rem;
width: 20rem;
display: flex;
justify-content: end;
align-items: center;
}
#autoloading-box {
display: grid;
gap: 0.5rem;
font-size: large;
border: solid thin gray;
background-color: #f9f9f9;
width: 10rem;
padding: 0.5rem 1rem;
margin: 0 0 0 1rem;
}
.loader {
opacity: 1;
width: 40px;
height: 40px;
align-self: inherit;
}
@starting-style {
.loader {
opacity: 0;
}
}
.loader.hidden {
display: none;
opacity: 0;
transition: opacity 1s, display 1s allow-discrete;
}
</style>
<script src="log-view.js"></script>
</head>
<body data-container-id="{{ id }}">
<div id="floating-box">
<div class="loader"></div>
<div id="autoloading-box">
<div>
Automatic loading of new log data is
<span id="autoloading-status">enabled</span>.
</div>
<button id="autoloading-control">Disable</button>
</div>
</div>
<pre>{{ logContent }}</pre>
</body>
</html>

View File

@@ -7,9 +7,7 @@ The official Nextcloud installation method. Nextcloud AIO provides easy deployme
Included are:
- Nextcloud
- High performance backend for Nextcloud Files (Client Push)
- Redis & APCU for performant caching
- PostgreSQL as database
- High performance backend for Nextcloud Files
- Nextcloud Office (optional)
- High performance backend for Nextcloud Talk and TURN-server (optional)
- Nextcloud Talk Recording-server (optional)
@@ -90,10 +88,9 @@ Included are:
## How to use this?
The steps below are written for Linux. For platform-specific guidance see:
- macOS: [How to run AIO on macOS?](#how-to-run-aio-on-macos)
- Windows: [How to run AIO on Windows?](#how-to-run-aio-on-windows)
- Unraid: [How to run AIO on Unraid?](#how-to-run-aio-on-unraid)
- Synology DSM: [How to run AIO on Synology DSM?](#how-to-run-aio-on-synology-dsm)
- macOS: [How to run AIO on macOS](#how-to-run-aio-on-macos)
- Windows: [How to run AIO on Windows](#how-to-run-aio-on-windows)
- Synology DSM: [How to run AIO on Synology DSM](#how-to-run-aio-on-synology-dsm)
- TrueNAS SCALE: [Can I run AIO on TrueNAS SCALE?](#can-i-run-aio-on-truenas-scale)
> [!IMPORTANT]
@@ -206,7 +203,6 @@ https://your-domain-that-points-to-this-server.tld:8443
- [Are other ports than the default 443 for Nextcloud supported?](#are-other-ports-than-the-default-443-for-nextcloud-supported)
- [Can I run Nextcloud in a subdirectory on my domain?](#can-i-run-nextcloud-in-a-subdirectory-on-my-domain)
- [How can I access Nextcloud locally?](#how-can-i-access-nextcloud-locally)
- [How to overwrite the local DNS resolution for some domains or add extra hosts to the containers?](#how-to-overwrite-the-local-dns-resolution-for-some-domains-or-add-extra-hosts-to-the-containers)
- [How to skip the domain validation?](#how-to-skip-the-domain-validation)
- [How to resolve firewall problems with Fedora Linux, RHEL OS, CentOS, SUSE Linux and others?](#how-to-resolve-firewall-problems-with-fedora-linux-rhel-os-centos-suse-linux-and-others)
- [What can I do to fix the internal or reserved ip-address error?](#what-can-i-do-to-fix-the-internal-or-reserved-ip-address-error)
@@ -220,7 +216,6 @@ https://your-domain-that-points-to-this-server.tld:8443
- [Customization](#customization)
- [How to adjust the internally used docker api version?](#how-to-adjust-the-internally-used-docker-api-version)
- [How to change the default location of Nextcloud's Datadir?](#how-to-change-the-default-location-of-nextclouds-datadir)
- [How to configure custom UID/GID?](#how-to-configure-custom-uidgid)
- [How to store the files/installation on a separate drive?](#how-to-store-the-filesinstallation-on-a-separate-drive)
- [How to limit the resource usage of AIO?](#how-to-limit-the-resource-usage-of-aio)
- [How to allow the Nextcloud container to access directories on the host?](#how-to-allow-the-nextcloud-container-to-access-directories-on-the-host)
@@ -242,7 +237,6 @@ https://your-domain-that-points-to-this-server.tld:8443
- [Guides](#guides)
- [How to run AIO on macOS?](#how-to-run-aio-on-macos)
- [How to run AIO on Windows?](#how-to-run-aio-on-windows)
- [How to run AIO on Unraid?](#how-to-run-aio-on-unraid)
- [How to run AIO on Synology DSM](#how-to-run-aio-on-synology-dsm)
- [How to run AIO with Portainer?](#how-to-run-aio-with-portainer)
- [Can I run AIO on TrueNAS SCALE?](#can-i-run-aio-on-truenas-scale)
@@ -381,12 +375,6 @@ Now that this is out of the way, the recommended way how to access Nextcloud loc
- https://dockerlabs.collabnix.com/intermediate/networking/Configuring_DNS.html
Apart from that there is now a community container that can be added to the AIO stack: https://github.com/nextcloud/all-in-one/tree/main/community-containers/pi-hole
### How to overwrite the local DNS resolution for some domains or add extra hosts to the containers?
For some use cases, you might need to overwrite the local DNS resolution of some domains inside the containers. On Linux, you can do so either by using a local DNS server as described in the section above and add a local DNS entry into the dns server and make your containers use that DNS server.
Another solution on Linux, depending on your network and docker configuration, it might also work to simply edit the `/etc/hosts` file. Add your custom entry like `172.18.0.1 mail.example.com` as additional line to the file and restart docker which should automatically push the entry to all docker containers.
### How to skip the domain validation?
If you are completely sure that you've configured everything correctly and are not able to pass the domain validation, you may skip the domain validation by adding `--env SKIP_DOMAIN_VALIDATION=true` to the docker run command of the mastercontainer (but before the last line `ghcr.io/nextcloud-releases/all-in-one:latest`! If it was started already, you will need to stop the mastercontainer, remove it (no data will be lost) and recreate it using the docker run command that you initially used).
@@ -465,18 +453,6 @@ You can configure the Nextcloud container to use a specific directory on your ho
```
In this example, it would mount `E:\your\data\path` into the volume so for a different location you need to adjust `/host_mnt/e/your/data/path` accordingly.
### How to configure custom UID/GID?
There are two ways to configure custom UIDs or GIDs that will be used inside the installation.
The first and recommended solution is to use Nextcloud's external storage app and use its functionality to add a connection into Nextcloud. See https://docs.nextcloud.com/server/latest/admin_manual/configuration_files/external_storage_configuration_gui.html
Another solution if you really need to use host mounts is to use a bind mount to map custom permissions to the directory. You can do so by editing the `/etc/fstab` file on your Linux server and create an entry like the following to the file:
```
/source/path /target/path/where/the/source/directory/will/be/mounted/on/the/server fuse.bindfs force-user=33,force-group=33,allow_other 0 0
```
You can then use `--env NEXTCLOUD_DATADIR="/target/path/where/the/source/directory/will/be/mounted/on/the/server"` as described in the section above.
### How to store the files/installation on a separate drive?
You can move the whole docker library and all its files including all Nextcloud AIO files and folders to a separate drive by first mounting the drive in the host OS (NTFS is not supported and ext4 is recommended as FS) and then following this tutorial: https://www.guguweb.com/2019/02/07/how-to-move-docker-data-directory-to-another-location-on-ubuntu/<br>
(Of course docker needs to be installed first for this to work.)
@@ -622,23 +598,6 @@ Also, you may be interested in adjusting Nextcloud's Datadir to store the files
> [!NOTE]
> Almost all commands in this project's documentation use `sudo docker ...`. Since `sudo` is not available on Windows, you simply remove `sudo` from the commands and they should work.
### How to run AIO on Unraid?
Generally on Unraid, before installing Nextcloud AIO, you should make sure to specify a directory for all docker related files instead of using the default docker image before continuing the installation process. See this guide for more details: https://forums.unraid.net/topic/103924-how-to-turn-my-docker-file-to-a-docker-folder/#comment-962317
Additionally, you might need to create a custom user script to start all sibling containers of AIO automatically whenever the server reboots (since the docker plugin in Unraid does not respect the normal docker restart-policy that is configured for all AIO containers). See the User scripts docs: https://docs.unraid.net/unraid-os/using-unraid-to/run-docker-containers/managing-and-customizing-containers/#user-scripts-plugin. You would need to use a script like the one below and use the schedule option `@reboot` to start the containers automatically.
```bash
#!/bin/bash
# Wait 120 seconds for other server components to start correctly
sleep 120
# Execute the actual command
docker exec --env START_CONTAINERS=1 nextcloud-aio-mastercontainer /daily-backup.sh`
```
Apart from that, the installation of AIO should work like on "normal" Linux. So refer to the Linux instructions for installation.
### How to run AIO on Synology DSM
On Synology, there are two things different in comparison to Linux: instead of using `--volume /var/run/docker.sock:/var/run/docker.sock:ro`, you need to use `--volume /volume1/docker/docker.sock:/var/run/docker.sock:ro` to run it. You also need to add `--env WATCHTOWER_DOCKER_SOCKET_PATH="/volume1/docker/docker.sock"`to the docker run command of the mastercontainer (but before the last line `ghcr.io/nextcloud-releases/all-in-one:latest`). Additionally, you likely need to adjust the internally used api version. See [this documentation](#how-to-adjust-the-internally-used-docker-api-version). Apart from that it should work and behave the same like on Linux. Obviously the Synology Docker GUI will not work with that so you will need to either use SSH or create a user-defined script task in the task scheduler as the user 'root' in order to run the command.
@@ -895,7 +854,7 @@ Be aware that this solution does not back up files and folders that are mounted
Backed up will get all important data of your Nextcloud AIO instance required to restore the instance, like the database, your files and configuration files of the mastercontainer and else. Files and folders that are mounted into Nextcloud using the external storage app are not getting backed up. There is currently no way to exclude the data directory because it would require hacks like running files:scan and would make the backup solution much more unreliable (since the database and your files/folders need to stay in sync). If you still don't want your datadirectory to be backed up, see https://github.com/nextcloud/all-in-one#how-to-enable-automatic-updates-without-creating-a-backup-beforehand for options (there is a hint what needs to be backed up in which order).
### How to adjust borgs retention policy?
The built-in borg-based backup solution has by default a retention policy of `--keep-within=7d --keep-weekly=4 --keep-monthly=6`. See https://borgbackup.readthedocs.io/en/stable/usage/prune.html for what these values mean. You can adjust the retention policy by providing `--env BORG_RETENTION_POLICY="--keep-within=7d --keep-weekly=4 --keep-monthly=6"` to the docker run command of the mastercontainer (but before the last line `ghcr.io/nextcloud-releases/all-in-one:latest`! If it was started already, you will need to stop the mastercontainer, remove it (no data will be lost) and recreate it using the docker run command that you initially used) and customize the value to your fitting. ⚠️ Please make sure that this value is valid, otherwise backup pruning will bug out! Also, don't include the `-a` or `--glob-archives` option, since AIO already provides it and you can't override it. See https://github.com/nextcloud/all-in-one/pull/7616
The built-in borg-based backup solution has by default a retention policy of `--keep-within=7d --keep-weekly=4 --keep-monthly=6`. See https://borgbackup.readthedocs.io/en/stable/usage/prune.html for what these values mean. You can adjust the retention policy by providing `--env BORG_RETENTION_POLICY="--keep-within=7d --keep-weekly=4 --keep-monthly=6"` to the docker run command of the mastercontainer (but before the last line `ghcr.io/nextcloud-releases/all-in-one:latest`! If it was started already, you will need to stop the mastercontainer, remove it (no data will be lost) and recreate it using the docker run command that you initially used) and customize the value to your fitting. ⚠️ Please make sure that this value is valid, otherwise backup pruning will bug out!
### How to migrate from AIO to AIO?
If you have the borg backup feature enabled, you can copy it over to the new host and restore from the backup. This guide assumes the new installation data dir will be on `/mnt/datadir`, you can adjust the steps if it's elsewhere.