diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md index aca2e718..5d6cc059 100644 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ b/.github/ISSUE_TEMPLATE/Bug_report.md @@ -37,3 +37,5 @@ labels: 0. Needs triage #### Output of `sudo docker ps -a` #### Other valuable info + +#### A picture of a cute animal diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 7fe1067e..f79c4ce2 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -10,8 +10,6 @@ updates: labels: - 3. to review - dependencies - cooldown: - default-days: 7 - package-ecosystem: composer directory: "/php/" schedule: diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 0350cecc..00000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,8 +0,0 @@ - - -* Resolves: # -* [Sign-off message](https://github.com/src-d/guide/blob/master/developer-community/fix-DCO.md) is added to all commits diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 475940a9..2bd4823a 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: Check spelling uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # v2 with: diff --git a/.github/workflows/collabora.yml b/.github/workflows/collabora.yml index a61067f3..8e464925 100644 --- a/.github/workflows/collabora.yml +++ b/.github/workflows/collabora.yml @@ -10,7 +10,7 @@ jobs: name: update collabora runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Run collabora-profile-update run: | rm -f php/cool-seccomp-profile.json @@ -18,7 +18,7 @@ jobs: mv cool-seccomp-profile.json php/ - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: collabora-seccomp-update automated change signoff: true diff --git a/.github/workflows/community-containers.yml b/.github/workflows/community-containers.yml index 5271bfa8..7446677f 100644 --- a/.github/workflows/community-containers.yml +++ b/.github/workflows/community-containers.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: Validate structure run: | CONTAINERS="$(find ./community-containers -mindepth 1 -maxdepth 1 -type d)" diff --git a/.github/workflows/dependency-updates.yml b/.github/workflows/dependency-updates.yml index 3805a0d0..1b448139 100644 --- a/.github/workflows/dependency-updates.yml +++ b/.github/workflows/dependency-updates.yml @@ -10,7 +10,7 @@ jobs: name: Run dependency update script runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2 with: php-version: 8.4 @@ -44,7 +44,7 @@ jobs: )" sed -i "s|pecl install APCu.*\;|pecl install APCu-$apcu_version\;|" ./Containers/mastercontainer/Dockerfile - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: php dependency updates signoff: true diff --git a/.github/workflows/docker-lint.yml b/.github/workflows/docker-lint.yml index 3f09bb98..917df1d6 100644 --- a/.github/workflows/docker-lint.yml +++ b/.github/workflows/docker-lint.yml @@ -25,7 +25,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: Install hadolint run: | diff --git a/.github/workflows/fail-on-prerelease.yml b/.github/workflows/fail-on-prerelease.yml deleted file mode 100644 index a5b876c3..00000000 --- a/.github/workflows/fail-on-prerelease.yml +++ /dev/null @@ -1,50 +0,0 @@ -name: Block if prerelease is present - -on: - pull_request: - -permissions: - contents: read - -jobs: - check-latest-release: - runs-on: ubuntu-latest - steps: - - name: "Check latest published release isn't a prerelease" - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v6 - with: - script: | - const tags = await github.rest.repos.listTags({ - owner: context.repo.owner, - repo: context.repo.repo, - per_page: 1 - }); - - if (!tags.data || tags.data.length === 0) { - core.info('No tags found for this repository; skipping prerelease check.'); - return; - } - - const latestTag = tags.data[0].name; - core.info(`Latest tag found: ${latestTag}`); - - try { - const { data } = await github.rest.repos.getReleaseByTag({ - owner: context.repo.owner, - repo: context.repo.repo, - tag: latestTag - }); - - if (data.prerelease) { - core.setFailed(`Release for tag ${latestTag} (${data.tag_name}) is a prerelease. Blocking merges to main as we need to wait for the prerelease to become stable.`); - } else { - core.info(`Release for tag ${latestTag} (${data.tag_name}) is not a prerelease.`); - } - - } catch (err) { - if (err.status === 404) { - core.info(`No release found for tag ${latestTag}; skipping prerelease check.`); - } else { - throw err; - } - } diff --git a/.github/workflows/helm-release.yml b/.github/workflows/helm-release.yml index ba3b865d..a4f441c2 100644 --- a/.github/workflows/helm-release.yml +++ b/.github/workflows/helm-release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: Turnstyle uses: softprops/turnstyle@e565d2d86403c5d23533937e95980570545e5586 # v2 diff --git a/.github/workflows/imaginary-update.yml b/.github/workflows/imaginary-update.yml index 05050a20..060b376e 100644 --- a/.github/workflows/imaginary-update.yml +++ b/.github/workflows/imaginary-update.yml @@ -10,7 +10,7 @@ jobs: name: update to latest imaginary commit on master branch runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Run imaginary-update run: | # Imaginary @@ -22,7 +22,7 @@ jobs: sed -i "s|^ENV IMAGINARY_HASH.*$|ENV IMAGINARY_HASH=$imaginary_version|" ./Containers/imaginary/Dockerfile - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: imaginary-update automated change signoff: true diff --git a/.github/workflows/json-validator.yml b/.github/workflows/json-validator.yml index 8c0a7f45..4cbd28ed 100644 --- a/.github/workflows/json-validator.yml +++ b/.github/workflows/json-validator.yml @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: Validate Json run: | sudo apt-get update diff --git a/.github/workflows/lint-helm.yml b/.github/workflows/lint-helm.yml index 61e51450..7beec865 100644 --- a/.github/workflows/lint-helm.yml +++ b/.github/workflows/lint-helm.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 with: fetch-depth: 0 diff --git a/.github/workflows/lint-php.yml b/.github/workflows/lint-php.yml index c0d2d577..0c5e2c74 100644 --- a/.github/workflows/lint-php.yml +++ b/.github/workflows/lint-php.yml @@ -36,7 +36,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.1 with: persist-credentials: false diff --git a/.github/workflows/lint-yaml.yml b/.github/workflows/lint-yaml.yml index e36b8f4c..3bb1d33f 100644 --- a/.github/workflows/lint-yaml.yml +++ b/.github/workflows/lint-yaml.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.1 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: persist-credentials: false diff --git a/.github/workflows/nextcloud-update.yml b/.github/workflows/nextcloud-update.yml index b2475290..7fe5bbf9 100644 --- a/.github/workflows/nextcloud-update.yml +++ b/.github/workflows/nextcloud-update.yml @@ -11,7 +11,7 @@ jobs: name: Run nextcloud-update script runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Run nextcloud-update script run: | # Inspired by https://github.com/nextcloud/docker/blob/master/update.sh @@ -79,7 +79,7 @@ jobs: fi - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: nextcloud-update automated change signoff: true diff --git a/.github/workflows/php-deprecation-detector.yml b/.github/workflows/php-deprecation-detector.yml index 38b0fa8d..c8638683 100644 --- a/.github/workflows/php-deprecation-detector.yml +++ b/.github/workflows/php-deprecation-detector.yml @@ -16,7 +16,7 @@ jobs: name: PHP Deprecation Detector runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Set up php uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2 with: diff --git a/.github/workflows/playwright-on-push.yml b/.github/workflows/playwright-on-push.yml index 40277e57..af8dec02 100644 --- a/.github/workflows/playwright-on-push.yml +++ b/.github/workflows/playwright-on-push.yml @@ -24,9 +24,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + - uses: actions/setup-node@v6 with: node-version: lts/* @@ -114,7 +114,7 @@ jobs: exit 1 fi - - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + - uses: actions/upload-artifact@v6 if: ${{ !cancelled() }} with: name: playwright-report diff --git a/.github/workflows/playwright-on-workflow-dispatch.yml b/.github/workflows/playwright-on-workflow-dispatch.yml index 6d2f6d32..252a6510 100644 --- a/.github/workflows/playwright-on-workflow-dispatch.yml +++ b/.github/workflows/playwright-on-workflow-dispatch.yml @@ -13,9 +13,9 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - - uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6 + - uses: actions/setup-node@v6 with: node-version: lts/* @@ -82,7 +82,7 @@ jobs: exit 1 fi - - uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6 + - uses: actions/upload-artifact@v6 if: ${{ !cancelled() }} with: name: playwright-report diff --git a/.github/workflows/psalm-update-baseline.yml b/.github/workflows/psalm-update-baseline.yml index bcbb12c3..1bd47ac4 100644 --- a/.github/workflows/psalm-update-baseline.yml +++ b/.github/workflows/psalm-update-baseline.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Set up php uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2 @@ -30,7 +30,7 @@ jobs: continue-on-error: true - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: token: ${{ secrets.COMMAND_BOT_PAT }} commit-message: Update psalm baseline diff --git a/.github/workflows/psalm.yml b/.github/workflows/psalm.yml index 2bab876e..bdae585e 100644 --- a/.github/workflows/psalm.yml +++ b/.github/workflows/psalm.yml @@ -32,7 +32,7 @@ jobs: name: static-psalm-analysis steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v5.0.1 with: persist-credentials: false diff --git a/.github/workflows/shellcheck.yml b/.github/workflows/shellcheck.yml index b051c355..86954033 100644 --- a/.github/workflows/shellcheck.yml +++ b/.github/workflows/shellcheck.yml @@ -15,7 +15,7 @@ jobs: name: Check Shell runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Run Shellcheck uses: ludeeus/action-shellcheck@00cae500b08a931fb5698e11e79bfbd38e612a38 # v2.0.0 with: diff --git a/.github/workflows/talk.yml b/.github/workflows/talk.yml index b19e1cb5..f28ad9f2 100644 --- a/.github/workflows/talk.yml +++ b/.github/workflows/talk.yml @@ -10,7 +10,7 @@ jobs: name: update talk runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Run talk-container-update run: | # Recording @@ -45,7 +45,7 @@ jobs: sed -i "s|^ARG JANUS_VERSION=.*$|ARG JANUS_VERSION=$janus_version|" ./Containers/talk/Dockerfile - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: talk-update automated change signoff: true diff --git a/.github/workflows/twig-lint.yml b/.github/workflows/twig-lint.yml index 27b8776d..7e9b5cdc 100644 --- a/.github/workflows/twig-lint.yml +++ b/.github/workflows/twig-lint.yml @@ -24,7 +24,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: Set up php ${{ matrix.php-versions }} uses: shivammathur/setup-php@7bf05c6b704e0b9bfee22300130a31b5ea68d593 # v2 diff --git a/.github/workflows/update-copyright.yml b/.github/workflows/update-copyright.yml index 103851c9..f7960ead 100644 --- a/.github/workflows/update-copyright.yml +++ b/.github/workflows/update-copyright.yml @@ -8,4 +8,4 @@ jobs: name: update copyright runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 diff --git a/.github/workflows/update-helm.yml b/.github/workflows/update-helm.yml index 92cbb978..ee8e4669 100644 --- a/.github/workflows/update-helm.yml +++ b/.github/workflows/update-helm.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: update helm chart run: | set -x @@ -23,7 +23,7 @@ jobs: sudo bash nextcloud-aio-helm-chart/update-helm.sh "$DOCKER_TAG" fi - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: Helm Chart updates signoff: true diff --git a/.github/workflows/update-yaml.yml b/.github/workflows/update-yaml.yml index 6e150261..ba92fd50 100644 --- a/.github/workflows/update-yaml.yml +++ b/.github/workflows/update-yaml.yml @@ -11,12 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6.0.1 - name: update yaml files run: | sudo bash manual-install/update-yaml.sh - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: Yaml updates signoff: true diff --git a/.github/workflows/watchtower-update.yml b/.github/workflows/watchtower-update.yml index ecd82a69..be929285 100644 --- a/.github/workflows/watchtower-update.yml +++ b/.github/workflows/watchtower-update.yml @@ -10,7 +10,7 @@ jobs: name: update watchtower runs-on: ubuntu-latest steps: - - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - uses: actions/checkout@v6.0.1 - name: Run watchtower-container-update run: | # Watchtower @@ -26,7 +26,7 @@ jobs: sed -i "s|\$WATCHTOWER_COMMIT_HASH.*$|\$WATCHTOWER_COMMIT_HASH # $watchtower_version|" ./Containers/watchtower/Dockerfile - name: Create Pull Request - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v7 + uses: peter-evans/create-pull-request@22a9089034f40e5a961c8808d113e2c98fb63676 # v7 with: commit-message: watchtower-update automated change signoff: true diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index fec85a59..00000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,13 +0,0 @@ - -In the Nextcloud community, participants from all over the world come together to create Free Software for a free internet. This is made possible by the support, hard work and enthusiasm of thousands of people, including those who create and use Nextcloud software. - -Our code of conduct offers some guidance to ensure Nextcloud participants can cooperate effectively in a positive and inspiring atmosphere, and to explain how together we can strengthen and support each other. - -The Code of Conduct is shared by all contributors and users who engage with the Nextcloud team and its community services. It presents a summary of the shared values and “common sense” thinking in our community. - -You can find our full code of conduct on our website: https://nextcloud.com/code-of-conduct/ - -Please, keep our CoC in mind when you contribute! That way, everyone can be a part of our community in a productive, positive, creative and fun way. diff --git a/Containers/alpine/Dockerfile b/Containers/alpine/Dockerfile index 1098b4c4..718c5510 100644 --- a/Containers/alpine/Dockerfile +++ b/Containers/alpine/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM alpine:3.23.3 +FROM alpine:3.23.2 RUN set -ex; \ apk upgrade --no-cache -a diff --git a/Containers/apache/Dockerfile b/Containers/apache/Dockerfile index 9ccadfb8..0948fb25 100644 --- a/Containers/apache/Dockerfile +++ b/Containers/apache/Dockerfile @@ -88,5 +88,4 @@ CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/borgbackup/Dockerfile b/Containers/borgbackup/Dockerfile index 6e3180cb..637d035c 100644 --- a/Containers/borgbackup/Dockerfile +++ b/Containers/borgbackup/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM alpine:3.23.3 +FROM alpine:3.23.2 RUN set -ex; \ \ @@ -24,6 +24,5 @@ ENTRYPOINT ["/start.sh"] USER root LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" ENV BORG_RETENTION_POLICY="--keep-within=7d --keep-weekly=4 --keep-monthly=6" diff --git a/Containers/clamav/Dockerfile b/Containers/clamav/Dockerfile index 6910ae1c..196b109a 100644 --- a/Containers/clamav/Dockerfile +++ b/Containers/clamav/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM alpine:3.23.3 +FROM alpine:3.23.2 RUN set -ex; \ apk upgrade --no-cache -a; \ @@ -33,6 +33,5 @@ VOLUME /var/lib/clamav ENTRYPOINT ["/start.sh"] CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf"] LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" HEALTHCHECK --start-period=60s --retries=9 CMD /healthcheck.sh diff --git a/Containers/collabora-online/Dockerfile b/Containers/collabora-online/Dockerfile index ec8b63f0..72f79928 100644 --- a/Containers/collabora-online/Dockerfile +++ b/Containers/collabora-online/Dockerfile @@ -12,5 +12,4 @@ USER 1001 HEALTHCHECK --start-period=60s --retries=9 CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/collabora/Dockerfile b/Containers/collabora/Dockerfile index d1693da0..50b6cfef 100644 --- a/Containers/collabora/Dockerfile +++ b/Containers/collabora/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:latest # From a file located probably somewhere here: https://github.com/CollaboraOnline/online/blob/master/docker/from-packages/Dockerfile -FROM collabora/code:25.04.8.2.1 +FROM collabora/code:25.04.8.1.1 USER root ARG DEBIAN_FRONTEND=noninteractive @@ -11,5 +11,4 @@ USER 1001 HEALTHCHECK --start-period=60s --retries=9 CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/docker-socket-proxy/Dockerfile b/Containers/docker-socket-proxy/Dockerfile index ffc867a8..796c855a 100644 --- a/Containers/docker-socket-proxy/Dockerfile +++ b/Containers/docker-socket-proxy/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM haproxy:3.3.2-alpine +FROM haproxy:3.3.1-alpine # hadolint ignore=DL3002 USER root @@ -19,5 +19,4 @@ COPY --chmod=664 haproxy.cfg /haproxy.cfg ENTRYPOINT ["/start.sh"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/domaincheck/Dockerfile b/Containers/domaincheck/Dockerfile index 374aba4a..769c24ac 100644 --- a/Containers/domaincheck/Dockerfile +++ b/Containers/domaincheck/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM alpine:3.23.3 +FROM alpine:3.23.2 RUN set -ex; \ apk upgrade --no-cache -a; \ apk add --no-cache bash lighttpd netcat-openbsd; \ @@ -18,5 +18,4 @@ ENTRYPOINT ["/start.sh"] HEALTHCHECK CMD nc -z 127.0.0.1 $APACHE_PORT || exit 1 LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/fulltextsearch/Dockerfile b/Containers/fulltextsearch/Dockerfile index ff1e923f..7975bcbb 100644 --- a/Containers/fulltextsearch/Dockerfile +++ b/Containers/fulltextsearch/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:latest # Probably from here https://github.com/elastic/elasticsearch/blob/main/distribution/docker/src/docker/Dockerfile -FROM elasticsearch:8.19.10 +FROM elasticsearch:8.19.9 USER root @@ -22,6 +22,5 @@ USER 1000:0 HEALTHCHECK --interval=10s --timeout=5s --start-period=1m --retries=5 CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" ENV ES_JAVA_OPTS="-Xms512M -Xmx512M" diff --git a/Containers/imaginary/Dockerfile b/Containers/imaginary/Dockerfile index b108ac18..11250a43 100644 --- a/Containers/imaginary/Dockerfile +++ b/Containers/imaginary/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM golang:1.25.6-alpine3.23 AS go +FROM golang:1.25.5-alpine3.23 AS go ENV IMAGINARY_HASH=6a274b488759a896aff02f52afee6e50b5e3a3ee @@ -14,7 +14,7 @@ RUN set -ex; \ build-base; \ go install github.com/h2non/imaginary@"$IMAGINARY_HASH"; -FROM alpine:3.23.3 +FROM alpine:3.23.2 RUN set -ex; \ apk upgrade --no-cache -a; \ apk add --no-cache \ @@ -43,5 +43,4 @@ ENTRYPOINT ["/start.sh"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/mastercontainer/Dockerfile b/Containers/mastercontainer/Dockerfile index f3079ca7..c5b91b7d 100644 --- a/Containers/mastercontainer/Dockerfile +++ b/Containers/mastercontainer/Dockerfile @@ -1,12 +1,12 @@ # syntax=docker/dockerfile:latest # Docker CLI is a requirement -FROM docker:29.2.0-cli AS docker +FROM docker:29.1.3-cli AS docker # Caddy is a requirement FROM caddy:2.10.2-alpine AS caddy # From https://github.com/docker-library/php/blob/master/8.4/alpine3.23/fpm/Dockerfile -FROM php:8.4.17-fpm-alpine3.23 +FROM php:8.4.16-fpm-alpine3.23 EXPOSE 80 EXPOSE 8080 @@ -127,7 +127,6 @@ RUN set -ex; \ # hadolint ignore=DL3048 LABEL org.label-schema.vendor="Nextcloud" \ - wud.watch="false" \ com.docker.compose.project="nextcloud-aio" # hadolint ignore=DL3002 diff --git a/Containers/mastercontainer/daily-backup.sh b/Containers/mastercontainer/daily-backup.sh index 89ef3cd5..d11f3e85 100644 --- a/Containers/mastercontainer/daily-backup.sh +++ b/Containers/mastercontainer/daily-backup.sh @@ -4,7 +4,7 @@ echo "Daily backup script has started" # Check if initial configuration has been done, otherwise this script should do nothing. CONFIG_FILE=/mnt/docker-aio-config/data/configuration.json -if ! [ -f "$CONFIG_FILE" ] || (! grep -q "wasStartButtonClicked.*1" "$CONFIG_FILE" && ! grep -q "wasStartButtonClicked.*true" "$CONFIG_FILE"); then +if ! [ -f "$CONFIG_FILE" ] || ! grep -q "wasStartButtonClicked.*1" "$CONFIG_FILE"; then echo "Initial configuration via AIO interface not done yet. Exiting..." exit 0 fi diff --git a/Containers/mastercontainer/start.sh b/Containers/mastercontainer/start.sh index a65e29ae..ad1734f1 100644 --- a/Containers/mastercontainer/start.sh +++ b/Containers/mastercontainer/start.sh @@ -166,7 +166,7 @@ elif ! sudo -E -u www-data docker volume ls --format "{{.Name}}" | grep -q "^nex 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 --format '{{.Mounts}}' | grep -q " nextcloud_aio_mastercontainer "; then +elif ! sudo -E -u www-data docker inspect nextcloud-aio-mastercontainer | 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 diff --git a/Containers/nextcloud/Dockerfile b/Containers/nextcloud/Dockerfile index c6d9bf7e..6b6be7f8 100644 --- a/Containers/nextcloud/Dockerfile +++ b/Containers/nextcloud/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM php:8.3.30-fpm-alpine3.23 +FROM php:8.3.29-fpm-alpine3.23 ENV PHP_MEMORY_LIMIT=512M ENV PHP_UPLOAD_LIMIT=16G @@ -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.5 +ENV NEXTCLOUD_VERSION=32.0.3 ENV AIO_TOKEN=123456 ENV AIO_URL=localhost # AIO settings end # Do not remove or change this line! @@ -264,5 +264,4 @@ CMD ["/usr/bin/supervisord", "-c", "/supervisord.conf"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/nextcloud/config/postgres.config.php b/Containers/nextcloud/config/postgres.config.php index 0dc835cc..71a657a7 100644 --- a/Containers/nextcloud/config/postgres.config.php +++ b/Containers/nextcloud/config/postgres.config.php @@ -10,7 +10,7 @@ if (getenv('NEXTCLOUD_TRUSTED_CERTIFICATES_POSTGRES')) { if (getenv('NEXTCLOUD_TRUSTED_CERTIFICATES_MYSQL')) { $CONFIG = array( 'dbdriveroptions' => array( - PDO::MYSQL_ATTR_SSL_CA => '/var/www/html/data/certificates/ca-bundle.crt', + 'PDO::MYSQL_ATTR_SSL_CA' => '/var/www/html/data/certificates/ca-bundle.crt', ), ); } diff --git a/Containers/nextcloud/entrypoint.sh b/Containers/nextcloud/entrypoint.sh index d4b4f253..6825f04c 100644 --- a/Containers/nextcloud/entrypoint.sh +++ b/Containers/nextcloud/entrypoint.sh @@ -65,6 +65,14 @@ if env | grep -q NEXTCLOUD_TRUSTED_CERTIFICATES_; then done + # Custom logic for ldap conf + if ! grep -q "TLS_" /etc/openldap/ldap.conf; then + cat << EOL >> /etc/openldap/ldap.conf +TLS_CACERT $CERTIFICATE_BUNDLE +TLS_REQCERT try +EOL + fi + # Backwards compatibility with older instances if [ -f "/var/www/html/config/postgres.config.php" ]; then sed -i "s|/var/www/html/data/certificates/POSTGRES|/var/www/html/data/certificates/ca-bundle.crt|" /var/www/html/config/postgres.config.php @@ -182,11 +190,8 @@ if ! [ -f "$NEXTCLOUD_DATA_DIR/skip.update" ]; then curl -fsSL -o nextcloud.tar.bz2.asc "https://download.nextcloud.com/server/releases/latest-${NEXT_MAJOR}.tar.bz2.asc" GNUPGHOME="$(mktemp -d)" export GNUPGHOME - if ! gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 28806A878AE423A28372792ED75899B9A724937A; then - if ! gpg --batch --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 28806A878AE423A28372792ED75899B9A724937A; then - curl -sSL https://nextcloud.com/nextcloud.asc | gpg --import - fi - fi + # gpg key from https://nextcloud.com/nextcloud.asc + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 28806A878AE423A28372792ED75899B9A724937A gpg --batch --verify nextcloud.tar.bz2.asc nextcloud.tar.bz2 mkdir -p /usr/src/tmp tar -xjf nextcloud.tar.bz2 -C /usr/src/tmp/ @@ -897,9 +902,7 @@ if [ -d "/var/www/html/custom_apps/spreed" ]; then RECORDING_SERVERS_STRING="{\"servers\":[{\"server\":\"http://$TALK_RECORDING_HOST:1234/\",\"verify\":true}],\"secret\":\"$RECORDING_SECRET\"}" php /var/www/html/occ config:app:set spreed recording_servers --value="$RECORDING_SERVERS_STRING" else - if [ "$REMOVE_DISABLED_APPS" = yes ]; then - php /var/www/html/occ config:app:delete spreed recording_servers - fi + php /var/www/html/occ config:app:delete spreed recording_servers fi fi diff --git a/Containers/notify-push/Dockerfile b/Containers/notify-push/Dockerfile index 838c847c..029c93f2 100644 --- a/Containers/notify-push/Dockerfile +++ b/Containers/notify-push/Dockerfile @@ -1,5 +1,5 @@ # syntax=docker/dockerfile:latest -FROM alpine:3.23.3 +FROM alpine:3.23.2 COPY --chmod=775 start.sh /start.sh COPY --chmod=775 healthcheck.sh /healthcheck.sh @@ -23,5 +23,4 @@ ENTRYPOINT ["/start.sh"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/onlyoffice/Dockerfile b/Containers/onlyoffice/Dockerfile index 13b4d456..d028ccbc 100644 --- a/Containers/onlyoffice/Dockerfile +++ b/Containers/onlyoffice/Dockerfile @@ -8,5 +8,4 @@ COPY --chmod=775 healthcheck.sh /healthcheck.sh HEALTHCHECK --start-period=60s --retries=9 CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/postgresql/Dockerfile b/Containers/postgresql/Dockerfile index 56090f26..725b8042 100644 --- a/Containers/postgresql/Dockerfile +++ b/Containers/postgresql/Dockerfile @@ -44,5 +44,4 @@ ENTRYPOINT ["/start.sh"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/redis/Dockerfile b/Containers/redis/Dockerfile index cc9181ad..7cc1ff84 100644 --- a/Containers/redis/Dockerfile +++ b/Containers/redis/Dockerfile @@ -21,5 +21,4 @@ ENTRYPOINT ["/start.sh"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/talk-recording/Dockerfile b/Containers/talk-recording/Dockerfile index 8df5b89e..65af7db4 100644 --- a/Containers/talk-recording/Dockerfile +++ b/Containers/talk-recording/Dockerfile @@ -58,5 +58,4 @@ CMD ["python", "-m", "nextcloud.talk.recording", "--config", "/conf/recording.co HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/talk/Dockerfile b/Containers/talk/Dockerfile index e8d3d72f..fc5f0379 100644 --- a/Containers/talk/Dockerfile +++ b/Containers/talk/Dockerfile @@ -1,8 +1,8 @@ # syntax=docker/dockerfile:latest -FROM nats:2.12.4-scratch AS nats +FROM nats:2.12.3-scratch AS nats FROM eturnal/eturnal:1.12.2-alpine AS eturnal FROM strukturag/nextcloud-spreed-signaling:2.0.4 AS signaling -FROM alpine:3.23.3 AS janus +FROM alpine:3.23.2 AS janus ARG JANUS_VERSION=v1.3.3 WORKDIR /src @@ -35,7 +35,7 @@ RUN set -ex; \ make configs; \ rename -v ".jcfg.sample" ".jcfg" /usr/local/etc/janus/*.jcfg.sample -FROM alpine:3.23.3 +FROM alpine:3.23.2 ENV ETURNAL_ETC_DIR="/conf" ENV SKIP_CERT_VERIFY=false COPY --from=janus --chmod=777 --chown=1000:1000 /usr/local /usr/local @@ -107,5 +107,4 @@ CMD ["supervisord", "-c", "/supervisord.conf"] HEALTHCHECK CMD /healthcheck.sh LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/watchtower/Dockerfile b/Containers/watchtower/Dockerfile index fc9ea093..cd5238ac 100644 --- a/Containers/watchtower/Dockerfile +++ b/Containers/watchtower/Dockerfile @@ -1,15 +1,15 @@ # syntax=docker/dockerfile:latest -FROM golang:1.25.6-alpine3.23 AS go +FROM golang:1.25.5-alpine3.23 AS go -ENV WATCHTOWER_COMMIT_HASH=f522ce27e1fbe4618da54833025a95be62aa838a +ENV WATCHTOWER_COMMIT_HASH=f6a7b29c312bec5f389a4fb52259919f0678800b 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.0 + go install github.com/nicholas-fedor/watchtower@$WATCHTOWER_COMMIT_HASH # v1.13.1 -FROM alpine:3.23.3 +FROM alpine:3.23.2 RUN set -ex; \ apk upgrade --no-cache -a; \ @@ -24,5 +24,4 @@ USER root ENTRYPOINT ["/start.sh"] LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/Containers/whiteboard/Dockerfile b/Containers/whiteboard/Dockerfile index c83dd46b..f6b8cc7d 100644 --- a/Containers/whiteboard/Dockerfile +++ b/Containers/whiteboard/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:latest # Probably from this file: https://github.com/nextcloud/whiteboard/blob/main/Dockerfile -FROM ghcr.io/nextcloud-releases/whiteboard:v1.5.4 +FROM ghcr.io/nextcloud-releases/whiteboard:v1.5.0 USER root RUN set -ex; \ @@ -23,5 +23,4 @@ WORKDIR /tmp ENTRYPOINT ["/start.sh"] LABEL com.centurylinklabs.watchtower.enable="false" \ - wud.watch="false" \ org.label-schema.vendor="Nextcloud" diff --git a/community-containers/caddy/readme.md b/community-containers/caddy/readme.md index fd2f30ef..803bbec2 100644 --- a/community-containers/caddy/readme.md +++ b/community-containers/caddy/readme.md @@ -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 [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. +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. ### 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! @@ -14,7 +14,6 @@ This container bundles caddy and auto-configures it for you. It also covers [vau - 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 [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! - You can add your own Caddy configurations in `/data/caddy-imports/` inside the Caddy container (`sudo docker exec -it nextcloud-aio-caddy bash`). These will be imported on container startup. **Please note:** If you do not have CLI access to the server, you can now run docker commands via a web session by using this community container: https://github.com/nextcloud/all-in-one/tree/main/community-containers/container-management - See https://github.com/nextcloud/all-in-one/tree/main/community-containers#community-containers how to add it to the AIO stack diff --git a/community-containers/local-ai/local-ai.json b/community-containers/local-ai/local-ai.json index e906b5a7..8e2aedb3 100644 --- a/community-containers/local-ai/local-ai.json +++ b/community-containers/local-ai/local-ai.json @@ -4,59 +4,42 @@ "container_name": "nextcloud-aio-local-ai", "display_name": "Local AI", "documentation": "https://github.com/nextcloud/all-in-one/tree/main/community-containers/local-ai", - "image": "ghcr.io/docjyj/aio-local-ai-vulkan", - "image_tag": "v1", - "internal_port": "10078", + "image": "ghcr.io/szaimen/aio-local-ai", + "image_tag": "v2", + "internal_port": "8080", "restart": "unless-stopped", "environment": [ "TZ=%TIMEZONE%", - "LOCALAI_API_KEY=%LOCALAI_API_KEY%", - "LOCALAI_ADDRESS=:10078", - "LOCALAI_CONFIG_DIR=/configuration", - "LOCALAI_MODEL_PATH=/models", - "LOCALAI_BACKEND_PATH=/backends" - ], - "ports": [ - { - "ip_binding": "%APACHE_IP_BINDING%", - "port_number": "10078", - "protocol": "tcp" - } + "MODELS_PATH=/models" ], "volumes": [ - { - "source": "nextcloud_aio_localai_configuration", - "destination": "/configuration", - "writeable": true - }, { "source": "nextcloud_aio_localai_models", "destination": "/models", "writeable": true }, { - "source": "nextcloud_aio_localai_backends", - "destination": "/backends", + "source": "nextcloud_aio_localai_images", + "destination": "/tmp/generated/images/", "writeable": true + }, + { + "source": "%NEXTCLOUD_DATADIR%", + "destination": "/nextcloud", + "writeable": false } ], - "secrets": [ - "LOCALAI_API_KEY" - ], - "ui_secret": "LOCALAI_API_KEY", - "devices": [ - "/dev/dri" - ], + "enable_nvidia_gpu": false, "nextcloud_exec_commands": [ + "mkdir '/mnt/ncdata/admin/files/nextcloud-aio-local-ai'", + "touch '/mnt/ncdata/admin/files/nextcloud-aio-local-ai/models.yaml'", + "echo 'Scanning nextcloud-aio-local-ai folder for admin user...'", + "php /var/www/html/occ files:scan --path='/admin/files/nextcloud-aio-local-ai'", "php /var/www/html/occ app:install integration_openai", "php /var/www/html/occ app:enable integration_openai", - "php /var/www/html/occ config:app:set integration_openai url --value http://nextcloud-aio-local-ai:10078", - "php /var/www/html/occ config:app:set integration_openai api_key --value %LOCALAI_API_KEY%", + "php /var/www/html/occ config:app:set integration_openai url --value http://nextcloud-aio-local-ai:8080", "php /var/www/html/occ app:install assistant", "php /var/www/html/occ app:enable assistant" - ], - "backup_volumes": [ - "nextcloud_aio_localai_configuration" ] } ] diff --git a/community-containers/local-ai/readme.md b/community-containers/local-ai/readme.md index 02722bd0..2ab05996 100644 --- a/community-containers/local-ai/readme.md +++ b/community-containers/local-ai/readme.md @@ -1,16 +1,21 @@ ## Local AI -This container bundles Local AI and auto-configures it for you. It support hardware acceleration with Vulkan. +This container bundles Local AI and auto-configures it for you. ### Notes -Documentation is available on the container repository. This documentation is regularly updated and is intended to be as simple and detailed as possible. Thanks for all your feedback! - -- See https://github.com/docjyJ/aio-local-ai-vulkan#getting-started for getting start with this container. +- Make sure to have enough storage space available. This container alone needs ~7GB storage. Every model that you add to `models.yaml` will of course use additional space which adds up quite fast. +- After the container was started the first time, you should see a new `nextcloud-aio-local-ai` folder when you open the files app with the default `admin` user. In there you should see a `models.yaml` config file. You can now add models in there. Please refer [here](https://github.com/mudler/LocalAI/blob/master/gallery/index.yaml) where you can get further urls that you can put in there. Afterwards restart all containers from the AIO interface and the models should automatically get downloaded by the local-ai container and activated. +- Example for content of `models.yaml` (if you add all of them, it takes around 10GB additional space): +```yaml +# Stable Diffusion in NCNN with c++, supported txt2img and img2img +- url: github:mudler/LocalAI/blob/master/gallery/stablediffusion.yaml + name: Stable_diffusion +``` +- To make it work, you first need to browse `https://your-nc-domain.com/settings/admin/ai` and enable or disable specific features for your models in the openAI settings. Afterwards using the Nextcloud Assistant should work. - See [this guide](https://github.com/nextcloud/all-in-one/discussions/5430) for how to improve AI task pickup speed -- Note that Nextcloud supports only one server for AI queries, so this container cannot be used at the same time as other AI containers. - See 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/docjyJ/aio-local-ai-vulkan +https://github.com/szaimen/aio-local-ai ### Maintainer -https://github.com/docjyJ +https://github.com/szaimen diff --git a/community-containers/nocodb/nocodb.json b/community-containers/nocodb/nocodb.json index e93d173c..7ef4cc5c 100644 --- a/community-containers/nocodb/nocodb.json +++ b/community-containers/nocodb/nocodb.json @@ -2,7 +2,7 @@ "aio_services_v1": [ { "container_name": "nextcloud-aio-nocodb", - "display_name": "NocoDB (deprecated)", + "display_name": "NocoDB", "documentation": "https://github.com/nextcloud/all-in-one/tree/main/community-containers/nocodb", "image": "nocodb/nocodb", "image_tag": "latest", diff --git a/community-containers/nocodb/readme.md b/community-containers/nocodb/readme.md index fa23f8f6..4c1281b5 100644 --- a/community-containers/nocodb/readme.md +++ b/community-containers/nocodb/readme.md @@ -1,8 +1,3 @@ -> [!CAUTION] -> NocoDB is licensed under a non-free license. -> -> And is no longer maintained. - > [!NOTE] > This container is there to compensate for the lack of functionality in Nextcloud Tables. > diff --git a/local-instance.md b/local-instance.md index 8abbddb6..1da26280 100644 --- a/local-instance.md +++ b/local-instance.md @@ -22,11 +22,10 @@ The normal way is the following: **Hint:** You may have a look at [this video](https://youtu.be/zk-y2wVkY4c) for a more complete but possibly outdated example. ## 3. Use the ACME DNS-challenge -You can alternatively use the ACME DNS-challenge to get a valid certificate for Nextcloud. Here is described how to set it up using an external caddy reverse proxy: https://github.com/nextcloud/all-in-one#how-to-get-nextcloud-running-using-the-acme-dns-challenge +You can alternatively use the ACME DNS-challenge to get a valid certificate for Nextcloud. Here is described how to set it up: https://github.com/nextcloud/all-in-one#how-to-get-nextcloud-running-using-the-acme-dns-challenge ## 4. Use Cloudflare If you do not have any control over the network, you may think about using Cloudflare Tunnel to get a valid certificate for your Nextcloud. However it will be opened to the public internet then. See https://github.com/nextcloud/all-in-one#how-to-run-nextcloud-behind-a-cloudflare-tunnel how to set this up. ## 5. Buy a certificate and use that If none of the above ways work for you, you may simply buy a certificate from an issuer for your domain. You then download the certificate onto your server, configure AIO in [reverse proxy mode](./reverse-proxy.md) and use the certificate for your domain in your reverse proxy config. - diff --git a/manual-install/update-yaml.sh b/manual-install/update-yaml.sh index 928275da..af746aee 100644 --- a/manual-install/update-yaml.sh +++ b/manual-install/update-yaml.sh @@ -47,7 +47,6 @@ sed -i '/AIO_URL/d' containers.yml sed -i '/DOCKER_SOCKET_PROXY_ENABLED/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 TCP="$(grep -oP '[%A-Z0-9_]+/tcp' containers.yml | sort -u)" mapfile -t TCP <<< "$TCP" diff --git a/nextcloud-aio-helm-chart/Chart.yaml b/nextcloud-aio-helm-chart/Chart.yaml index 6288a381..7d990549 100755 --- a/nextcloud-aio-helm-chart/Chart.yaml +++ b/nextcloud-aio-helm-chart/Chart.yaml @@ -1,6 +1,6 @@ name: nextcloud-aio-helm-chart description: A generated Helm Chart for Nextcloud AIO from Skippbox Kompose -version: 12.5.0 +version: 12.4.0 apiVersion: v2 keywords: - latest diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml index e540791c..6cdf8db8 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-apache name: nextcloud-aio-apache @@ -17,7 +17,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-apache spec: @@ -61,7 +61,7 @@ spec: value: "{{ .Values.TIMEZONE }}" - name: WHITEBOARD_HOST value: nextcloud-aio-whiteboard - image: ghcr.io/nextcloud-releases/aio-apache:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-apache:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-service.yaml index 98e33a4d..404ee626 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-apache-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-apache name: nextcloud-aio-apache diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml index 57ec7739..d7627802 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-clamav name: nextcloud-aio-clamav @@ -18,7 +18,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-clamav spec: @@ -36,7 +36,7 @@ spec: {{- end }} initContainers: - name: init-subpath - image: ghcr.io/nextcloud-releases/aio-alpine:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729 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:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-clamav:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-service.yaml index 8b236093..8dc8597d 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-clamav-service.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-clamav name: nextcloud-aio-clamav diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml index cd4e1368..7e86c402 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-collabora name: nextcloud-aio-collabora @@ -16,7 +16,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-collabora spec: @@ -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:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-collabora-online:20260114_114729 {{- else }} - image: ghcr.io/nextcloud-releases/aio-collabora:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-collabora:20260114_114729 {{- end }} readinessProbe: exec: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-service.yaml index 5c81ef3e..ebe7bf3f 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-collabora-service.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-collabora name: nextcloud-aio-collabora diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml index be6a9c90..055ecd0a 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-database name: nextcloud-aio-database @@ -17,7 +17,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-database spec: @@ -35,7 +35,7 @@ spec: {{- end }} initContainers: - name: init-subpath - image: ghcr.io/nextcloud-releases/aio-alpine:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729 command: - mkdir - "-p" @@ -64,7 +64,7 @@ spec: value: nextcloud - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-postgresql:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-postgresql:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-service.yaml index 45fdce3a..9451d908 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-database-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-database name: nextcloud-aio-database diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml index bed60a0c..df30e6a8 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-fulltextsearch name: nextcloud-aio-fulltextsearch @@ -18,13 +18,13 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-fulltextsearch spec: initContainers: - name: init-volumes - image: ghcr.io/nextcloud-releases/aio-alpine:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729 command: - chmod - "777" @@ -54,7 +54,7 @@ spec: value: basic - name: xpack.security.enabled value: "false" - image: ghcr.io/nextcloud-releases/aio-fulltextsearch:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-fulltextsearch:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-service.yaml index efe474b3..ae759475 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-fulltextsearch-service.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-fulltextsearch name: nextcloud-aio-fulltextsearch diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml index af15d4b3..d2fc1375 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-imaginary name: nextcloud-aio-imaginary @@ -16,7 +16,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-imaginary spec: @@ -38,7 +38,7 @@ spec: value: "{{ .Values.IMAGINARY_SECRET }}" - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-imaginary:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-imaginary:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-service.yaml index 44a57006..a5fb3266 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-imaginary-service.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-imaginary name: nextcloud-aio-imaginary diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml index 8b6e8211..fe72d307 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-nextcloud name: nextcloud-aio-nextcloud @@ -17,7 +17,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-nextcloud spec: @@ -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:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729 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:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-nextcloud:20260114_114729 {{- if eq (.Values.RPSS_ENABLED | default "no") "yes" }} # AIO-config - do not change this comment! securityContext: # The items below only work in container context diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-service.yaml index 08ab70f2..18cf84d8 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-nextcloud-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-nextcloud name: nextcloud-aio-nextcloud diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml index c8e30d05..5b05336e 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-notify-push name: nextcloud-aio-notify-push @@ -17,7 +17,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-notify-push spec: @@ -57,7 +57,7 @@ spec: value: "6379" - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-notify-push:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-notify-push:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-service.yaml index 986d98d4..2b7bfccd 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-notify-push-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-notify-push name: nextcloud-aio-notify-push diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml index 2bb79f19..0e3a7fda 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-onlyoffice name: nextcloud-aio-onlyoffice @@ -18,13 +18,13 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-onlyoffice spec: initContainers: - name: init-volumes - image: ghcr.io/nextcloud-releases/aio-alpine:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-alpine:20260114_114729 command: - chmod - "777" @@ -42,7 +42,7 @@ spec: value: "{{ .Values.ONLYOFFICE_SECRET }}" - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-onlyoffice:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-onlyoffice:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-service.yaml index 5fc10b85..6ff9afa1 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-onlyoffice-service.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-onlyoffice name: nextcloud-aio-onlyoffice diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml index 28335e64..1ccebd79 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-deployment.yaml @@ -2,7 +2,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-redis name: nextcloud-aio-redis @@ -17,7 +17,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-redis spec: @@ -39,7 +39,7 @@ spec: value: "{{ .Values.REDIS_PASSWORD }}" - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-redis:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-redis:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-service.yaml index a6a9a0a5..af82a0bb 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-redis-service.yaml @@ -2,7 +2,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-redis name: nextcloud-aio-redis diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml index 679dd66e..8635a6ce 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-talk name: nextcloud-aio-talk @@ -16,7 +16,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-talk spec: @@ -52,7 +52,7 @@ spec: value: "{{ .Values.TURN_SECRET }}" - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-talk:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-talk:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml index 8e631656..2cfcaa53 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-talk-recording name: nextcloud-aio-talk-recording @@ -18,7 +18,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-talk-recording spec: @@ -44,7 +44,7 @@ spec: value: "{{ .Values.RECORDING_SECRET }}" - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-talk-recording:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-talk-recording:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-service.yaml index 87fe0355..4410ed72 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-recording-service.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-talk-recording name: nextcloud-aio-talk-recording diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-service.yaml index 65388792..10d17177 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-talk-service.yaml @@ -4,7 +4,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-talk name: nextcloud-aio-talk-public @@ -27,7 +27,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-talk name: nextcloud-aio-talk diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml index 5788cfa0..50dfc3c4 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-deployment.yaml @@ -3,7 +3,7 @@ apiVersion: apps/v1 kind: Deployment metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-whiteboard name: nextcloud-aio-whiteboard @@ -16,7 +16,7 @@ spec: template: metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-whiteboard spec: @@ -50,7 +50,7 @@ spec: value: redis - name: TZ value: "{{ .Values.TIMEZONE }}" - image: ghcr.io/nextcloud-releases/aio-whiteboard:20260122_105751 + image: ghcr.io/nextcloud-releases/aio-whiteboard:20260114_114729 readinessProbe: exec: command: diff --git a/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-service.yaml b/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-service.yaml index 299f1ec3..8c8cb5aa 100755 --- a/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-service.yaml +++ b/nextcloud-aio-helm-chart/templates/nextcloud-aio-whiteboard-service.yaml @@ -3,7 +3,7 @@ apiVersion: v1 kind: Service metadata: annotations: - kompose.version: 1.38.0 (a8f5d1cbd) + kompose.version: 1.37.0 (fb0539e64) labels: io.kompose.service: nextcloud-aio-whiteboard name: nextcloud-aio-whiteboard diff --git a/nextcloud-aio-helm-chart/update-helm.sh b/nextcloud-aio-helm-chart/update-helm.sh index 9e5aba86..f39d3035 100755 --- a/nextcloud-aio-helm-chart/update-helm.sh +++ b/nextcloud-aio-helm-chart/update-helm.sh @@ -407,7 +407,7 @@ rm latest.yml mv latest.yml.backup latest.yml # Get version of AIO -AIO_VERSION="$(grep 'Nextcloud AIO ' ../php/templates/includes/aio-version.twig | grep -oP '[0-9]+.[0-9]+.[0-9]+')" +AIO_VERSION="$(grep 'Nextcloud AIO ' ../php/templates/containers.twig | grep -oP '[0-9]+.[0-9]+.[0-9]+')" sed -i "s|^version:.*|version: $AIO_VERSION|" ../helm-chart/Chart.yaml # Conversion of sample.conf diff --git a/php/composer.lock b/php/composer.lock index 77403624..ed6667ed 100644 --- a/php/composer.lock +++ b/php/composer.lock @@ -391,16 +391,16 @@ }, { "name": "laravel/serializable-closure", - "version": "v2.0.8", + "version": "v2.0.7", "source": { "type": "git", "url": "https://github.com/laravel/serializable-closure.git", - "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b" + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/7581a4407012f5f53365e11bafc520fd7f36bc9b", - "reference": "7581a4407012f5f53365e11bafc520fd7f36bc9b", + "url": "https://api.github.com/repos/laravel/serializable-closure/zipball/cb291e4c998ac50637c7eeb58189c14f5de5b9dd", + "reference": "cb291e4c998ac50637c7eeb58189c14f5de5b9dd", "shasum": "" }, "require": { @@ -448,7 +448,7 @@ "issues": "https://github.com/laravel/serializable-closure/issues", "source": "https://github.com/laravel/serializable-closure" }, - "time": "2026-01-08T16:22:46+00:00" + "time": "2025-11-21T20:52:36+00:00" }, { "name": "nikic/fast-route", @@ -1644,16 +1644,16 @@ }, { "name": "twig/twig", - "version": "v3.23.0", + "version": "v3.22.2", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9" + "reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", - "reference": "a64dc5d2cc7d6cafb9347f6cd802d0d06d0351c9", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/946ddeafa3c9f4ce279d1f34051af041db0e16f2", + "reference": "946ddeafa3c9f4ce279d1f34051af041db0e16f2", "shasum": "" }, "require": { @@ -1707,7 +1707,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.23.0" + "source": "https://github.com/twigphp/Twig/tree/v3.22.2" }, "funding": [ { @@ -1719,7 +1719,7 @@ "type": "tidelift" } ], - "time": "2026-01-23T21:00:41+00:00" + "time": "2025-12-14T11:28:47+00:00" } ], "packages-dev": [ @@ -2755,22 +2755,22 @@ }, { "name": "danog/advanced-json-rpc", - "version": "v3.2.3", + "version": "v3.2.2", "source": { "type": "git", "url": "https://github.com/danog/php-advanced-json-rpc.git", - "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91" + "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/ae703ea7b4811797a10590b6078de05b3b33dd91", - "reference": "ae703ea7b4811797a10590b6078de05b3b33dd91", + "url": "https://api.github.com/repos/danog/php-advanced-json-rpc/zipball/aadb1c4068a88c3d0530cfe324b067920661efcb", + "reference": "aadb1c4068a88c3d0530cfe324b067920661efcb", "shasum": "" }, "require": { "netresearch/jsonmapper": "^5", "php": ">=8.1", - "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0 || ^6" + "phpdocumentor/reflection-docblock": "^4.3.4 || ^5.0.0" }, "replace": { "felixfbecker/php-advanced-json-rpc": "^3" @@ -2801,9 +2801,9 @@ "description": "A more advanced JSONRPC implementation", "support": { "issues": "https://github.com/danog/php-advanced-json-rpc/issues", - "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.3" + "source": "https://github.com/danog/php-advanced-json-rpc/tree/v3.2.2" }, - "time": "2026-01-12T21:07:10+00:00" + "time": "2025-02-14T10:55:15+00:00" }, { "name": "daverandom/libdns", @@ -3111,20 +3111,20 @@ }, { "name": "league/uri", - "version": "7.8.0", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri.git", - "reference": "4436c6ec8d458e4244448b069cc572d088230b76" + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri/zipball/4436c6ec8d458e4244448b069cc572d088230b76", - "reference": "4436c6ec8d458e4244448b069cc572d088230b76", + "url": "https://api.github.com/repos/thephpleague/uri/zipball/8d587cddee53490f9b82bf203d3a9aa7ea4f9807", + "reference": "8d587cddee53490f9b82bf203d3a9aa7ea4f9807", "shasum": "" }, "require": { - "league/uri-interfaces": "^7.8", + "league/uri-interfaces": "^7.7", "php": "^8.1", "psr/http-factory": "^1" }, @@ -3138,11 +3138,11 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "ext-uri": "to use the PHP native URI class", - "jeremykendall/php-domain-parser": "to further parse the URI host and resolve its Public Suffix and Top Level Domain", - "league/uri-components": "to provide additional tools to manipulate URI objects components", - "league/uri-polyfill": "to backport the PHP URI extension for older versions of PHP", + "jeremykendall/php-domain-parser": "to resolve Public Suffix and Top Level Domain", + "league/uri-components": "Needed to easily manipulate URI objects components", + "league/uri-polyfill": "Needed to backport the PHP URI extension for older versions of PHP", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3197,7 +3197,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri/tree/7.8.0" + "source": "https://github.com/thephpleague/uri/tree/7.7.0" }, "funding": [ { @@ -3205,20 +3205,20 @@ "type": "github" } ], - "time": "2026-01-14T17:24:56+00:00" + "time": "2025-12-07T16:02:06+00:00" }, { "name": "league/uri-interfaces", - "version": "7.8.0", + "version": "7.7.0", "source": { "type": "git", "url": "https://github.com/thephpleague/uri-interfaces.git", - "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4" + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/c5c5cd056110fc8afaba29fa6b72a43ced42acd4", - "reference": "c5c5cd056110fc8afaba29fa6b72a43ced42acd4", + "url": "https://api.github.com/repos/thephpleague/uri-interfaces/zipball/62ccc1a0435e1c54e10ee6022df28d6c04c2946c", + "reference": "62ccc1a0435e1c54e10ee6022df28d6c04c2946c", "shasum": "" }, "require": { @@ -3231,7 +3231,7 @@ "ext-gmp": "to improve IPV4 host parsing", "ext-intl": "to handle IDN host with the best performance", "php-64bit": "to improve IPV4 host parsing", - "rowbot/url": "to handle URLs using the WHATWG URL Living Standard specification", + "rowbot/url": "to handle WHATWG URL", "symfony/polyfill-intl-idn": "to handle IDN host via the Symfony polyfill if ext-intl is not present" }, "type": "library", @@ -3281,7 +3281,7 @@ "docs": "https://uri.thephpleague.com", "forum": "https://thephpleague.slack.com", "issues": "https://github.com/thephpleague/uri-src/issues", - "source": "https://github.com/thephpleague/uri-interfaces/tree/7.8.0" + "source": "https://github.com/thephpleague/uri-interfaces/tree/7.7.0" }, "funding": [ { @@ -3289,7 +3289,7 @@ "type": "github" } ], - "time": "2026-01-15T06:54:53+00:00" + "time": "2025-12-07T16:03:21+00:00" }, { "name": "netresearch/jsonmapper", @@ -3455,16 +3455,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "6.0.1", + "version": "5.6.6", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "2f5cbed597cb261d1ea458f3da3a9ad32e670b1e" + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/2f5cbed597cb261d1ea458f3da3a9ad32e670b1e", - "reference": "2f5cbed597cb261d1ea458f3da3a9ad32e670b1e", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/5cee1d3dfc2d2aa6599834520911d246f656bcb8", + "reference": "5cee1d3dfc2d2aa6599834520911d246f656bcb8", "shasum": "" }, "require": { @@ -3472,8 +3472,8 @@ "ext-filter": "*", "php": "^7.4 || ^8.0", "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^2.0", - "phpstan/phpdoc-parser": "^2.0", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", "webmozart/assert": "^1.9.1 || ^2" }, "require-dev": { @@ -3483,8 +3483,7 @@ "phpstan/phpstan-mockery": "^1.1", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26", - "shipmonk/dead-code-detector": "^0.5.1" + "psalm/phar": "^5.26" }, "type": "library", "extra": { @@ -3514,44 +3513,44 @@ "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", "support": { "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/6.0.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.6" }, - "time": "2026-01-20T15:30:42+00:00" + "time": "2025-12-22T21:13:58+00:00" }, { "name": "phpdocumentor/type-resolver", - "version": "2.0.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9" + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/327a05bbee54120d4786a0dc67aad30226ad4cf9", - "reference": "327a05bbee54120d4786a0dc67aad30226ad4cf9", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/92a98ada2b93d9b201a613cb5a33584dde25f195", + "reference": "92a98ada2b93d9b201a613cb5a33584dde25f195", "shasum": "" }, "require": { "doctrine/deprecations": "^1.0", - "php": "^7.4 || ^8.0", + "php": "^7.3 || ^8.0", "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^2.0" + "phpstan/phpdoc-parser": "^1.18|^2.0" }, "require-dev": { "ext-tokenizer": "*", "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.4", - "phpstan/phpstan": "^2.1", - "phpstan/phpstan-phpunit": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", "phpunit/phpunit": "^9.5", - "psalm/phar": "^4" + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" }, "type": "library", "extra": { "branch-alias": { - "dev-1.x": "1.x-dev", - "dev-2.x": "2.x-dev" + "dev-1.x": "1.x-dev" } }, "autoload": { @@ -3572,22 +3571,22 @@ "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", "support": { "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/2.0.0" + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.12.0" }, - "time": "2026-01-06T21:53:42+00:00" + "time": "2025-11-21T15:09:14+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "2.3.2", + "version": "2.3.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a" + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/a004701b11273a26cd7955a61d67a7f1e525a45a", - "reference": "a004701b11273a26cd7955a61d67a7f1e525a45a", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", "shasum": "" }, "require": { @@ -3619,9 +3618,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.2" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" }, - "time": "2026-01-25T14:56:51+00:00" + "time": "2025-08-30T15:50:23+00:00" }, { "name": "revolt/event-loop", @@ -3890,16 +3889,16 @@ }, { "name": "symfony/console", - "version": "v6.4.32", + "version": "v6.4.31", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3" + "reference": "f9f8a889f54c264f9abac3fc0f7a371ffca51997" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3", - "reference": "0bc2199c6c1f05276b05956f1ddc63f6d7eb5fc3", + "url": "https://api.github.com/repos/symfony/console/zipball/f9f8a889f54c264f9abac3fc0f7a371ffca51997", + "reference": "f9f8a889f54c264f9abac3fc0f7a371ffca51997", "shasum": "" }, "require": { @@ -3964,7 +3963,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.4.32" + "source": "https://github.com/symfony/console/tree/v6.4.31" }, "funding": [ { @@ -3984,7 +3983,7 @@ "type": "tidelift" } ], - "time": "2026-01-13T08:45:59+00:00" + "time": "2025-12-22T08:30:34+00:00" }, { "name": "symfony/filesystem", @@ -4058,16 +4057,16 @@ }, { "name": "symfony/finder", - "version": "v6.4.33", + "version": "v6.4.31", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "24965ca011dac87431729640feef8bcf7b5523e0" + "reference": "5547f2e1f0ca8e2e7abe490156b62da778cfbe2b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/24965ca011dac87431729640feef8bcf7b5523e0", - "reference": "24965ca011dac87431729640feef8bcf7b5523e0", + "url": "https://api.github.com/repos/symfony/finder/zipball/5547f2e1f0ca8e2e7abe490156b62da778cfbe2b", + "reference": "5547f2e1f0ca8e2e7abe490156b62da778cfbe2b", "shasum": "" }, "require": { @@ -4102,7 +4101,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.33" + "source": "https://github.com/symfony/finder/tree/v6.4.31" }, "funding": [ { @@ -4122,7 +4121,7 @@ "type": "tidelift" } ], - "time": "2026-01-26T13:03:48+00:00" + "time": "2025-12-11T14:52:17+00:00" }, { "name": "symfony/polyfill-intl-grapheme", @@ -4460,16 +4459,16 @@ }, { "name": "symfony/string", - "version": "v7.4.4", + "version": "v7.4.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f" + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/1c4b10461bf2ec27537b5f36105337262f5f5d6f", - "reference": "1c4b10461bf2ec27537b5f36105337262f5f5d6f", + "url": "https://api.github.com/repos/symfony/string/zipball/d50e862cb0a0e0886f73ca1f31b865efbb795003", + "reference": "d50e862cb0a0e0886f73ca1f31b865efbb795003", "shasum": "" }, "require": { @@ -4527,7 +4526,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.4.4" + "source": "https://github.com/symfony/string/tree/v7.4.0" }, "funding": [ { @@ -4547,7 +4546,7 @@ "type": "tidelift" } ], - "time": "2026-01-12T10:54:30+00:00" + "time": "2025-11-27T13:27:24+00:00" }, { "name": "vimeo/psalm", @@ -4736,16 +4735,16 @@ }, { "name": "webmozart/assert", - "version": "2.1.2", + "version": "2.0.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649" + "reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", - "reference": "ce6a2f100c404b2d32a1dd1270f9b59ad4f57649", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/1b34b004e35a164bc5bb6ebd33c844b2d8069a54", + "reference": "1b34b004e35a164bc5bb6ebd33c844b2d8069a54", "shasum": "" }, "require": { @@ -4792,9 +4791,9 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/2.1.2" + "source": "https://github.com/webmozarts/assert/tree/2.0.0" }, - "time": "2026-01-13T14:02:24+00:00" + "time": "2025-12-16T21:36:00+00:00" } ], "aliases": [], diff --git a/php/containers.json b/php/containers.json index 8e9218ac..8c507f91 100644 --- a/php/containers.json +++ b/php/containers.json @@ -219,7 +219,6 @@ "SIGNALING_SECRET=%SIGNALING_SECRET%", "ONLYOFFICE_SECRET=%ONLYOFFICE_SECRET%", "AIO_URL=%AIO_URL%", - "NC_AIO_VERSION=v%AIO_VERSION%", "NEXTCLOUD_MOUNT=%NEXTCLOUD_MOUNT%", "CLAMAV_ENABLED=%CLAMAV_ENABLED%", "CLAMAV_HOST=nextcloud-aio-clamav", diff --git a/php/get-configurable-aio-variables.sh b/php/get-configurable-aio-variables.sh deleted file mode 100755 index 3093e1e0..00000000 --- a/php/get-configurable-aio-variables.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -awk '/^ public [^f][^u][^n]/ { sub(/\$/, "", $3); print $3 }' src/Data/ConfigurationManager.php | sort diff --git a/php/psalm.xml b/php/psalm.xml index 576d82d2..d7ce38c9 100644 --- a/php/psalm.xml +++ b/php/psalm.xml @@ -20,10 +20,5 @@ - - - - - diff --git a/php/public/containers-form-submit.js b/php/public/containers-form-submit.js index 1382bced..b7ffd2d8 100644 --- a/php/public/containers-form-submit.js +++ b/php/public/containers-form-submit.js @@ -1,9 +1,7 @@ document.addEventListener("DOMContentLoaded", function () { // Hide submit button initially - const optionsFormSubmit = document.querySelectorAll(".options-form-submit"); - optionsFormSubmit.forEach(element => { - element.style.display = 'none'; - }); + const optionsFormSubmit = document.getElementById("options-form-submit"); + optionsFormSubmit.style.display = 'none'; const communityFormSubmit = document.getElementById("community-form-submit"); communityFormSubmit.style.display = 'none'; @@ -14,14 +12,6 @@ document.addEventListener("DOMContentLoaded", function () { const optionsContainersCheckboxes = document.querySelectorAll("#options-form input[type='checkbox']"); const communityContainersCheckboxes = document.querySelectorAll("#community-form input[type='checkbox']"); - // Office suite radio buttons - const collaboraRadio = document.getElementById('office-collabora'); - const onlyofficeRadio = document.getElementById('office-onlyoffice'); - const noneRadio = document.getElementById('office-none'); - const collaboraHidden = document.getElementById('collabora'); - const onlyofficeHidden = document.getElementById('onlyoffice'); - let initialOfficeSelection = null; - optionsContainersCheckboxes.forEach(checkbox => { initialStateOptionsContainers[checkbox.id] = checkbox.checked; // Use checked property to capture actual initial state }); @@ -30,17 +20,6 @@ document.addEventListener("DOMContentLoaded", function () { initialStateCommunityContainers[checkbox.id] = checkbox.checked; // Use checked property to capture actual initial state }); - // Store initial office suite selection - if (collaboraRadio && onlyofficeRadio && noneRadio) { - if (collaboraRadio.checked) { - initialOfficeSelection = 'collabora'; - } else if (onlyofficeRadio.checked) { - initialOfficeSelection = 'onlyoffice'; - } else { - initialOfficeSelection = 'none'; - } - } - // Function to compare current states to initial states function checkForOptionContainerChanges() { let hasChanges = false; @@ -51,32 +30,8 @@ document.addEventListener("DOMContentLoaded", function () { } }); - // Check office suite changes and sync to hidden inputs - if (collaboraRadio && onlyofficeRadio && noneRadio && collaboraHidden && onlyofficeHidden) { - let currentOfficeSelection = null; - if (collaboraRadio.checked) { - currentOfficeSelection = 'collabora'; - collaboraHidden.value = 'on'; - onlyofficeHidden.value = ''; - } else if (onlyofficeRadio.checked) { - currentOfficeSelection = 'onlyoffice'; - collaboraHidden.value = ''; - onlyofficeHidden.value = 'on'; - } else { - currentOfficeSelection = 'none'; - collaboraHidden.value = ''; - onlyofficeHidden.value = ''; - } - - if (currentOfficeSelection !== initialOfficeSelection) { - hasChanges = true; - } - } - // Show or hide submit button based on changes - optionsFormSubmit.forEach(element => { - element.style.display = hasChanges ? 'block' : 'none'; - }); + optionsFormSubmit.style.display = hasChanges ? 'block' : 'none'; } // Function to compare current states to initial states @@ -127,13 +82,6 @@ document.addEventListener("DOMContentLoaded", function () { // Initialize talk-recording visibility on page load handleTalkVisibility(); // Ensure talk-recording is correctly initialized - // Add event listeners for office suite radio buttons - if (collaboraRadio && onlyofficeRadio && noneRadio) { - collaboraRadio.addEventListener('change', checkForOptionContainerChanges); - onlyofficeRadio.addEventListener('change', checkForOptionContainerChanges); - noneRadio.addEventListener('change', checkForOptionContainerChanges); - } - // Initial call to check for changes checkForOptionContainerChanges(); checkForCommunityContainerChanges(); diff --git a/php/public/disable-collabora.js b/php/public/disable-collabora.js index 762252ce..3064ef51 100644 --- a/php/public/disable-collabora.js +++ b/php/public/disable-collabora.js @@ -1,5 +1,5 @@ document.addEventListener("DOMContentLoaded", function(event) { // Collabora - const collabora = document.getElementById("office-collabora"); + let collabora = document.getElementById("collabora"); collabora.disabled = true; }); \ No newline at end of file diff --git a/php/public/disable-onlyoffice.js b/php/public/disable-onlyoffice.js index c660bd9d..83482339 100644 --- a/php/public/disable-onlyoffice.js +++ b/php/public/disable-onlyoffice.js @@ -1,5 +1,7 @@ document.addEventListener("DOMContentLoaded", function(event) { // OnlyOffice - const onlyoffice = document.getElementById("office-onlyoffice"); - onlyoffice.disabled = true; + let onlyoffice = document.getElementById("onlyoffice"); + if (onlyoffice) { + onlyoffice.disabled = true; + } }); \ No newline at end of file diff --git a/php/public/index.php b/php/public/index.php index cc06bb90..b57f65a5 100644 --- a/php/public/index.php +++ b/php/public/index.php @@ -91,54 +91,54 @@ $app->get('/containers', function (Request $request, Response $response, array $ $skip_domain_validation = isset($params['skip_domain_validation']); return $view->render($response, 'containers.twig', [ - 'domain' => $configurationManager->domain, - 'apache_port' => $configurationManager->apachePort, - 'borg_backup_host_location' => $configurationManager->borgBackupHostLocation, - 'borg_remote_repo' => $configurationManager->borgRemoteRepo, - 'borg_public_key' => $configurationManager->getBorgPublicKey(), - 'nextcloud_password' => $configurationManager->getAndGenerateSecret('NEXTCLOUD_PASSWORD'), + 'domain' => $configurationManager->GetDomain(), + 'apache_port' => $configurationManager->GetApachePort(), + 'borg_backup_host_location' => $configurationManager->GetBorgBackupHostLocation(), + 'borg_remote_repo' => $configurationManager->GetBorgRemoteRepo(), + 'borg_public_key' => $configurationManager->GetBorgPublicKey(), + 'nextcloud_password' => $configurationManager->GetAndGenerateSecret('NEXTCLOUD_PASSWORD'), 'containers' => (new \AIO\ContainerDefinitionFetcher($container->get(\AIO\Data\ConfigurationManager::class), $container))->FetchDefinition(), - 'borgbackup_password' => $configurationManager->getAndGenerateSecret('BORGBACKUP_PASSWORD'), + 'borgbackup_password' => $configurationManager->GetAndGenerateSecret('BORGBACKUP_PASSWORD'), 'is_mastercontainer_update_available' => ( $bypass_mastercontainer_update ? false : $dockerActionManager->IsMastercontainerUpdateAvailable() ), 'has_backup_run_once' => $configurationManager->hasBackupRunOnce(), 'is_backup_container_running' => $dockerActionManager->isBackupContainerRunning(), 'backup_exit_code' => $dockerActionManager->GetBackupcontainerExitCode(), - 'is_instance_restore_attempt' => $configurationManager->instanceRestoreAttempt, - 'borg_backup_mode' => $configurationManager->backupMode, - 'was_start_button_clicked' => $configurationManager->wasStartButtonClicked, + 'is_instance_restore_attempt' => $configurationManager->isInstanceRestoreAttempt(), + 'borg_backup_mode' => $configurationManager->GetBackupMode(), + 'was_start_button_clicked' => $configurationManager->wasStartButtonClicked(), 'has_update_available' => $dockerActionManager->isAnyUpdateAvailable(), - 'last_backup_time' => $configurationManager->getLastBackupTime(), - 'backup_times' => $configurationManager->getBackupTimes(), + 'last_backup_time' => $configurationManager->GetLastBackupTime(), + 'backup_times' => $configurationManager->GetBackupTimes(), 'current_channel' => $dockerActionManager->GetCurrentChannel(), - 'is_clamav_enabled' => $configurationManager->isClamavEnabled, - 'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled, - 'is_collabora_enabled' => $configurationManager->isCollaboraEnabled, - 'is_talk_enabled' => $configurationManager->isTalkEnabled, - 'borg_restore_password' => $configurationManager->borgRestorePassword, - 'daily_backup_time' => $configurationManager->getDailyBackupTime(), + 'is_clamav_enabled' => $configurationManager->isClamavEnabled(), + 'is_onlyoffice_enabled' => $configurationManager->isOnlyofficeEnabled(), + 'is_collabora_enabled' => $configurationManager->isCollaboraEnabled(), + 'is_talk_enabled' => $configurationManager->isTalkEnabled(), + 'borg_restore_password' => $configurationManager->GetBorgRestorePassword(), + 'daily_backup_time' => $configurationManager->GetDailyBackupTime(), 'is_daily_backup_running' => $configurationManager->isDailyBackupRunning(), - 'timezone' => $configurationManager->timezone, + 'timezone' => $configurationManager->GetTimezone(), 'skip_domain_validation' => $configurationManager->shouldDomainValidationBeSkipped($skip_domain_validation), - 'talk_port' => $configurationManager->talkPort, - 'collabora_dictionaries' => $configurationManager->collaboraDictionaries, - 'collabora_additional_options' => $configurationManager->collaboraAdditionalOptions, + 'talk_port' => $configurationManager->GetTalkPort(), + 'collabora_dictionaries' => $configurationManager->GetCollaboraDictionaries(), + 'collabora_additional_options' => $configurationManager->GetAdditionalCollaboraOptions(), 'automatic_updates' => $configurationManager->areAutomaticUpdatesEnabled(), - 'is_backup_section_enabled' => !$configurationManager->disableBackupSection, - 'is_imaginary_enabled' => $configurationManager->isImaginaryEnabled, - 'is_fulltextsearch_enabled' => $configurationManager->isFulltextsearchEnabled, - 'additional_backup_directories' => $configurationManager->getAdditionalBackupDirectoriesString(), - 'nextcloud_datadir' => $configurationManager->nextcloudDatadirMount, - 'nextcloud_mount' => $configurationManager->nextcloudMount, - 'nextcloud_upload_limit' => $configurationManager->nextcloudUploadLimit, - 'nextcloud_max_time' => $configurationManager->nextcloudMaxTime, - 'nextcloud_memory_limit' => $configurationManager->nextcloudMemoryLimit, - 'is_dri_device_enabled' => $configurationManager->nextcloudEnableDriDevice, - 'is_nvidia_gpu_enabled' => $configurationManager->enableNvidiaGpu, - 'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled, - 'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled, - 'is_whiteboard_enabled' => $configurationManager->isWhiteboardEnabled, + 'is_backup_section_enabled' => $configurationManager->isBackupSectionEnabled(), + 'is_imaginary_enabled' => $configurationManager->isImaginaryEnabled(), + 'is_fulltextsearch_enabled' => $configurationManager->isFulltextsearchEnabled(), + 'additional_backup_directories' => $configurationManager->GetAdditionalBackupDirectoriesString(), + 'nextcloud_datadir' => $configurationManager->GetNextcloudDatadirMount(), + 'nextcloud_mount' => $configurationManager->GetNextcloudMount(), + 'nextcloud_upload_limit' => $configurationManager->GetNextcloudUploadLimit(), + 'nextcloud_max_time' => $configurationManager->GetNextcloudMaxTime(), + 'nextcloud_memory_limit' => $configurationManager->GetNextcloudMemoryLimit(), + 'is_dri_device_enabled' => $configurationManager->isDriDeviceEnabled(), + 'is_nvidia_gpu_enabled' => $configurationManager->isNvidiaGpuEnabled(), + 'is_talk_recording_enabled' => $configurationManager->isTalkRecordingEnabled(), + 'is_docker_socket_proxy_enabled' => $configurationManager->isDockerSocketProxyEnabled(), + 'is_whiteboard_enabled' => $configurationManager->isWhiteboardEnabled(), 'community_containers' => $configurationManager->listAvailableCommunityContainers(), - 'community_containers_enabled' => $configurationManager->aioCommunityContainers, + 'community_containers_enabled' => $configurationManager->GetEnabledCommunityContainers(), 'bypass_container_update' => $bypass_container_update, ]); })->setName('profile'); diff --git a/php/public/style.css b/php/public/style.css index b35883d0..b4d5f8a5 100644 --- a/php/public/style.css +++ b/php/public/style.css @@ -28,7 +28,7 @@ --border-radius-large: 12px; --default-font-size: 13px; --checkbox-size: 16px; - --max-width: 580px; + --max-width: 500px; --container-top-margin: 20px; --container-bottom-margin: 20px; --container-padding: 2px; @@ -37,9 +37,9 @@ --main-padding: 50px; } -/* Breakpoint calculation: 580px (max-width) + 100px (main-padding * 2) + 200px (additional space) = 880px +/* Breakpoint calculation: 500px (max-width) + 100px (main-padding * 2) + 200px (additional space) = 800px Note: Unfortunately, it's not possible to calculate this dynamically using CSS variables in media queries */ -@media only screen and (max-width: 880px) { +@media only screen and (max-width: 800px) { :root { --container-top-margin: 50px; --container-bottom-margin: 0px; @@ -549,160 +549,3 @@ input[type="checkbox"]:disabled:not(:checked) + label { #theme-toggle:not(:hover) #theme-icon { opacity: 0.6; /* Slightly transparent */ } -/* Office Suite Feature Cards */ -.office-suite-cards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 16px; - margin: 20px 0; - align-items: stretch; -} - -.office-radio { - display: none; -} - -.office-card { - position: relative; - border: 2px solid var(--color-border-maxcontrast); - border-radius: var(--border-radius-large); - padding: 20px; - cursor: pointer; - transition: all 0.3s ease; - background-color: var(--color-main-background); - display: flex; - flex-direction: column; -} - -.office-card-disabled { - opacity: 50%; - pointer-events: none; -} - -.office-card:hover { - border-color: var(--color-primary-element); - box-shadow: 0 4px 12px rgba(0, 130, 201, 0.15); - transform: translateY(-2px); -} - -#office-collabora:checked + .office-card, -#office-onlyoffice:checked + .office-card { - border-color: var(--color-nextcloud-blue); - background: linear-gradient(135deg, rgba(0, 130, 201, 0.08) 0%, rgba(0, 130, 201, 0.02) 100%); -} - -[data-theme="dark"] #office-collabora:checked + .office-card, -[data-theme="dark"] #office-onlyoffice:checked + .office-card { - background: linear-gradient(135deg, rgba(0, 145, 242, 0.15) 0%, rgba(0, 145, 242, 0.03) 100%); -} - -.office-card-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 16px; -} - -.office-card h4 { - margin: 0; - height: 24px; - font-size: 18px; - font-weight: 600; - color: var(--color-main-text); -} - -.office-checkmark { - flex-shrink: 0; - display: none; -} - -#office-collabora:checked + .office-card .office-checkmark, -#office-onlyoffice:checked + .office-card .office-checkmark { - display: block; -} - -.office-features { - list-style: none; - padding: 0; - margin: 0; -} - -.office-features li { - position: relative; - padding-left: 20px; - margin-bottom: 4px; - font-size: var(--default-font-size); - line-height: 1.5; - color: var(--color-main-text); -} - -.office-features li::before { - content: '•'; - position: absolute; - left: 6px; - color: var(--color-nextcloud-blue); - font-weight: bold; -} - -.office-checkbox { - position: absolute; - opacity: 0; - pointer-events: none; -} - -.office-learn-more { - display: inline-flex; - align-items: center; - margin-top: 12px; - color: var(--color-primary-element); - text-decoration: none; - font-size: var(--default-font-size); - font-weight: 500; - transition: color 0.2s ease; -} - -.office-learn-more:hover { - color: var(--color-primary-element-hover); -} - -.office-learn-more svg { - transition: transform 0.2s ease; -} - -.office-learn-more:hover svg { - transform: translateX(3px); -} - -.office-none-card { - text-align: center; - margin: 12px 0 20px 0; -} - -.office-none-label { - display: inline-flex; - align-items: center; - font-size: 13px; - color: var(--color-primary-element); - cursor: pointer; - opacity: 0.7; - transition: opacity 0.2s ease; - padding: 8px 12px; - border-radius: var(--border-radius); -} - -.office-none-label:hover { - opacity: 1; - background-color: var(--color-primary-element-light); -} - -#office-none:checked + .office-none-label { - opacity: 1; - font-weight: 600; -} - -/* Responsive adjustments for mobile */ -@media only screen and (max-width: 800px) { - .office-suite-cards { - grid-template-columns: 1fr; - } -} \ No newline at end of file diff --git a/php/src/Auth/AuthManager.php b/php/src/Auth/AuthManager.php index f6ab0d10..925ff89f 100644 --- a/php/src/Auth/AuthManager.php +++ b/php/src/Auth/AuthManager.php @@ -15,11 +15,11 @@ readonly class AuthManager { } public function CheckCredentials(string $password) : bool { - return hash_equals($this->configurationManager->password, $password); + return hash_equals($this->configurationManager->GetPassword(), $password); } public function CheckToken(string $token) : bool { - return hash_equals($this->configurationManager->aioToken, $token); + return hash_equals($this->configurationManager->GetToken(), $token); } public function SetAuthState(bool $isLoggedIn) : void { diff --git a/php/src/Container/Container.php b/php/src/Container/Container.php index 6e5d2b54..baee1c00 100644 --- a/php/src/Container/Container.php +++ b/php/src/Container/Container.php @@ -5,56 +5,121 @@ namespace AIO\Container; use AIO\Data\ConfigurationManager; use AIO\Docker\DockerActionManager; use AIO\ContainerDefinitionFetcher; -use JsonException; readonly class Container { public function __construct( - public string $identifier, - public string $displayName, - public string $containerName, - public string $restartPolicy, - public int $maxShutdownTime, - public ContainerPorts $ports, - public string $internalPorts, - public ContainerVolumes $volumes, - public ContainerEnvironmentVariables $containerEnvironmentVariables, + private string $identifier, + private string $displayName, + private string $containerName, + private string $restartPolicy, + private int $maxShutdownTime, + private ContainerPorts $ports, + private string $internalPorts, + private ContainerVolumes $volumes, + private ContainerEnvironmentVariables $containerEnvironmentVariables, /** @var string[] */ - public array $dependsOn, + private array $dependsOn, private string $uiSecret, /** @var string[] */ - public array $devices, - public bool $enableNvidiaGpu, + private array $devices, + private bool $enableNvidiaGpu, /** @var string[] */ - public array $capAdd, - public int $shmSize, - public bool $apparmorUnconfined, + private array $capAdd, + private int $shmSize, + private bool $apparmorUnconfined, /** @var string[] */ - public array $backupVolumes, - public array $nextcloudExecCommands, - public bool $readOnlyRootFs, - public array $tmpfs, - public bool $init, - public string $imageTag, - public AioVariables $aioVariables, - public string $documentation, + private array $backupVolumes, + private array $nextcloudExecCommands, + private bool $readOnlyRootFs, + private array $tmpfs, + private bool $init, + private string $imageTag, + private AioVariables $aioVariables, + private string $documentation, private DockerActionManager $dockerActionManager ) { } + public function GetIdentifier() : string { + return $this->identifier; + } + + public function GetDisplayName() : string { + return $this->displayName; + } + + public function GetContainerName() : string { + return $this->containerName; + } + + public function GetRestartPolicy() : string { + return $this->restartPolicy; + } + + public function GetImageTag() : string { + return $this->imageTag; + } + + public function GetReadOnlySetting() : bool { + return $this->readOnlyRootFs; + } + + public function GetInit() : bool { + return $this->init; + } + + public function GetShmSize() : int { + return $this->shmSize; + } + + public function isApparmorUnconfined() : bool { + return $this->apparmorUnconfined; + } + + public function GetMaxShutdownTime() : int { + return $this->maxShutdownTime; + } + public function GetUiSecret() : string { return $this->dockerActionManager->GetAndGenerateSecretWrapper($this->uiSecret); } - /** - * @throws JsonException - */ + public function GetTmpfs() : array { + return $this->tmpfs; + } + + public function GetDevices() : array { + return $this->devices; + } + + public function isNvidiaGpuEnabled() : bool { + return $this->enableNvidiaGpu; + } + + public function GetCapAdds() : array { + return $this->capAdd; + } + + public function GetBackupVolumes() : array { + return $this->backupVolumes; + } + + public function GetPorts() : ContainerPorts { + return $this->ports; + } + + public function GetInternalPort() : string { + return $this->internalPorts; + } + + public function GetVolumes() : ContainerVolumes { + return $this->volumes; + } + public function GetRunningState() : ContainerState { return $this->dockerActionManager->GetContainerRunningState($this); } - /** - * @throws JsonException - */ public function GetRestartingState() : ContainerState { return $this->dockerActionManager->GetContainerRestartingState($this); } @@ -66,4 +131,27 @@ readonly class Container { public function GetStartingState() : ContainerState { return $this->dockerActionManager->GetContainerStartingState($this); } + + /** + * @return string[] + */ + public function GetDependsOn() : array { + return $this->dependsOn; + } + + public function GetNextcloudExecCommands() : array { + return $this->nextcloudExecCommands; + } + + public function GetEnvironmentVariables() : ContainerEnvironmentVariables { + return $this->containerEnvironmentVariables; + } + + public function GetAioVariables() : AioVariables { + return $this->aioVariables; + } + + public function GetDocumentation() : string { + return $this->documentation; + } } diff --git a/php/src/ContainerDefinitionFetcher.php b/php/src/ContainerDefinitionFetcher.php index d2519ed7..7b092e45 100644 --- a/php/src/ContainerDefinitionFetcher.php +++ b/php/src/ContainerDefinitionFetcher.php @@ -25,7 +25,7 @@ readonly class ContainerDefinitionFetcher { $containers = $this->FetchDefinition(); foreach ($containers as $container) { - if ($container->identifier === $id) { + if ($container->GetIdentifier() === $id) { return $container; } } @@ -41,7 +41,7 @@ readonly class ContainerDefinitionFetcher { $data = json_decode((string)file_get_contents(DataConst::GetContainersDefinitionPath()), true, 512, JSON_THROW_ON_ERROR); $additionalContainerNames = []; - foreach ($this->configurationManager->aioCommunityContainers as $communityContainer) { + foreach ($this->configurationManager->GetEnabledCommunityContainers() as $communityContainer) { if ($communityContainer !== '') { $path = DataConst::GetCommunityContainersDirectory() . '/' . $communityContainer . '/' . $communityContainer . '.json'; $additionalData = json_decode((string)file_get_contents($path), true, 512, JSON_THROW_ON_ERROR); @@ -56,42 +56,42 @@ readonly class ContainerDefinitionFetcher { $containers = []; foreach ($data['aio_services_v1'] as $entry) { if ($entry['container_name'] === 'nextcloud-aio-clamav') { - if (!$this->configurationManager->isClamavEnabled) { + if (!$this->configurationManager->isClamavEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-onlyoffice') { - if (!$this->configurationManager->isOnlyofficeEnabled) { + if (!$this->configurationManager->isOnlyofficeEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-collabora') { - if (!$this->configurationManager->isCollaboraEnabled) { + if (!$this->configurationManager->isCollaboraEnabled()) { continue; } if ($this->configurationManager->isCollaboraSubscriptionEnabled()) { $entry['image'] = 'ghcr.io/nextcloud-releases/aio-collabora-online'; } } elseif ($entry['container_name'] === 'nextcloud-aio-talk') { - if (!$this->configurationManager->isTalkEnabled) { + if (!$this->configurationManager->isTalkEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-talk-recording') { - if (!$this->configurationManager->isTalkRecordingEnabled) { + if (!$this->configurationManager->isTalkRecordingEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-imaginary') { - if (!$this->configurationManager->isImaginaryEnabled) { + if (!$this->configurationManager->isImaginaryEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-fulltextsearch') { - if (!$this->configurationManager->isFulltextsearchEnabled) { + if (!$this->configurationManager->isFulltextsearchEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-docker-socket-proxy') { - if (!$this->configurationManager->isDockerSocketProxyEnabled) { + if (!$this->configurationManager->isDockerSocketProxyEnabled()) { continue; } } elseif ($entry['container_name'] === 'nextcloud-aio-whiteboard') { - if (!$this->configurationManager->isWhiteboardEnabled) { + if (!$this->configurationManager->isWhiteboardEnabled()) { continue; } } @@ -113,34 +113,34 @@ readonly class ContainerDefinitionFetcher { if (isset($entry['volumes'])) { foreach ($entry['volumes'] as $value) { if($value['source'] === '%BORGBACKUP_HOST_LOCATION%') { - $value['source'] = $this->configurationManager->borgBackupHostLocation; + $value['source'] = $this->configurationManager->GetBorgBackupHostLocation(); if($value['source'] === '') { continue; } } if($value['source'] === '%NEXTCLOUD_MOUNT%') { - $value['source'] = $this->configurationManager->nextcloudMount; + $value['source'] = $this->configurationManager->GetNextcloudMount(); if($value['source'] === '') { continue; } } elseif ($value['source'] === '%NEXTCLOUD_DATADIR%') { - $value['source'] = $this->configurationManager->nextcloudDatadirMount; + $value['source'] = $this->configurationManager->GetNextcloudDatadirMount(); if ($value['source'] === '') { continue; } } elseif ($value['source'] === '%WATCHTOWER_DOCKER_SOCKET_PATH%') { - $value['source'] = $this->configurationManager->dockerSocketPath; + $value['source'] = $this->configurationManager->GetDockerSocketPath(); if($value['source'] === '') { continue; } } elseif ($value['source'] === '%NEXTCLOUD_TRUSTED_CACERTS_DIR%') { - $value['source'] = $this->configurationManager->trustedCacertsDir; + $value['source'] = $this->configurationManager->GetTrustedCacertsDir(); if($value['source'] === '') { continue; } } if ($value['destination'] === '%NEXTCLOUD_MOUNT%') { - $value['destination'] = $this->configurationManager->nextcloudMount; + $value['destination'] = $this->configurationManager->GetNextcloudMount(); if($value['destination'] === '') { continue; } @@ -168,39 +168,39 @@ readonly class ContainerDefinitionFetcher { } foreach ($valueDependsOn as $value) { if ($value === 'nextcloud-aio-clamav') { - if (!$this->configurationManager->isClamavEnabled) { + if (!$this->configurationManager->isClamavEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-onlyoffice') { - if (!$this->configurationManager->isOnlyofficeEnabled) { + if (!$this->configurationManager->isOnlyofficeEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-collabora') { - if (!$this->configurationManager->isCollaboraEnabled) { + if (!$this->configurationManager->isCollaboraEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-talk') { - if (!$this->configurationManager->isTalkEnabled) { + if (!$this->configurationManager->isTalkEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-talk-recording') { - if (!$this->configurationManager->isTalkRecordingEnabled) { + if (!$this->configurationManager->isTalkRecordingEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-imaginary') { - if (!$this->configurationManager->isImaginaryEnabled) { + if (!$this->configurationManager->isImaginaryEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-fulltextsearch') { - if (!$this->configurationManager->isFulltextsearchEnabled) { + if (!$this->configurationManager->isFulltextsearchEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-docker-socket-proxy') { - if (!$this->configurationManager->isDockerSocketProxyEnabled) { + if (!$this->configurationManager->isDockerSocketProxyEnabled()) { continue; } } elseif ($value === 'nextcloud-aio-whiteboard') { - if (!$this->configurationManager->isWhiteboardEnabled) { + if (!$this->configurationManager->isWhiteboardEnabled()) { continue; } } @@ -246,7 +246,7 @@ readonly class ContainerDefinitionFetcher { // All secrets are registered with the configuration when they // are discovered so they can be later generated at time-of-use. foreach ($entry['secrets'] as $secret) { - $this->configurationManager->registerSecret($secret); + $this->configurationManager->RegisterSecret($secret); } } diff --git a/php/src/Controller/ConfigurationController.php b/php/src/Controller/ConfigurationController.php index c40ee98c..45586f9c 100644 --- a/php/src/Controller/ConfigurationController.php +++ b/php/src/Controller/ConfigurationController.php @@ -17,30 +17,29 @@ readonly class ConfigurationController { public function SetConfig(Request $request, Response $response, array $args): Response { try { - $this->configurationManager->startTransaction(); if (isset($request->getParsedBody()['domain'])) { $domain = $request->getParsedBody()['domain'] ?? ''; $skipDomainValidation = isset($request->getParsedBody()['skip_domain_validation']); - $this->configurationManager->setDomain($domain, $skipDomainValidation); + $this->configurationManager->SetDomain($domain, $skipDomainValidation); } if (isset($request->getParsedBody()['current-master-password']) || isset($request->getParsedBody()['new-master-password'])) { $currentMasterPassword = $request->getParsedBody()['current-master-password'] ?? ''; $newMasterPassword = $request->getParsedBody()['new-master-password'] ?? ''; - $this->configurationManager->changeMasterPassword($currentMasterPassword, $newMasterPassword); + $this->configurationManager->ChangeMasterPassword($currentMasterPassword, $newMasterPassword); } if (isset($request->getParsedBody()['borg_backup_host_location']) || isset($request->getParsedBody()['borg_remote_repo'])) { $location = $request->getParsedBody()['borg_backup_host_location'] ?? ''; $borgRemoteRepo = $request->getParsedBody()['borg_remote_repo'] ?? ''; - $this->configurationManager->setBorgLocationVars($location, $borgRemoteRepo); + $this->configurationManager->SetBorgLocationVars($location, $borgRemoteRepo); } if (isset($request->getParsedBody()['borg_restore_host_location']) || isset($request->getParsedBody()['borg_restore_remote_repo']) || isset($request->getParsedBody()['borg_restore_password'])) { $restoreLocation = $request->getParsedBody()['borg_restore_host_location'] ?? ''; $borgRemoteRepo = $request->getParsedBody()['borg_restore_remote_repo'] ?? ''; $borgPassword = $request->getParsedBody()['borg_restore_password'] ?? ''; - $this->configurationManager->setBorgRestoreLocationVarsAndPassword($restoreLocation, $borgRemoteRepo, $borgPassword); + $this->configurationManager->SetBorgRestoreLocationVarsAndPassword($restoreLocation, $borgRemoteRepo, $borgPassword); } if (isset($request->getParsedBody()['daily_backup_time'])) { @@ -55,47 +54,76 @@ readonly class ConfigurationController { $successNotification = false; } $dailyBackupTime = $request->getParsedBody()['daily_backup_time'] ?? ''; - $this->configurationManager->setDailyBackupTime($dailyBackupTime, $enableAutomaticUpdates, $successNotification); + $this->configurationManager->SetDailyBackupTime($dailyBackupTime, $enableAutomaticUpdates, $successNotification); } if (isset($request->getParsedBody()['delete_daily_backup_time'])) { - $this->configurationManager->deleteDailyBackupTime(); + $this->configurationManager->DeleteDailyBackupTime(); } if (isset($request->getParsedBody()['additional_backup_directories'])) { $additionalBackupDirectories = $request->getParsedBody()['additional_backup_directories'] ?? ''; - $this->configurationManager->setAdditionalBackupDirectories($additionalBackupDirectories); + $this->configurationManager->SetAdditionalBackupDirectories($additionalBackupDirectories); } if (isset($request->getParsedBody()['delete_timezone'])) { - $this->configurationManager->deleteTimezone(); + $this->configurationManager->DeleteTimezone(); } if (isset($request->getParsedBody()['timezone'])) { $timezone = $request->getParsedBody()['timezone'] ?? ''; - $this->configurationManager->timezone = $timezone; + $this->configurationManager->SetTimezone($timezone); } if (isset($request->getParsedBody()['options-form'])) { - $officeSuiteChoice = $request->getParsedBody()['office_suite_choice'] ?? ''; - - if ($officeSuiteChoice === 'collabora') { - $this->configurationManager->isCollaboraEnabled = true; - $this->configurationManager->isOnlyofficeEnabled = false; - } elseif ($officeSuiteChoice === 'onlyoffice') { - $this->configurationManager->isCollaboraEnabled = false; - $this->configurationManager->isOnlyofficeEnabled = true; - } else { - $this->configurationManager->isCollaboraEnabled = false; - $this->configurationManager->isOnlyofficeEnabled = false; + if (isset($request->getParsedBody()['collabora']) && isset($request->getParsedBody()['onlyoffice'])) { + throw new InvalidSettingConfigurationException("Collabora and Onlyoffice are not allowed to be enabled at the same time!"); + } + if (isset($request->getParsedBody()['clamav'])) { + $this->configurationManager->SetClamavEnabledState(1); + } else { + $this->configurationManager->SetClamavEnabledState(0); + } + if (isset($request->getParsedBody()['onlyoffice'])) { + $this->configurationManager->SetOnlyofficeEnabledState(1); + } else { + $this->configurationManager->SetOnlyofficeEnabledState(0); + } + if (isset($request->getParsedBody()['collabora'])) { + $this->configurationManager->SetCollaboraEnabledState(1); + } else { + $this->configurationManager->SetCollaboraEnabledState(0); + } + if (isset($request->getParsedBody()['talk'])) { + $this->configurationManager->SetTalkEnabledState(1); + } else { + $this->configurationManager->SetTalkEnabledState(0); + } + if (isset($request->getParsedBody()['talk-recording'])) { + $this->configurationManager->SetTalkRecordingEnabledState(1); + } else { + $this->configurationManager->SetTalkRecordingEnabledState(0); + } + if (isset($request->getParsedBody()['imaginary'])) { + $this->configurationManager->SetImaginaryEnabledState(1); + } else { + $this->configurationManager->SetImaginaryEnabledState(0); + } + if (isset($request->getParsedBody()['fulltextsearch'])) { + $this->configurationManager->SetFulltextsearchEnabledState(1); + } else { + $this->configurationManager->SetFulltextsearchEnabledState(0); + } + if (isset($request->getParsedBody()['docker-socket-proxy'])) { + $this->configurationManager->SetDockerSocketProxyEnabledState(1); + } else { + $this->configurationManager->SetDockerSocketProxyEnabledState(0); + } + if (isset($request->getParsedBody()['whiteboard'])) { + $this->configurationManager->SetWhiteboardEnabledState(1); + } else { + $this->configurationManager->SetWhiteboardEnabledState(0); } - $this->configurationManager->isClamavEnabled = isset($request->getParsedBody()['clamav']); - $this->configurationManager->isTalkEnabled = isset($request->getParsedBody()['talk']); - $this->configurationManager->isTalkRecordingEnabled = isset($request->getParsedBody()['talk-recording']); - $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->isWhiteboardEnabled = isset($request->getParsedBody()['whiteboard']); } if (isset($request->getParsedBody()['community-form'])) { @@ -109,37 +137,35 @@ readonly class ConfigurationController { $enabledCC[] = $item; } } - $this->configurationManager->aioCommunityContainers = $enabledCC; + $this->configurationManager->SetEnabledCommunityContainers($enabledCC); } if (isset($request->getParsedBody()['delete_collabora_dictionaries'])) { - $this->configurationManager->deleteCollaboraDictionaries(); + $this->configurationManager->DeleteCollaboraDictionaries(); } if (isset($request->getParsedBody()['collabora_dictionaries'])) { $collaboraDictionaries = $request->getParsedBody()['collabora_dictionaries'] ?? ''; - $this->configurationManager->collaboraDictionaries = $collaboraDictionaries; + $this->configurationManager->SetCollaboraDictionaries($collaboraDictionaries); } if (isset($request->getParsedBody()['delete_collabora_additional_options'])) { - $this->configurationManager->deleteAdditionalCollaboraOptions(); + $this->configurationManager->DeleteAdditionalCollaboraOptions(); } if (isset($request->getParsedBody()['collabora_additional_options'])) { $additionalCollaboraOptions = $request->getParsedBody()['collabora_additional_options'] ?? ''; - $this->configurationManager->collaboraAdditionalOptions = $additionalCollaboraOptions; + $this->configurationManager->SetAdditionalCollaboraOptions($additionalCollaboraOptions); } if (isset($request->getParsedBody()['delete_borg_backup_location_vars'])) { - $this->configurationManager->deleteBorgBackupLocationItems(); + $this->configurationManager->DeleteBorgBackupLocationItems(); } return $response->withStatus(201)->withHeader('Location', '.'); } catch (InvalidSettingConfigurationException $ex) { $response->getBody()->write($ex->getMessage()); return $response->withStatus(422); - } finally { - $this->configurationManager->commitTransaction(); } } } diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php index 81b920d0..27a06bc8 100644 --- a/php/src/Controller/DockerController.php +++ b/php/src/Controller/DockerController.php @@ -23,7 +23,7 @@ readonly class DockerController { $container = $this->containerDefinitionFetcher->GetContainerById($id); // Start all dependencies first and then itself - foreach($container->dependsOn as $dependency) { + foreach($container->GetDependsOn() as $dependency) { $this->PerformRecursiveContainerStart($dependency, $pullImage); } @@ -46,7 +46,7 @@ readonly class DockerController { $container = $this->containerDefinitionFetcher->GetContainerById($id); // Pull all dependencies first and then itself - foreach($container->dependsOn as $dependency) { + foreach($container->GetDependsOn() as $dependency) { $this->PerformRecursiveImagePull($dependency); } @@ -89,7 +89,7 @@ readonly class DockerController { } public function startBackup(bool $forceStopNextcloud = false) : void { - $this->configurationManager->backupMode = 'backup'; + $this->configurationManager->SetBackupMode('backup'); $id = self::TOP_CONTAINER; $this->PerformRecursiveContainerStop($id, $forceStopNextcloud); @@ -109,25 +109,29 @@ readonly class DockerController { } public function checkBackup() : void { - $this->configurationManager->backupMode = 'check'; + $this->configurationManager->SetBackupMode('check'); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); } private function listBackup() : void { - $this->configurationManager->backupMode = 'list'; + $this->configurationManager->SetBackupMode('list'); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); } public function StartBackupContainerRestore(Request $request, Response $response, array $args) : Response { - $this->configurationManager->startTransaction(); - $this->configurationManager->backupMode = 'restore'; - $this->configurationManager->selectedRestoreTime = $request->getParsedBody()['selected_restore_time'] ?? ''; - $this->configurationManager->restoreExcludePreviews = isset($request->getParsedBody()['restore-exclude-previews']); - $this->configurationManager->commitTransaction(); + $this->configurationManager->SetBackupMode('restore'); + $config = $this->configurationManager->GetConfig(); + $config['selected-restore-time'] = $request->getParsedBody()['selected_restore_time'] ?? ''; + if (isset($request->getParsedBody()['restore-exclude-previews'])) { + $config['restore-exclude-previews'] = 1; + } else { + $config['restore-exclude-previews'] = ''; + } + $this->configurationManager->WriteConfig($config); $id = self::TOP_CONTAINER; $forceStopNextcloud = true; @@ -140,22 +144,22 @@ readonly class DockerController { } public function StartBackupContainerCheckRepair(Request $request, Response $response, array $args) : Response { - $this->configurationManager->backupMode = 'check-repair'; + $this->configurationManager->SetBackupMode('check-repair'); $id = 'nextcloud-aio-borgbackup'; $this->PerformRecursiveContainerStart($id); // Restore to backup check which is needed to make the UI logic work correctly - $this->configurationManager->backupMode = 'check'; + $this->configurationManager->SetBackupMode('check'); return $response->withStatus(201)->withHeader('Location', '.'); } public function StartBackupContainerTest(Request $request, Response $response, array $args) : Response { - $this->configurationManager->startTransaction(); - $this->configurationManager->backupMode = 'test'; - $this->configurationManager->instanceRestoreAttempt = false; - $this->configurationManager->commitTransaction(); + $this->configurationManager->SetBackupMode('test'); + $config = $this->configurationManager->GetConfig(); + $config['instance_restore_attempt'] = 0; + $this->configurationManager->WriteConfig($config); $id = self::TOP_CONTAINER; $this->PerformRecursiveContainerStop($id); @@ -178,19 +182,20 @@ readonly class DockerController { } if (isset($request->getParsedBody()['install_latest_major'])) { - $installLatestMajor = '32'; + $installLatestMajor = 32; } else { - $installLatestMajor = ''; + $installLatestMajor = ""; } - - $this->configurationManager->startTransaction(); - $this->configurationManager->installLatestMajor = $installLatestMajor; + + $config = $this->configurationManager->GetConfig(); // set AIO_URL - $this->configurationManager->aioUrl = $host . ':' . (string)$port . $path; + $config['AIO_URL'] = $host . ':' . (string)$port . $path; // set wasStartButtonClicked - $this->configurationManager->wasStartButtonClicked = true; - $this->configurationManager->commitTransaction(); - + $config['wasStartButtonClicked'] = 1; + // set install_latest_major + $config['install_latest_major'] = $installLatestMajor; + $this->configurationManager->WriteConfig($config); + // Do not pull container images in case 'bypass_container_update' is set via url params // Needed for local testing $pullImage = !isset($request->getParsedBody()['bypass_container_update']); @@ -208,7 +213,10 @@ readonly class DockerController { } public function startTopContainer(bool $pullImage) : void { - $this->configurationManager->aioToken = bin2hex(random_bytes(24)); + $config = $this->configurationManager->GetConfig(); + // set AIO_TOKEN + $config['AIO_TOKEN'] = bin2hex(random_bytes(24)); + $this->configurationManager->WriteConfig($config); // Stop domaincheck since apache would not be able to start otherwise $this->StopDomaincheckContainer(); @@ -236,7 +244,7 @@ readonly class DockerController { // This is a hack but no better solution was found for the meantime // Stop Collabora first to make sure it force-saves // See https://github.com/nextcloud/richdocuments/issues/3799 - if ($id === self::TOP_CONTAINER && $this->configurationManager->isCollaboraEnabled) { + if ($id === self::TOP_CONTAINER && $this->configurationManager->isCollaboraEnabled()) { $this->PerformRecursiveContainerStop('nextcloud-aio-collabora'); } @@ -247,7 +255,7 @@ readonly class DockerController { // We want to stop the Nextcloud container after 10s and not wait for the configured stop_grace_period $this->dockerActionManager->StopContainer($container, $forceStopNextcloud); } - foreach($container->dependsOn as $dependency) { + foreach($container->GetDependsOn() as $dependency) { $this->PerformRecursiveContainerStop($dependency, $forceStopNextcloud); } } @@ -269,7 +277,7 @@ readonly class DockerController { public function StartDomaincheckContainer() : void { # Don't start if domain is already set - if ($this->configurationManager->domain !== '' || $this->configurationManager->wasStartButtonClicked) { + if ($this->configurationManager->GetDomain() !== '' || $this->configurationManager->wasStartButtonClicked()) { return; } diff --git a/php/src/Data/ConfigurationManager.php b/php/src/Data/ConfigurationManager.php index 7534acda..320bc477 100644 --- a/php/src/Data/ConfigurationManager.php +++ b/php/src/Data/ConfigurationManager.php @@ -9,354 +9,61 @@ class ConfigurationManager { private array $secrets = []; - private array $config = []; - - private bool $noWrite = false; - - public string $aioToken { - get => $this->get('AIO_TOKEN', ''); - set { $this->set('AIO_TOKEN', $value); } - } - - public string $password { - get => $this->get('password', ''); - set { $this->set('password', $value); } - } - - public bool $isDockerSocketProxyEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isDockerSocketProxyEnabled', false); - set { $this->set('isDockerSocketProxyEnabled', $value); } - } - - public bool $isWhiteboardEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isWhiteboardEnabled', true); - set { $this->set('isWhiteboardEnabled', $value); } - } - - public bool $restoreExcludePreviews { - // Type-cast because old configs could have '1'/'' for this key. - get => (bool) $this->get('restore-exclude-previews', false); - set { $this->set('restore-exclude-previews', $value); } - } - - public string $selectedRestoreTime { - get => $this->get('selected-restore-time', ''); - set { $this->set('selected-restore-time', $value); } - } - - public string $backupMode { - get => $this->get('backup-mode', ''); - set { $this->set('backup-mode', $value); } - } - - public bool $instanceRestoreAttempt { - // Type-cast because old configs could have 1/'' for this key. - get => (bool) $this->get('instance_restore_attempt', false); - set { $this->set('instance_restore_attempt', $value); } - } - - public string $aioUrl { - get => $this->get('AIO_URL', ''); - set { $this->set('AIO_URL', $value); } - } - - public bool $wasStartButtonClicked { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('wasStartButtonClicked', false); - set { $this->set('wasStartButtonClicked', $value); } - } - - public string $installLatestMajor { - // Type-cast because old configs could have integers for this key. - get => (string) $this->get('install_latest_major', ''); - set { $this->set('install_latest_major', $value); } - } - - public bool $isClamavEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isClamavEnabled', false); - set { $this->set('isClamavEnabled', $value); } - } - - public bool $isOnlyofficeEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isOnlyofficeEnabled', false); - set { $this->set('isOnlyofficeEnabled', $value); } - } - - public bool $isCollaboraEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isCollaboraEnabled', true); - set { $this->set('isCollaboraEnabled', $value); } - } - - public bool $isTalkEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isTalkEnabled', true); - set { $this->set('isTalkEnabled', $value); } - } - - public bool $isTalkRecordingEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->isTalkEnabled && $this->get('isTalkRecordingEnabled', false); - set { $this->set('isTalkRecordingEnabled', $this->isTalkEnabled && $value); } - } - - public bool $isImaginaryEnabled { - // Type-cast because old configs could have 1/0 for this key. - get => (bool) $this->get('isImaginaryEnabled', true); - set { $this->set('isImaginaryEnabled', $value); } - } - - public bool $isFulltextsearchEnabled { - // 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)); } - } - - public string $domain { - get => $this->get('domain', ''); - set { $this->setDomain($value); } - } - - public string $borgBackupHostLocation { - get => $this->get('borg_backup_host_location', ''); - set { $this->set('borg_backup_host_location', $value); } - } - - public string $borgRemoteRepo { - get => $this->get('borg_remote_repo', ''); - set { $this->set('borg_remote_repo', $value); } - } - - public string $borgRestorePassword { - get => $this->get('borg_restore_password', ''); - set { $this->set('borg_restore_password', $value); } - } - - public string $apacheIpBinding { - get => $this->getEnvironmentalVariableOrConfig('APACHE_IP_BINDING', 'apache_ip_binding', ''); - set { $this->set('apache_ip_binding', $value); } - } - - /** - * @throws InvalidSettingConfigurationException - */ - public string $timezone { - get => $this->get('timezone', ''); - set { - // This throws an exception if the validation fails. - $this->validateTimezone($value); - $this->set('timezone', $value); - } - } - - /** - * @throws InvalidSettingConfigurationException - */ - public string $collaboraDictionaries { - get => $this->get('collabora_dictionaries', ''); - set { - // This throws an exception if the validation fails. - $this->validateCollaboraDictionaries($value); - $this->set('collabora_dictionaries', $value); - } - } - - /** - * @throws InvalidSettingConfigurationException - */ - public string $collaboraAdditionalOptions { - get => $this->get('collabora_additional_options', ''); - set { - // This throws an exception if the validation fails. - $this->validateCollaboraAdditionalOptions($value); - $this->set('collabora_additional_options', $value); - } - } - - public array $aioCommunityContainers { - get => explode(' ', $this->get('aio_community_containers', '')); - set { $this->set('aio_community_containers', implode(' ', $value)); } - } - - public string $turnDomain { - get => $this->get('turn_domain', ''); - set { $this->set('turn_domain', $value); } - } - - public string $apachePort { - get => $this->getEnvironmentalVariableOrConfig('APACHE_PORT', 'apache_port', '443'); - set { $this->set('apache_port', $value); } - } - - public string $talkPort { - get => $this->getEnvironmentalVariableOrConfig('TALK_PORT', 'talk_port', '3478'); - set { $this->set('talk_port', $value); } - } - - public string $nextcloudMount { - get => $this->getEnvironmentalVariableOrConfig('NEXTCLOUD_MOUNT', 'nextcloud_mount', ''); - set { $this->set('nextcloud_mount', $value); } - } - - public string $nextcloudDatadirMount { - get => $this->getEnvironmentalVariableOrConfig('NEXTCLOUD_DATADIR', 'nextcloud_datadir', 'nextcloud_aio_nextcloud_data'); - set { $this->set('nextcloud_datadir_mount', $value); } - } - - public string $nextcloudUploadLimit { - get => $this->getEnvironmentalVariableOrConfig('NEXTCLOUD_UPLOAD_LIMIT', 'nextcloud_upload_limit', '16G'); - set { $this->set('nextcloud_upload_limit', $value); } - } - - public string $nextcloudMemoryLimit { - get => $this->getEnvironmentalVariableOrConfig('NEXTCLOUD_MEMORY_LIMIT', 'nextcloud_memory_limit', '512M'); - set { $this->set('nextcloud_memory_limit', $value); } - } - - public function getApacheMaxSize() : int { - $uploadLimit = (int)rtrim($this->nextcloudUploadLimit, 'G'); - return $uploadLimit * 1024 * 1024 * 1024; - } - - public string $nextcloudMaxTime { - get => $this->getEnvironmentalVariableOrConfig('NEXTCLOUD_MAX_TIME', 'nextcloud_max_time', '3600'); - set { $this->set('nextcloud_max_time', $value); } - } - - public string $borgRetentionPolicy { - get => $this->getEnvironmentalVariableOrConfig('BORG_RETENTION_POLICY', 'borg_retention_policy', '--keep-within=7d --keep-weekly=4 --keep-monthly=6'); - set { $this->set('borg_retention_policy', $value); } - } - - public string $fulltextsearchJavaOptions { - get => $this->getEnvironmentalVariableOrConfig('FULLTEXTSEARCH_JAVA_OPTIONS', 'fulltextsearch_java_options', '-Xms512M -Xmx512M'); - set { $this->set('fulltextsearch_java_options', $value); } - } - - public string $dockerSocketPath { - get => $this->getEnvironmentalVariableOrConfig('WATCHTOWER_DOCKER_SOCKET_PATH', 'docker_socket_path', '/var/run/docker.sock'); - set { $this->set('docker_socket_path', $value); } - } - - public string $trustedCacertsDir { - get => $this->getEnvironmentalVariableOrConfig('NEXTCLOUD_TRUSTED_CACERTS_DIR', 'trusted_cacerts_dir', ''); - set { $this->set('trusted_cacerts_dir', $value); } - } - - public string $nextcloudAdditionalApks { - get => trim($this->getEnvironmentalVariableOrConfig('NEXTCLOUD_ADDITIONAL_APKS', 'nextcloud_additional_apks', 'imagemagick')); - set { $this->set('nextcloud_addtional_apks', $value); } - } - - public string $nextcloudAdditionalPhpExtensions { - get => trim($this->getEnvironmentalVariableOrConfig('NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS', 'nextcloud_additional_php_extensions', 'imagick')); - set { $this->set('nextcloud_additional_php_extensions', $value); } - } - - public bool $collaboraSeccompDisabled { - get => $this->booleanize($this->getEnvironmentalVariableOrConfig('COLLABORA_SECCOMP_DISABLED', 'collabora_seccomp_disabled', '')); - set { $this->set('collabora_seccomp_disabled', $value); } - } - - public bool $disableBackupSection { - get => $this->booleanize($this->getEnvironmentalVariableOrConfig('AIO_DISABLE_BACKUP_SECTION', 'disable_backup_section', '')); - set { $this->set('disable_backup_section', $value); } - } - - public bool $nextcloudEnableDriDevice{ - get => $this->booleanize($this->getEnvironmentalVariableOrConfig('NEXTCLOUD_ENABLE_DRI_DEVICE', 'nextcloud_enable_dri_device', '')); - set { $this->set('nextcloud_enable_dri_device', $value); } - } - - public bool $enableNvidiaGpu { - get => $this->booleanize($this->getEnvironmentalVariableOrConfig('NEXTCLOUD_ENABLE_NVIDIA_GPU', 'enable_nvidia_gpu', '')); - set { $this->set('enable_nvidia_gpu', $value); } - } - - public bool $nextcloudKeepDisabledApps { - get => $this->booleanize($this->getEnvironmentalVariableOrConfig('NEXTCLOUD_KEEP_DISABLED_APPS', 'nextcloud_keep_disabled_apps', '')); - set { $this->set('nextcloud_keep_disabled_apps', $value); } - } - - private function getConfig() : array + public function GetConfig() : array { - if ($this->config === [] && file_exists(DataConst::GetConfigFile())) + if(file_exists(DataConst::GetConfigFile())) { $configContent = (string)file_get_contents(DataConst::GetConfigFile()); - $this->config = json_decode($configContent, true, 512, JSON_THROW_ON_ERROR); + return json_decode($configContent, true, 512, JSON_THROW_ON_ERROR); } - return $this->config; + return []; } - private function get(string $key, mixed $fallbackValue = null) : mixed { - return $this->getConfig()[$key] ?? $fallbackValue; + public function GetPassword() : string { + return $this->GetConfig()['password']; } - private function set(string $key, mixed $value) : void { - $this->getConfig(); - $this->config[$key] = $value; - // Only write if this isn't called in between startTransaction() and commitTransaction(). - if ($this->noWrite !== true) { - $this->writeConfig(); - } + public function GetToken() : string { + return $this->GetConfig()['AIO_TOKEN']; } - /** - * This allows to assign multiple attributes without saving the config to disk in between. It must be - * followed by a call to commitTransaction(), which then writes all changes to disk. - */ - public function startTransaction() : void { - $this->getConfig(); - $this->noWrite = true; + public function SetPassword(string $password) : void { + $config = $this->GetConfig(); + $config['password'] = $password; + $this->WriteConfig($config); } - /** - * This allows to assign multiple attributes without saving the config to disk in between. - */ - public function commitTransaction() : void { - try { - $this->writeConfig(); - } finally { - $this->noWrite = false; - } - } - - public function getAndGenerateSecret(string $secretId) : string { + public function GetAndGenerateSecret(string $secretId) : string { if ($secretId === '') { return ''; } - $secrets = $this->get('secrets', []); - if (!isset($secrets[$secretId])) { - $secrets[$secretId] = bin2hex(random_bytes(24)); - $this->set('secrets', $secrets); + $config = $this->GetConfig(); + if(!isset($config['secrets'][$secretId])) { + $config['secrets'][$secretId] = bin2hex(random_bytes(24)); + $this->WriteConfig($config); } if ($secretId === 'BORGBACKUP_PASSWORD' && !file_exists(DataConst::GetBackupSecretFile())) { - $this->doubleSafeBackupSecret($secrets[$secretId]); + $this->DoubleSafeBackupSecret($config['secrets'][$secretId]); } - return $secrets[$secretId]; + return $config['secrets'][$secretId]; } - public function getRegisteredSecret(string $secretId) : string { + public function GetRegisteredSecret(string $secretId) : string { if ($this->secrets[$secretId]) { - return $this->getAndGenerateSecret($secretId); + return $this->GetAndGenerateSecret($secretId); } throw new \Exception("The secret " . $secretId . " was not registered. Please check if it is defined in secrets of containers.json."); } - public function registerSecret(string $secretId) : void { + public function RegisterSecret(string $secretId) : void { $this->secrets[$secretId] = true; } - private function doubleSafeBackupSecret(string $borgBackupPassword) : void { + private function DoubleSafeBackupSecret(string $borgBackupPassword) : void { file_put_contents(DataConst::GetBackupSecretFile(), $borgBackupPassword); } @@ -368,7 +75,7 @@ class ConfigurationManager } } - public function getLastBackupTime() : string { + public function GetLastBackupTime() : string { if (!file_exists(DataConst::GetBackupArchivesList())) { return ''; } @@ -393,7 +100,7 @@ class ConfigurationManager return $lastBackupTime; } - public function getBackupTimes() : array { + public function GetBackupTimes() : array { if (!file_exists(DataConst::GetBackupArchivesList())) { return []; } @@ -415,12 +122,12 @@ class ConfigurationManager return $backupTimes; } - public function getAioVersion() : string { - $path = DataConst::GetAioVersionFile(); - if ($path !== '' && file_exists($path)) { - return trim((string)file_get_contents($path)); + public function wasStartButtonClicked() : bool { + if (isset($this->GetConfig()['wasStartButtonClicked'])) { + return true; + } else { + return false; } - return ''; } private function isx64Platform() : bool { @@ -431,12 +138,157 @@ class ConfigurationManager } } + public function isClamavEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isClamavEnabled']) && $config['isClamavEnabled'] === 1) { + return true; + } else { + return false; + } + } + + public function isDockerSocketProxyEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isDockerSocketProxyEnabled']) && $config['isDockerSocketProxyEnabled'] === 1) { + return true; + } else { + return false; + } + } + + public function SetDockerSocketProxyEnabledState(int $value) : void { + $config = $this->GetConfig(); + $config['isDockerSocketProxyEnabled'] = $value; + $this->WriteConfig($config); + } + + public function isWhiteboardEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isWhiteboardEnabled']) && $config['isWhiteboardEnabled'] === 0) { + return false; + } else { + return true; + } + } + + public function SetWhiteboardEnabledState(int $value) : void { + $config = $this->GetConfig(); + $config['isWhiteboardEnabled'] = $value; + $this->WriteConfig($config); + } + + public function SetClamavEnabledState(int $value) : void { + $config = $this->GetConfig(); + $config['isClamavEnabled'] = $value; + $this->WriteConfig($config); + } + + public function isImaginaryEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isImaginaryEnabled']) && $config['isImaginaryEnabled'] === 0) { + return false; + } else { + return true; + } + } + + public function SetImaginaryEnabledState(int $value) : void { + $config = $this->GetConfig(); + $config['isImaginaryEnabled'] = $value; + $this->WriteConfig($config); + } + + public function isFulltextsearchEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isFulltextsearchEnabled']) && $config['isFulltextsearchEnabled'] === 1) { + return true; + } else { + return false; + } + } + + public function SetFulltextsearchEnabledState(int $value) : void { + // Elasticsearch does not work on kernels without seccomp anymore. See https://github.com/nextcloud/all-in-one/discussions/5768 + if ($this->isSeccompDisabled()) { + $value = 0; + } + + $config = $this->GetConfig(); + $config['isFulltextsearchEnabled'] = $value; + $this->WriteConfig($config); + } + + public function isOnlyofficeEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isOnlyofficeEnabled']) && $config['isOnlyofficeEnabled'] === 1) { + return true; + } else { + return false; + } + } + + public function SetOnlyofficeEnabledState(int $value) : void { + $config = $this->GetConfig(); + $config['isOnlyofficeEnabled'] = $value; + $this->WriteConfig($config); + } + + public function isCollaboraEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isCollaboraEnabled']) && $config['isCollaboraEnabled'] === 0) { + return false; + } else { + return true; + } + } + + public function SetCollaboraEnabledState(int $value) : void { + $config = $this->GetConfig(); + $config['isCollaboraEnabled'] = $value; + $this->WriteConfig($config); + } + + public function isTalkEnabled() : bool { + $config = $this->GetConfig(); + if (isset($config['isTalkEnabled']) && $config['isTalkEnabled'] === 0) { + return false; + } else { + return true; + } + } + + public function SetTalkEnabledState(int $value) : void { + $config = $this->GetConfig(); + $config['isTalkEnabled'] = $value; + $this->WriteConfig($config); + } + + public function isTalkRecordingEnabled() : bool { + if (!$this->isTalkEnabled()) { + return false; + } + $config = $this->GetConfig(); + if (isset($config['isTalkRecordingEnabled']) && $config['isTalkRecordingEnabled'] === 1) { + return true; + } else { + return false; + } + } + + public function SetTalkRecordingEnabledState(int $value) : void { + if (!$this->isTalkEnabled()) { + $value = 0; + } + + $config = $this->GetConfig(); + $config['isTalkRecordingEnabled'] = $value; + $this->WriteConfig($config); + } + /** * @throws InvalidSettingConfigurationException - * - * We can't turn this into a private validation method because of the second argument. */ - public function setDomain(string $domain, bool $skipDomainValidation) : void { + public function SetDomain(string $domain, bool $skipDomainValidation) : void { // Validate that at least one dot is contained if (!str_contains($domain, '.')) { throw new InvalidSettingConfigurationException("Domain must contain at least one dot!"); @@ -484,7 +336,7 @@ class ConfigurationManager } // Get the apache port - $port = $this->apachePort; + $port = $this->GetApachePort(); if (!filter_var($dnsRecordIP, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) { if ($port === '443') { @@ -503,7 +355,7 @@ class ConfigurationManager } // Get Instance ID - $instanceID = $this->getAndGenerateSecret('INSTANCE_ID'); + $instanceID = $this->GetAndGenerateSecret('INSTANCE_ID'); // set protocol if ($port !== '443') { @@ -540,36 +392,86 @@ class ConfigurationManager } } - $this->startTransaction(); // Write domain - // Don't set the domain via the attribute, or we create a loop. - $this->set('domain', $domain); + $config = $this->GetConfig(); + $config['domain'] = $domain; // Reset the borg restore password when setting the domain - $this->borgRestorePassword = ''; - $this->startTransaction(); - $this->commitTransaction(); + $config['borg_restore_password'] = ''; + $this->WriteConfig($config); } - public function getBaseDN() : string { - $domain = $this->domain; + public function GetDomain() : string { + $config = $this->GetConfig(); + if(!isset($config['domain'])) { + $config['domain'] = ''; + } + + return $config['domain']; + } + + public function GetBaseDN() : string { + $domain = $this->GetDomain(); if ($domain === "") { return ""; } return 'dc=' . implode(',dc=', explode('.', $domain)); } + public function GetBackupMode() : string { + $config = $this->GetConfig(); + if(!isset($config['backup-mode'])) { + $config['backup-mode'] = ''; + } + + return $config['backup-mode']; + } + + public function SetBackupMode(string $mode) : void { + $config = $this->GetConfig(); + $config['backup-mode'] = $mode; + $this->WriteConfig($config); + } + + public function GetSelectedRestoreTime() : string { + $config = $this->GetConfig(); + if(!isset($config['selected-restore-time'])) { + $config['selected-restore-time'] = ''; + } + + return $config['selected-restore-time']; + } + + public function GetRestoreExcludePreviews() : string { + $config = $this->GetConfig(); + if(!isset($config['restore-exclude-previews'])) { + $config['restore-exclude-previews'] = ''; + } + + return $config['restore-exclude-previews']; + } + + public function GetAIOURL() : string { + $config = $this->GetConfig(); + if(!isset($config['AIO_URL'])) { + $config['AIO_URL'] = ''; + } + + return $config['AIO_URL']; + } + /** * @throws InvalidSettingConfigurationException */ - public function setBorgLocationVars(string $location, string $repo) : void { - $this->validateBorgLocationVars($location, $repo); - $this->startTransaction(); - $this->borgBackupHostLocation = $location; - $this->borgRemoteRepo = $repo; - $this->commitTransaction(); + public function SetBorgLocationVars(string $location, string $repo) : void { + $this->ValidateBorgLocationVars($location, $repo); + + $config = $this->GetConfig(); + $config['borg_backup_host_location'] = $location; + $config['borg_remote_repo'] = $repo; + $this->WriteConfig($config); } - private function validateBorgLocationVars(string $location, string $repo) : void { + private function ValidateBorgLocationVars(string $location, string $repo) : void { if ($location === '' && $repo === '') { throw new InvalidSettingConfigurationException("Please enter a path or a remote repo url!"); } elseif ($location !== '' && $repo !== '') { @@ -590,16 +492,16 @@ class ConfigurationManager // Prevent backup to be contained in Nextcloud Datadir as this will delete the backup archive upon restore // See https://github.com/nextcloud/all-in-one/issues/6607 - if (str_starts_with($location . '/', rtrim($this->nextcloudDatadirMount, '/') . '/')) { - throw new InvalidSettingConfigurationException("The path must not be a children of or equal to NEXTCLOUD_DATADIR, which is currently set to " . $this->nextcloudDatadirMount); + if (str_starts_with($location . '/', rtrim($this->GetNextcloudDatadirMount(), '/') . '/')) { + throw new InvalidSettingConfigurationException("The path must not be a children of or equal to NEXTCLOUD_DATADIR, which is currently set to " . $this->GetNextcloudDatadirMount()); } } else { - $this->validateBorgRemoteRepo($repo); + $this->ValidateBorgRemoteRepo($repo); } } - private function validateBorgRemoteRepo(string $repo) : void { + private function ValidateBorgRemoteRepo(string $repo) : void { $commonMsg = "For valid urls, see the remote examples at https://borgbackup.readthedocs.io/en/stable/usage/general.html#repository-urls"; if ($repo === "") { // Ok, remote repo is optional @@ -610,12 +512,12 @@ class ConfigurationManager } } - public function deleteBorgBackupLocationItems() : void { + public function DeleteBorgBackupLocationItems() : void { // Delete the variables - $this->startTransaction(); - $this->borgBackupHostLocation = ''; - $this->borgRemoteRepo = ''; - $this->commitTransaction(); + $config = $this->GetConfig(); + $config['borg_backup_host_location'] = ''; + $config['borg_remote_repo'] = ''; + $this->WriteConfig($config); // Also delete the borg config file to be able to start over if (file_exists(DataConst::GetBackupKeyFile())) { @@ -628,30 +530,30 @@ class ConfigurationManager /** * @throws InvalidSettingConfigurationException */ - public function setBorgRestoreLocationVarsAndPassword(string $location, string $repo, string $password) : void { - $this->validateBorgLocationVars($location, $repo); + public function SetBorgRestoreLocationVarsAndPassword(string $location, string $repo, string $password) : void { + $this->ValidateBorgLocationVars($location, $repo); if ($password === '') { throw new InvalidSettingConfigurationException("Please enter the password!"); } - $this->startTransaction(); - $this->borgBackupHostLocation = $location; - $this->borgRemoteRepo = $repo; - $this->borgRestorePassword = $password; - $this->instanceRestoreAttempt = true; - $this->commitTransaction(); + $config = $this->GetConfig(); + $config['borg_backup_host_location'] = $location; + $config['borg_remote_repo'] = $repo; + $config['borg_restore_password'] = $password; + $config['instance_restore_attempt'] = 1; + $this->WriteConfig($config); } /** * @throws InvalidSettingConfigurationException */ - public function changeMasterPassword(string $currentPassword, string $newPassword) : void { + public function ChangeMasterPassword(string $currentPassword, string $newPassword) : void { if ($currentPassword === '') { throw new InvalidSettingConfigurationException("Please enter your current password."); } - if ($currentPassword !== $this->password) { + if ($currentPassword !== $this->GetPassword()) { throw new InvalidSettingConfigurationException("The entered current password is not correct."); } @@ -668,50 +570,89 @@ class ConfigurationManager } // All checks pass so set the password - $this->set('password', $newPassword); + $this->SetPassword($newPassword); + } + + public function GetApachePort() : string { + $envVariableName = 'APACHE_PORT'; + $configName = 'apache_port'; + $defaultValue = '443'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetTalkPort() : string { + $envVariableName = 'TALK_PORT'; + $configName = 'talk_port'; + $defaultValue = '3478'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetTurnDomain() : string { + $config = $this->GetConfig(); + if(!isset($config['turn_domain'])) { + $config['turn_domain'] = ''; + } + + return $config['turn_domain']; } /** * @throws InvalidSettingConfigurationException */ - private function writeConfig() : void { + public function WriteConfig(array $config) : void { if(!is_dir(DataConst::GetDataDirectory())) { throw new InvalidSettingConfigurationException(DataConst::GetDataDirectory() . " does not exist! Something was set up falsely!"); } - // Shouldn't happen, but as a precaution we won't write an empty config to disk. - if ($this->config === []) { - return; - } $df = disk_free_space(DataConst::GetDataDirectory()); - $content = json_encode($this->config, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR); + $content = json_encode($config, JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR); $size = strlen($content) + 10240; if ($df !== false && (int)$df < $size) { throw new InvalidSettingConfigurationException(DataConst::GetDataDirectory() . " does not have enough space for writing the config file! Not writing it back!"); } file_put_contents(DataConst::GetConfigFile(), $content); - $this->config = []; } - private function getEnvironmentalVariableOrConfig(string $envVariableName, string $configName, string $defaultValue) : string { + private function GetEnvironmentalVariableOrConfig(string $envVariableName, string $configName, string $defaultValue) : string { $envVariableOutput = getenv($envVariableName); - $configValue = $this->get($configName, ''); if ($envVariableOutput === false) { - if ($configValue === '') { - return $defaultValue; + $config = $this->GetConfig(); + if (!isset($config[$configName]) || $config[$configName] === '') { + $config[$configName] = $defaultValue; } - return $configValue; + return $config[$configName]; } - - if (file_exists(DataConst::GetConfigFile())) { - if ($envVariableOutput !== $configValue) { - $this->set($configName, $envVariableOutput); + if(file_exists(DataConst::GetConfigFile())) { + $config = $this->GetConfig(); + if (!isset($config[$configName])) { + $config[$configName] = ''; + } + if ($envVariableOutput !== $config[$configName]) { + $config[$configName] = $envVariableOutput; + $this->WriteConfig($config); } } - return $envVariableOutput; } - public function getBorgPublicKey() : string { + public function GetBorgBackupHostLocation() : string { + $config = $this->GetConfig(); + if(!isset($config['borg_backup_host_location'])) { + $config['borg_backup_host_location'] = ''; + } + + return $config['borg_backup_host_location']; + } + + public function GetBorgRemoteRepo() : string { + $config = $this->GetConfig(); + if(!isset($config['borg_remote_repo'])) { + $config['borg_remote_repo'] = ''; + } + + return $config['borg_remote_repo']; + } + + public function GetBorgPublicKey() : string { if (!file_exists(DataConst::GetBackupPublicKey())) { return ""; } @@ -719,18 +660,135 @@ class ConfigurationManager return trim((string)file_get_contents(DataConst::GetBackupPublicKey())); } - public function getCollaboraSeccompPolicy() : string { + public function GetBorgRestorePassword() : string { + $config = $this->GetConfig(); + if(!isset($config['borg_restore_password'])) { + $config['borg_restore_password'] = ''; + } + + return $config['borg_restore_password']; + } + + public function isInstanceRestoreAttempt() : bool { + $config = $this->GetConfig(); + if(!isset($config['instance_restore_attempt'])) { + $config['instance_restore_attempt'] = ''; + } + + if ($config['instance_restore_attempt'] === 1) { + return true; + } + return false; + } + + public function GetNextcloudMount() : string { + $envVariableName = 'NEXTCLOUD_MOUNT'; + $configName = 'nextcloud_mount'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetNextcloudDatadirMount() : string { + $envVariableName = 'NEXTCLOUD_DATADIR'; + $configName = 'nextcloud_datadir'; + $defaultValue = 'nextcloud_aio_nextcloud_data'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetNextcloudUploadLimit() : string { + $envVariableName = 'NEXTCLOUD_UPLOAD_LIMIT'; + $configName = 'nextcloud_upload_limit'; + $defaultValue = '16G'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetNextcloudMemoryLimit() : string { + $envVariableName = 'NEXTCLOUD_MEMORY_LIMIT'; + $configName = 'nextcloud_memory_limit'; + $defaultValue = '512M'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetApacheMaxSize() : int { + $uploadLimit = (int)rtrim($this->GetNextcloudUploadLimit(), 'G'); + return $uploadLimit * 1024 * 1024 * 1024; + } + + public function GetNextcloudMaxTime() : string { + $envVariableName = 'NEXTCLOUD_MAX_TIME'; + $configName = 'nextcloud_max_time'; + $defaultValue = '3600'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetBorgRetentionPolicy() : string { + $envVariableName = 'BORG_RETENTION_POLICY'; + $configName = 'borg_retention_policy'; + $defaultValue = '--keep-within=7d --keep-weekly=4 --keep-monthly=6'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetFulltextsearchJavaOptions() : string { + $envVariableName = 'FULLTEXTSEARCH_JAVA_OPTIONS'; + $configName = 'fulltextsearch_java_options'; + $defaultValue = '-Xms512M -Xmx512M'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetDockerSocketPath() : string { + $envVariableName = 'WATCHTOWER_DOCKER_SOCKET_PATH'; + $configName = 'docker_socket_path'; + $defaultValue = '/var/run/docker.sock'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetTrustedCacertsDir() : string { + $envVariableName = 'NEXTCLOUD_TRUSTED_CACERTS_DIR'; + $configName = 'trusted_cacerts_dir'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetNextcloudAdditionalApks() : string { + $envVariableName = 'NEXTCLOUD_ADDITIONAL_APKS'; + $configName = 'nextcloud_additional_apks'; + $defaultValue = 'imagemagick'; + return trim($this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue)); + } + + public function GetNextcloudAdditionalPhpExtensions() : string { + $envVariableName = 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS'; + $configName = 'nextcloud_additional_php_extensions'; + $defaultValue = 'imagick'; + return trim($this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue)); + } + + public function GetCollaboraSeccompPolicy() : string { $defaultString = '--o:security.seccomp='; - if (!$this->collaboraSeccompDisabled) { + if (!$this->isSeccompDisabled()) { return $defaultString . 'true'; } return $defaultString . 'false'; } + private function GetCollaboraSeccompDisabledState() : string { + $envVariableName = 'COLLABORA_SECCOMP_DISABLED'; + $configName = 'collabora_seccomp_disabled'; + $defaultValue = 'false'; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function isSeccompDisabled() : bool { + if ($this->GetCollaboraSeccompDisabledState() === 'true') { + return true; + } + return false; + } + /** * @throws InvalidSettingConfigurationException */ - public function setDailyBackupTime(string $time, bool $enableAutomaticUpdates, bool $successNotification) : void { + public function SetDailyBackupTime(string $time, bool $enableAutomaticUpdates, bool $successNotification) : void { if ($time === "") { throw new InvalidSettingConfigurationException("The daily backup time must not be empty!"); } @@ -752,7 +810,7 @@ class ConfigurationManager file_put_contents(DataConst::GetDailyBackupTimeFile(), $time); } - public function getDailyBackupTime() : string { + public function GetDailyBackupTime() : string { if (!file_exists(DataConst::GetDailyBackupTimeFile())) { return ''; } @@ -774,7 +832,7 @@ class ConfigurationManager } } - public function deleteDailyBackupTime() : void { + public function DeleteDailyBackupTime() : void { if (file_exists(DataConst::GetDailyBackupTimeFile())) { unlink(DataConst::GetDailyBackupTimeFile()); } @@ -783,7 +841,7 @@ class ConfigurationManager /** * @throws InvalidSettingConfigurationException */ - public function setAdditionalBackupDirectories(string $additionalBackupDirectories) : void { + public function SetAdditionalBackupDirectories(string $additionalBackupDirectories) : void { $additionalBackupDirectoriesArray = explode("\n", $additionalBackupDirectories); $validDirectories = ''; foreach($additionalBackupDirectoriesArray as $entry) { @@ -804,28 +862,48 @@ class ConfigurationManager } } - public function getAdditionalBackupDirectoriesString() : string { + public function shouldLatestMajorGetInstalled() : bool { + $config = $this->GetConfig(); + if(!isset($config['install_latest_major'])) { + $config['install_latest_major'] = ''; + } + return $config['install_latest_major'] !== ''; + } + + public function GetAdditionalBackupDirectoriesString() : string { if (!file_exists(DataConst::GetAdditionalBackupDirectoriesFile())) { return ''; } return (string)file_get_contents(DataConst::GetAdditionalBackupDirectoriesFile()); } - public function getAdditionalBackupDirectoriesArray() : array { - $additionalBackupDirectories = $this->getAdditionalBackupDirectoriesString(); + public function GetAdditionalBackupDirectoriesArray() : array { + $additionalBackupDirectories = $this->GetAdditionalBackupDirectoriesString(); $additionalBackupDirectoriesArray = explode("\n", $additionalBackupDirectories); $additionalBackupDirectoriesArray = array_unique($additionalBackupDirectoriesArray, SORT_REGULAR); return $additionalBackupDirectoriesArray; } public function isDailyBackupRunning() : bool { - return file_exists(DataConst::GetDailyBackupBlockFile()); + if (file_exists(DataConst::GetDailyBackupBlockFile())) { + return true; + } + return false; + } + + public function GetTimezone() : string { + $config = $this->GetConfig(); + if(!isset($config['timezone'])) { + $config['timezone'] = ''; + } + + return $config['timezone']; } /** * @throws InvalidSettingConfigurationException */ - private function validateTimezone(string $timezone) : void { + public function SetTimezone(string $timezone) : void { if ($timezone === "") { throw new InvalidSettingConfigurationException("The timezone must not be empty!"); } @@ -833,13 +911,16 @@ class ConfigurationManager if (!preg_match("#^[a-zA-Z0-9_\-\/\+]+$#", $timezone)) { throw new InvalidSettingConfigurationException("The entered timezone does not seem to be a valid timezone!"); } + + $config = $this->GetConfig(); + $config['timezone'] = $timezone; + $this->WriteConfig($config); } - /** - * Provide an extra method since our `timezone` attribute setter prevents setting an empty timezone. - */ - public function deleteTimezone() : void { - $this->set('timezone', ''); + public function DeleteTimezone() : void { + $config = $this->GetConfig(); + $config['timezone'] = ''; + $this->WriteConfig($config); } public function shouldDomainValidationBeSkipped(bool $skipDomainValidation) : bool { @@ -849,15 +930,7 @@ class ConfigurationManager return false; } - public function getApacheAdditionalNetwork() : string { - $network = getenv('APACHE_ADDITIONAL_NETWORK'); - if (is_string($network)) { - return trim($network); - } - return ''; - } - - public function getNextcloudStartupApps() : string { + public function GetNextcloudStartupApps() : string { $apps = getenv('NEXTCLOUD_STARTUP_APPS'); if (is_string($apps)) { return trim($apps); @@ -865,10 +938,19 @@ class ConfigurationManager return 'deck twofactor_totp tasks calendar contacts notes'; } + public function GetCollaboraDictionaries() : string { + $config = $this->GetConfig(); + if(!isset($config['collabora_dictionaries'])) { + $config['collabora_dictionaries'] = ''; + } + + return $config['collabora_dictionaries']; + } + /** * @throws InvalidSettingConfigurationException */ - private function validateCollaboraDictionaries(string $CollaboraDictionaries) : void { + public function SetCollaboraDictionaries(string $CollaboraDictionaries) : void { if ($CollaboraDictionaries === "") { throw new InvalidSettingConfigurationException("The dictionaries must not be empty!"); } @@ -876,19 +958,22 @@ class ConfigurationManager if (!preg_match("#^[a-zA-Z_ ]+$#", $CollaboraDictionaries)) { throw new InvalidSettingConfigurationException("The entered dictionaries do not seem to be a valid!"); } + + $config = $this->GetConfig(); + $config['collabora_dictionaries'] = $CollaboraDictionaries; + $this->WriteConfig($config); } - /** - * Provide an extra method since the corresponding attribute setter prevents setting an empty value. - */ - public function deleteCollaboraDictionaries() : void { - $this->set('collabora_dictionaries', ''); + public function DeleteCollaboraDictionaries() : void { + $config = $this->GetConfig(); + $config['collabora_dictionaries'] = ''; + $this->WriteConfig($config); } /** * @throws InvalidSettingConfigurationException */ - private function validateCollaboraAdditionalOptions(string $additionalCollaboraOptions) : void { + public function SetAdditionalCollaboraOptions(string $additionalCollaboraOptions) : void { if ($additionalCollaboraOptions === "") { throw new InvalidSettingConfigurationException("The additional options must not be empty!"); } @@ -896,19 +981,73 @@ class ConfigurationManager if (!preg_match("#^--o:#", $additionalCollaboraOptions)) { throw new InvalidSettingConfigurationException("The entered options must start with '--o:'. So the config does not seem to be a valid!"); } + + $config = $this->GetConfig(); + $config['collabora_additional_options'] = $additionalCollaboraOptions; + $this->WriteConfig($config); + } + + public function GetAdditionalCollaboraOptions() : string { + $config = $this->GetConfig(); + if(!isset($config['collabora_additional_options'])) { + $config['collabora_additional_options'] = ''; + } + + return $config['collabora_additional_options']; } public function isCollaboraSubscriptionEnabled() : bool { - return str_contains($this->collaboraAdditionalOptions, '--o:support_key='); + if (str_contains($this->GetAdditionalCollaboraOptions(), '--o:support_key=')) { + return true; + } + return false; } - /** - * Provide an extra method since the corresponding attribute setter prevents setting an empty value. - */ - public function deleteAdditionalCollaboraOptions() : void { - $this->set('collabora_additional_options', ''); + public function DeleteAdditionalCollaboraOptions() : void { + $config = $this->GetConfig(); + $config['collabora_additional_options'] = ''; + $this->WriteConfig($config); } + public function GetApacheAdditionalNetwork() : string { + $envVariableName = 'APACHE_ADDITIONAL_NETWORK'; + $configName = 'apache_additional_network'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function GetApacheIPBinding() : string { + $envVariableName = 'APACHE_IP_BINDING'; + $configName = 'apache_ip_binding'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + private function GetDisableBackupSection() : string { + $envVariableName = 'AIO_DISABLE_BACKUP_SECTION'; + $configName = 'disable_backup_section'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function isBackupSectionEnabled() : bool { + if ($this->GetDisableBackupSection() === 'true') { + return false; + } else { + return true; + } + } + + private function GetCommunityContainers() : string { + $config = $this->GetConfig(); + if(!isset($config['aio_community_containers'])) { + $config['aio_community_containers'] = ''; + } + + return $config['aio_community_containers']; + } + + public function listAvailableCommunityContainers() : array { $cc = []; $dir = scandir(DataConst::GetCommunityContainersDirectory()); @@ -944,122 +1083,55 @@ class ConfigurationManager return $cc; } - private function camelize(string $input, string $delimiter = '_') : string { - if ($input === '') { - throw new InvalidSettingConfigurationException('input cannot be empty!'); - } - if ($delimiter === '') { - $delimiter = '_'; - } - return lcfirst(implode("", array_map('ucfirst', explode($delimiter, strtolower($input))))); - + /** @return list */ + public function GetEnabledCommunityContainers(): array { + return explode(' ', $this->GetCommunityContainers()); } - public function setAioVariables(array $input) : void { - if ($input === []) { - return; - } - $this->startTransaction(); - foreach ($input as $variable) { - if (!is_string($variable) || !str_contains($variable, '=')) { - error_log("Invalid input: '$variable' is not a string or does not contain an equal sign ('=')"); - continue; - } - $keyWithValue = $this->replaceEnvPlaceholders($variable); - // Pad the result with nulls so psalm is happy (and we don't risk to run into warnings in case - // the check for an equal sign from above gets changed). - [$key, $value] = explode('=', $keyWithValue, 2) + [null, null]; - $key = $this->camelize($key); - if ($value === null) { - error_log("Invalid input: '$keyWithValue' has no value after the equal sign"); - } else if (!property_exists($this, $key)) { - error_log("Error: '$key' is not a valid configuration key (in '$keyWithValue')"); - } else { - $this->$key = $value; - } - } - $this->commitTransaction(); + public function SetEnabledCommunityContainers(array $enabledCommunityContainers) : void { + $config = $this->GetConfig(); + $config['aio_community_containers'] = implode(' ', $enabledCommunityContainers); + $this->WriteConfig($config); } - // - // Replaces placeholders in $envValue with their values. - // E.g. "%NC_DOMAIN%:%APACHE_PORT" becomes "my.nextcloud.com:11000" - public function replaceEnvPlaceholders(string $envValue): string { - // $pattern breaks down as: - // % - matches a literal percent sign - // ([^%]+) - capture group that matches one or more characters that are NOT percent signs - // % - matches the closing percent sign - // - // Assumes literal percent signs are always matched and there is no - // escaping. - $pattern = '/%([^%]+)%/'; - $matchCount = preg_match_all($pattern, $envValue, $matches); + private function GetEnabledDriDevice() : string { + $envVariableName = 'NEXTCLOUD_ENABLE_DRI_DEVICE'; + $configName = 'nextcloud_enable_dri_device'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } - if ($matchCount === 0) { - return $envValue; + public function isDriDeviceEnabled() : bool { + if ($this->GetEnabledDriDevice() === 'true') { + return true; + } else { + return false; } - - $placeholders = $matches[0]; // ["%PLACEHOLDER1%", "%PLACEHOLDER2%", ...] - $placeholderNames = $matches[1]; // ["PLACEHOLDER1", "PLACEHOLDER2", ...] - $placeholderPatterns = array_map(static fn(string $p) => '/' . preg_quote($p) . '/', $placeholders); // ["/%PLACEHOLDER1%/", ...] - $placeholderValues = array_map($this->getPlaceholderValue(...), $placeholderNames); // ["val1", "val2"] - // Guaranteed to be non-null because we found the placeholders in the preg_match_all. - return (string) preg_replace($placeholderPatterns, $placeholderValues, $envValue); } - private function getPlaceholderValue(string $placeholder) : string { - return match ($placeholder) { - 'NC_DOMAIN' => $this->domain, - 'NC_BASE_DN' => $this->getBaseDN(), - 'AIO_TOKEN' => $this->aioToken, - 'BORGBACKUP_REMOTE_REPO' => $this->borgRemoteRepo, - 'BORGBACKUP_MODE' => $this->backupMode, - 'AIO_URL' => $this->aioUrl, - 'SELECTED_RESTORE_TIME' => $this->selectedRestoreTime, - 'RESTORE_EXCLUDE_PREVIEWS' => $this->restoreExcludePreviews ? '1' : '', - 'APACHE_PORT' => $this->apachePort, - 'APACHE_IP_BINDING' => $this->apacheIpBinding, - 'TALK_PORT' => $this->talkPort, - 'TURN_DOMAIN' => $this->turnDomain, - 'NEXTCLOUD_MOUNT' => $this->nextcloudMount, - 'BACKUP_RESTORE_PASSWORD' => $this->borgRestorePassword, - 'CLAMAV_ENABLED' => $this->isClamavEnabled ? 'yes' : '', - 'TALK_RECORDING_ENABLED' => $this->isTalkRecordingEnabled ? 'yes' : '', - 'ONLYOFFICE_ENABLED' => $this->isOnlyofficeEnabled ? 'yes' : '', - 'COLLABORA_ENABLED' => $this->isCollaboraEnabled ? 'yes' : '', - 'TALK_ENABLED' => $this->isTalkEnabled ? 'yes' : '', - 'UPDATE_NEXTCLOUD_APPS' => ($this->isDailyBackupRunning() && $this->areAutomaticUpdatesEnabled()) ? 'yes' : '', - 'TIMEZONE' => $this->timezone === '' ? 'Etc/UTC' : $this->timezone, - 'COLLABORA_DICTIONARIES' => $this->collaboraDictionaries === '' ? 'de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru' : $this->collaboraDictionaries, - 'IMAGINARY_ENABLED' => $this->isImaginaryEnabled ? 'yes' : '', - 'FULLTEXTSEARCH_ENABLED' => $this->isFulltextsearchEnabled ? 'yes' : '', - 'DOCKER_SOCKET_PROXY_ENABLED' => $this->isDockerSocketProxyEnabled ? 'yes' : '', - 'NEXTCLOUD_UPLOAD_LIMIT' => $this->nextcloudUploadLimit, - 'NEXTCLOUD_MEMORY_LIMIT' => $this->nextcloudMemoryLimit, - 'NEXTCLOUD_MAX_TIME' => $this->nextcloudMaxTime, - 'BORG_RETENTION_POLICY' => $this->borgRetentionPolicy, - 'FULLTEXTSEARCH_JAVA_OPTIONS' => $this->fulltextsearchJavaOptions, - 'NEXTCLOUD_TRUSTED_CACERTS_DIR' => $this->trustedCacertsDir, - 'ADDITIONAL_DIRECTORIES_BACKUP' => $this->getAdditionalBackupDirectoriesString() !== '' ? 'yes' : '', - 'BORGBACKUP_HOST_LOCATION' => $this->borgBackupHostLocation, - 'APACHE_MAX_SIZE' => (string)($this->getApacheMaxSize()), - 'COLLABORA_SECCOMP_POLICY' => $this->getCollaboraSeccompPolicy(), - 'NEXTCLOUD_STARTUP_APPS' => $this->getNextcloudStartupApps(), - 'NEXTCLOUD_ADDITIONAL_APKS' => $this->nextcloudAdditionalApks, - 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS' => $this->nextcloudAdditionalPhpExtensions, - 'INSTALL_LATEST_MAJOR' => $this->installLatestMajor ? 'yes' : '', - 'REMOVE_DISABLED_APPS' => $this->nextcloudKeepDisabledApps ? '' : 'yes', - // Allow to get local ip-address of database container which allows to talk to it even in host mode (the container that requires this needs to be started first then) - 'AIO_DATABASE_HOST' => gethostbyname('nextcloud-aio-database'), - // Allow to get local ip-address of caddy container and add it to trusted proxies automatically - 'CADDY_IP_ADDRESS' => in_array('caddy', $this->aioCommunityContainers, true) ? gethostbyname('nextcloud-aio-caddy') : '', - 'WHITEBOARD_ENABLED' => $this->isWhiteboardEnabled ? 'yes' : '', - 'AIO_VERSION' => $this->getAioVersion(), - default => $this->getRegisteredSecret($placeholder), - }; + private function GetEnabledNvidiaGpu() : string { + $envVariableName = 'NEXTCLOUD_ENABLE_NVIDIA_GPU'; + $configName = 'enable_nvidia_gpu'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); } - - private function booleanize(mixed $value) : bool { - return in_array($value, [true, 'true'], true); + + public function isNvidiaGpuEnabled() : bool { + return $this->GetEnabledNvidiaGpu() === 'true'; + } + + private function GetKeepDisabledApps() : string { + $envVariableName = 'NEXTCLOUD_KEEP_DISABLED_APPS'; + $configName = 'nextcloud_keep_disabled_apps'; + $defaultValue = ''; + return $this->GetEnvironmentalVariableOrConfig($envVariableName, $configName, $defaultValue); + } + + public function shouldDisabledAppsGetRemoved() : bool { + if ($this->GetKeepDisabledApps() === 'true') { + return false; + } else { + return true; + } } } diff --git a/php/src/Data/DataConst.php b/php/src/Data/DataConst.php index 9272e3d4..9111a98a 100644 --- a/php/src/Data/DataConst.php +++ b/php/src/Data/DataConst.php @@ -66,8 +66,4 @@ class DataConst { public static function GetContainersDefinitionPath() : string { return (string)realpath(__DIR__ . '/../../containers.json'); } - - public static function GetAioVersionFile() : string { - return (string)realpath(__DIR__ . '/../../templates/includes/aio-version.twig'); - } } diff --git a/php/src/Data/Setup.php b/php/src/Data/Setup.php index e409eef8..f8f43e4b 100644 --- a/php/src/Data/Setup.php +++ b/php/src/Data/Setup.php @@ -17,7 +17,7 @@ readonly class Setup { } $password = $this->passwordGenerator->GeneratePassword(8); - $this->configurationManager->password = $password; + $this->configurationManager->SetPassword($password); return $password; } diff --git a/php/src/Docker/DockerActionManager.php b/php/src/Docker/DockerActionManager.php index 86b36619..9e8a8ff2 100644 --- a/php/src/Docker/DockerActionManager.php +++ b/php/src/Docker/DockerActionManager.php @@ -36,15 +36,15 @@ readonly class DockerActionManager { } private function BuildImageName(Container $container): string { - $tag = $container->imageTag; + $tag = $container->GetImageTag(); if ($tag === '%AIO_CHANNEL%') { $tag = $this->GetCurrentChannel(); } - return $container->containerName . ':' . $tag; + return $container->GetContainerName() . ':' . $tag; } public function GetContainerRunningState(Container $container): ContainerState { - $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->identifier))); + $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->GetIdentifier()))); try { $response = $this->guzzleClient->get($url); } catch (RequestException $e) { @@ -64,7 +64,7 @@ readonly class DockerActionManager { } public function GetContainerRestartingState(Container $container): ContainerState { - $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->identifier))); + $url = $this->BuildApiUrl(sprintf('containers/%s/json', urlencode($container->GetIdentifier()))); try { $response = $this->guzzleClient->get($url); } catch (RequestException $e) { @@ -84,16 +84,16 @@ readonly class DockerActionManager { } public function GetContainerUpdateState(Container $container): VersionState { - $tag = $container->imageTag; + $tag = $container->GetImageTag(); if ($tag === '%AIO_CHANNEL%') { $tag = $this->GetCurrentChannel(); } - $runningDigests = $this->GetRepoDigestsOfContainer($container->identifier); + $runningDigests = $this->GetRepoDigestsOfContainer($container->GetIdentifier()); if ($runningDigests === null) { return VersionState::Different; } - $remoteDigest = $this->GetLatestDigestOfTag($container->containerName, $tag); + $remoteDigest = $this->GetLatestDigestOfTag($container->GetContainerName(), $tag); if ($remoteDigest === null) { return VersionState::Equal; } @@ -112,12 +112,12 @@ readonly class DockerActionManager { return $runningState; } - $containerName = $container->identifier; - $internalPort = $container->internalPorts; + $containerName = $container->GetIdentifier(); + $internalPort = $container->GetInternalPort(); if ($internalPort === '%APACHE_PORT%') { - $internalPort = $this->configurationManager->apachePort; + $internalPort = $this->configurationManager->GetApachePort(); } elseif ($internalPort === '%TALK_PORT%') { - $internalPort = $this->configurationManager->talkPort; + $internalPort = $this->configurationManager->GetTalkPort(); } if ($internalPort !== "" && $internalPort !== 'host') { @@ -134,7 +134,7 @@ readonly class DockerActionManager { } public function DeleteContainer(Container $container): void { - $url = $this->BuildApiUrl(sprintf('containers/%s?v=true', urlencode($container->identifier))); + $url = $this->BuildApiUrl(sprintf('containers/%s?v=true', urlencode($container->GetIdentifier()))); try { $this->guzzleClient->delete($url); } catch (RequestException $e) { @@ -166,17 +166,17 @@ readonly class DockerActionManager { } public function StartContainer(Container $container): void { - $url = $this->BuildApiUrl(sprintf('containers/%s/start', urlencode($container->identifier))); + $url = $this->BuildApiUrl(sprintf('containers/%s/start', urlencode($container->GetIdentifier()))); try { $this->guzzleClient->post($url); } catch (RequestException $e) { - throw new \Exception("Could not start container " . $container->identifier . ": " . $e->getResponse()?->getBody()->getContents()); + throw new \Exception("Could not start container " . $container->GetIdentifier() . ": " . $e->getResponse()?->getBody()->getContents()); } } public function CreateVolumes(Container $container): void { $url = $this->BuildApiUrl('volumes/create'); - foreach ($container->volumes->GetVolumes() as $volume) { + foreach ($container->GetVolumes()->GetVolumes() as $volume) { $forbiddenChars = [ '/', ]; @@ -202,10 +202,10 @@ readonly class DockerActionManager { public function CreateContainer(Container $container): void { $volumes = []; - foreach ($container->volumes->GetVolumes() as $volume) { + foreach ($container->GetVolumes()->GetVolumes() as $volume) { // // NEXTCLOUD_MOUNT gets added via bind-mount later on - // if ($container->identifier === 'nextcloud-aio-nextcloud') { - // if ($volume->name === $this->configurationManager->nextcloudMount) { + // if ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') { + // if ($volume->name === $this->configurationManager->GetNextcloudMount()) { // continue; // } // } @@ -228,38 +228,46 @@ readonly class DockerActionManager { $requestBody['HostConfig']['Binds'] = $volumes; } - $this->configurationManager->setAioVariables($container->aioVariables->GetVariables()); + $aioVariables = $container->GetAioVariables()->GetVariables(); + foreach ($aioVariables as $variable) { + $config = $this->configurationManager->GetConfig(); + $variable = $this->replaceEnvPlaceholders($variable); + $variableArray = explode('=', $variable); + $config[$variableArray[0]] = $variableArray[1]; + $this->configurationManager->WriteConfig($config); + sleep(1); + } - $envs = $container->containerEnvironmentVariables->GetVariables(); + $envs = $container->GetEnvironmentVariables()->GetVariables(); // Special thing for the nextcloud container - if ($container->identifier === 'nextcloud-aio-nextcloud') { + if ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') { $envs[] = $this->GetAllNextcloudExecCommands(); } foreach ($envs as $key => $env) { - $envs[$key] = $this->configurationManager->replaceEnvPlaceholders($env); + $envs[$key] = $this->replaceEnvPlaceholders($env); } if (count($envs) > 0) { $requestBody['Env'] = $envs; } - $requestBody['HostConfig']['RestartPolicy']['Name'] = $container->restartPolicy; + $requestBody['HostConfig']['RestartPolicy']['Name'] = $container->GetRestartPolicy(); - $requestBody['HostConfig']['ReadonlyRootfs'] = $container->readOnlyRootFs; + $requestBody['HostConfig']['ReadonlyRootfs'] = $container->GetReadOnlySetting(); $exposedPorts = []; - if ($container->internalPorts !== 'host') { - foreach ($container->ports->GetPorts() as $value) { + if ($container->GetInternalPort() !== 'host') { + foreach ($container->GetPorts()->GetPorts() as $value) { $port = $value->port; $protocol = $value->protocol; if ($port === '%APACHE_PORT%') { - $port = $this->configurationManager->apachePort; + $port = $this->configurationManager->GetApachePort(); // Do not expose udp if AIO is in reverse proxy mode if ($port !== '443' && $protocol === 'udp') { continue; } } else if ($port === '%TALK_PORT%') { - $port = $this->configurationManager->talkPort; + $port = $this->configurationManager->GetTalkPort(); } $portWithProtocol = $port . '/' . $protocol; $exposedPorts[$portWithProtocol] = null; @@ -271,17 +279,17 @@ readonly class DockerActionManager { if (count($exposedPorts) > 0) { $requestBody['ExposedPorts'] = $exposedPorts; - foreach ($container->ports->GetPorts() as $value) { + foreach ($container->GetPorts()->GetPorts() as $value) { $port = $value->port; $protocol = $value->protocol; if ($port === '%APACHE_PORT%') { - $port = $this->configurationManager->apachePort; + $port = $this->configurationManager->GetApachePort(); // Do not expose udp if AIO is in reverse proxy mode if ($port !== '443' && $protocol === 'udp') { continue; } } else if ($port === '%TALK_PORT%') { - $port = $this->configurationManager->talkPort; + $port = $this->configurationManager->GetTalkPort(); // Skip publishing talk tcp port if it is set to 443 if ($port === '443' && $protocol === 'tcp') { continue; @@ -289,7 +297,7 @@ readonly class DockerActionManager { } $ipBinding = $value->ipBinding; if ($ipBinding === '%APACHE_IP_BINDING%') { - $ipBinding = $this->configurationManager->apacheIpBinding; + $ipBinding = $this->configurationManager->GetApacheIPBinding(); // Do not expose if AIO is in internal network mode if ($ipBinding === '@INTERNAL') { continue; @@ -306,8 +314,8 @@ readonly class DockerActionManager { } $devices = []; - foreach ($container->devices as $device) { - if ($device === '/dev/dri' && !$this->configurationManager->nextcloudEnableDriDevice) { + foreach ($container->GetDevices() as $device) { + if ($device === '/dev/dri' && !$this->configurationManager->isDriDeviceEnabled()) { continue; } $devices[] = ["PathOnHost" => $device, "PathInContainer" => $device, "CgroupPermissions" => "rwm"]; @@ -317,7 +325,7 @@ readonly class DockerActionManager { $requestBody['HostConfig']['Devices'] = $devices; } - if ($container->enableNvidiaGpu && $this->configurationManager->enableNvidiaGpu) { + if ($container->isNvidiaGpuEnabled() && $this->configurationManager->isNvidiaGpuEnabled()) { $requestBody['HostConfig']['Runtime'] = 'nvidia'; $requestBody['HostConfig']['DeviceRequests'] = [ [ @@ -328,13 +336,13 @@ readonly class DockerActionManager { ]; } - $shmSize = $container->shmSize; + $shmSize = $container->GetShmSize(); if ($shmSize > 0) { $requestBody['HostConfig']['ShmSize'] = $shmSize; } $tmpfs = []; - foreach ($container->tmpfs as $tmp) { + foreach ($container->GetTmpfs() as $tmp) { $mode = ""; if (str_contains($tmp, ':')) { $mode = explode(':', $tmp)[1]; @@ -346,14 +354,9 @@ readonly class DockerActionManager { $requestBody['HostConfig']['Tmpfs'] = $tmpfs; } - $requestBody['HostConfig']['Init'] = $container->init; + $requestBody['HostConfig']['Init'] = $container->GetInit(); - $maxShutDownTime = $container->maxShutdownTime; - if ($maxShutDownTime > 0) { - $requestBody['StopTimeout'] = $maxShutDownTime; - } - - $capAdds = $container->capAdd; + $capAdds = $container->GetCapAdds(); if (count($capAdds) > 0) { $requestBody['HostConfig']['CapAdd'] = $capAdds; } @@ -365,14 +368,14 @@ readonly class DockerActionManager { // Disable SELinux for AIO containers so that it does not break them $requestBody['HostConfig']['SecurityOpt'] = ["label:disable"]; - if ($container->apparmorUnconfined) { + if ($container->isApparmorUnconfined()) { $requestBody['HostConfig']['SecurityOpt'] = ["apparmor:unconfined", "label:disable"]; } $mounts = []; // Special things for the backup container which should not be exposed in the containers.json - if (str_starts_with($container->identifier, 'nextcloud-aio-borgbackup')) { + if (str_starts_with($container->GetIdentifier(), 'nextcloud-aio-borgbackup')) { // Additional backup directories foreach ($this->getAllBackupVolumes() as $additionalBackupVolumes) { if ($additionalBackupVolumes !== '') { @@ -381,9 +384,9 @@ readonly class DockerActionManager { } // Make volumes read only in case of borgbackup container. The viewer makes them writeable - $isReadOnly = $container->identifier === 'nextcloud-aio-borgbackup'; + $isReadOnly = $container->GetIdentifier() === 'nextcloud-aio-borgbackup'; - foreach ($this->configurationManager->getAdditionalBackupDirectoriesArray() as $additionalBackupDirectories) { + foreach ($this->configurationManager->GetAdditionalBackupDirectoriesArray() as $additionalBackupDirectories) { if ($additionalBackupDirectories !== '') { if (!str_starts_with($additionalBackupDirectories, '/')) { $mounts[] = ["Type" => "volume", "Source" => $additionalBackupDirectories, "Target" => "/docker_volumes/" . $additionalBackupDirectories, "ReadOnly" => $isReadOnly]; @@ -394,39 +397,33 @@ readonly class DockerActionManager { } // Special things for the talk container which should not be exposed in the containers.json - } elseif ($container->identifier === 'nextcloud-aio-talk') { + } elseif ($container->GetIdentifier() === 'nextcloud-aio-talk') { // This is needed due to a bug in libwebsockets used in Janus which cannot handle unlimited ulimits $requestBody['HostConfig']['Ulimits'] = [["Name" => "nofile", "Hard" => 200000, "Soft" => 200000]]; // // Special things for the nextcloud container which should not be exposed in the containers.json - // } elseif ($container->identifier === 'nextcloud-aio-nextcloud') { - // foreach ($container->volumes->GetVolumes() as $volume) { - // if ($volume->name !== $this->configurationManager->nextcloudMount) { + // } elseif ($container->GetIdentifier() === 'nextcloud-aio-nextcloud') { + // foreach ($container->GetVolumes()->GetVolumes() as $volume) { + // if ($volume->name !== $this->configurationManager->GetNextcloudMount()) { // continue; // } // $mounts[] = ["Type" => "bind", "Source" => $volume->name, "Target" => $volume->mountPoint, "ReadOnly" => !$volume->isWritable, "BindOptions" => [ "Propagation" => "rshared"]]; // } // Special things for the caddy community container - } elseif ($container->identifier === 'nextcloud-aio-caddy') { + } elseif ($container->GetIdentifier() === 'nextcloud-aio-caddy') { $requestBody['HostConfig']['ExtraHosts'] = ['host.docker.internal:host-gateway']; // Special things for the collabora container which should not be exposed in the containers.json - } elseif ($container->identifier === 'nextcloud-aio-collabora') { - if (!$this->configurationManager->collaboraSeccompDisabled) { + } elseif ($container->GetIdentifier() === 'nextcloud-aio-collabora') { + if (!$this->configurationManager->isSeccompDisabled()) { // Load reference seccomp profile for collabora $seccompProfile = (string)file_get_contents(DataConst::GetCollaboraSeccompProfilePath()); $requestBody['HostConfig']['SecurityOpt'] = ["label:disable", "seccomp=$seccompProfile"]; } // Additional Collabora options - if ($this->configurationManager->collaboraAdditionalOptions !== '') { - // Split the list of Collabora options, which are stored as a string but must be assigned as an array. - // To avoid problems with whitespace or dashes in option arguments we use a regular expression - // that splits the string at every position where a whitespace is followed by '--o:'. - // The leading whitespace is removed in the split but the following characters are not. - // Example: "--o:example_config1='some thing' --o:example_config2=something-else" -> ["--o:example_config1='some thing'", "--o:example_config2=something-else"] - $regEx = '/\s+(?=--o:)/'; - $requestBody['Cmd'] = preg_split($regEx, rtrim($this->configurationManager->collaboraAdditionalOptions)); + if ($this->configurationManager->GetAdditionalCollaboraOptions() !== '') { + $requestBody['Cmd'] = [$this->configurationManager->GetAdditionalCollaboraOptions()]; } } @@ -437,12 +434,12 @@ readonly class DockerActionManager { // All AIO-managed containers should not be updated externally via watchtower but gracefully by AIO's backup and update feature. // Also DIUN should not send update notifications. See https://crazymax.dev/diun/providers/docker/#docker-labels // Additionally set a default org.label-schema.vendor and com.docker.compose.project - $requestBody['Labels'] = ["com.centurylinklabs.watchtower.enable" => "false", "wud.watch" => "false", "diun.enable" => "false", "org.label-schema.vendor" => "Nextcloud", "com.docker.compose.project" => "nextcloud-aio"]; + $requestBody['Labels'] = ["com.centurylinklabs.watchtower.enable" => "false", "diun.enable" => "false", "org.label-schema.vendor" => "Nextcloud", "com.docker.compose.project" => "nextcloud-aio"]; // Containers should have a fixed host name. See https://github.com/nextcloud/all-in-one/discussions/6589 - $requestBody['Hostname'] = $container->identifier; + $requestBody['Hostname'] = $container->GetIdentifier(); - $url = $this->BuildApiUrl('containers/create?name=' . $container->identifier); + $url = $this->BuildApiUrl('containers/create?name=' . $container->GetIdentifier()); try { $this->guzzleClient->request( 'POST', @@ -452,18 +449,18 @@ readonly class DockerActionManager { ] ); } catch (RequestException $e) { - throw new \Exception("Could not create container " . $container->identifier . ": " . $e->getResponse()?->getBody()->getContents()); + throw new \Exception("Could not create container " . $container->GetIdentifier() . ": " . $e->getResponse()?->getBody()->getContents()); } } public function isRegistryReachable(Container $container): bool { - $tag = $container->imageTag; + $tag = $container->GetImageTag(); if ($tag === '%AIO_CHANNEL%') { $tag = $this->GetCurrentChannel(); } - $remoteDigest = $this->GetLatestDigestOfTag($container->containerName, $tag); + $remoteDigest = $this->GetLatestDigestOfTag($container->GetContainerName(), $tag); if ($remoteDigest === null) { return false; @@ -475,7 +472,7 @@ readonly class DockerActionManager { 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 ($container->GetIdentifier() === 'nextcloud-aio-database') { if ($this->GetDatabasecontainerExitCode() > 0) { $pullImage = false; error_log('Not pulling the latest database image because the container was not correctly shut down.'); @@ -487,7 +484,7 @@ readonly class DockerActionManager { if ($pullImage) { if (!$this->isRegistryReachable($container)) { $pullImage = false; - error_log('Not pulling the ' . $container->containerName . ' image for the ' . $container->identifier . ' container because the registry does not seem to be reachable.'); + error_log('Not pulling the ' . $container->GetContainerName() . ' image for the ' . $container->GetIdentifier() . ' container because the registry does not seem to be reachable.'); } } @@ -506,28 +503,94 @@ readonly class DockerActionManager { } catch (\Throwable $e) { $imageIsThere = false; } - - $maxRetries = 3; - for ($attempt = 1; $attempt <= $maxRetries; $attempt++) { - try { - $this->guzzleClient->post($url); - break; - } catch (RequestException $e) { - $message = "Could not pull image " . $imageName . " (attempt $attempt/$maxRetries): " . $e->getResponse()?->getBody()->getContents(); - if ($attempt === $maxRetries) { - if ($imageIsThere === false) { - throw new \Exception($message); - } else { - error_log($message); - } - } else { - error_log($message . ' Retrying...'); - sleep(1); - } + try { + $this->guzzleClient->post($url); + } catch (RequestException $e) { + $message = "Could not pull image " . $imageName . ": " . $e->getResponse()?->getBody()->getContents(); + if ($imageIsThere === false) { + throw new \Exception($message); + } else { + error_log($message); } } } + // Replaces placeholders in $envValue with their values. + // E.g. "%NC_DOMAIN%:%APACHE_PORT" becomes "my.nextcloud.com:11000" + private function replaceEnvPlaceholders(string $envValue): string { + // $pattern breaks down as: + // % - matches a literal percent sign + // ([^%]+) - capture group that matches one or more characters that are NOT percent signs + // % - matches the closing percent sign + // + // Assumes literal percent signs are always matched and there is no + // escaping. + $pattern = '/%([^%]+)%/'; + $matchCount = preg_match_all($pattern, $envValue, $matches); + + if ($matchCount === 0) { + return $envValue; + } + + $placeholders = $matches[0]; // ["%PLACEHOLDER1%", "%PLACEHOLDER2%", ...] + $placeholderNames = $matches[1]; // ["PLACEHOLDER1", "PLACEHOLDER2", ...] + $placeholderPatterns = array_map(static fn(string $p) => '/' . preg_quote($p) . '/', $placeholders); // ["/%PLACEHOLDER1%/", ...] + $placeholderValues = array_map($this->getPlaceholderValue(...), $placeholderNames); // ["val1", "val2"] + // Guaranteed to be non-null because we found the placeholders in the preg_match_all. + return (string) preg_replace($placeholderPatterns, $placeholderValues, $envValue); + } + + private function getPlaceholderValue(string $placeholder) : string { + return match ($placeholder) { + 'NC_DOMAIN' => $this->configurationManager->GetDomain(), + 'NC_BASE_DN' => $this->configurationManager->GetBaseDN(), + 'AIO_TOKEN' => $this->configurationManager->GetToken(), + 'BORGBACKUP_REMOTE_REPO' => $this->configurationManager->GetBorgRemoteRepo(), + 'BORGBACKUP_MODE' => $this->configurationManager->GetBackupMode(), + 'AIO_URL' => $this->configurationManager->GetAIOURL(), + 'SELECTED_RESTORE_TIME' => $this->configurationManager->GetSelectedRestoreTime(), + 'RESTORE_EXCLUDE_PREVIEWS' => $this->configurationManager->GetRestoreExcludePreviews(), + 'APACHE_PORT' => $this->configurationManager->GetApachePort(), + 'APACHE_IP_BINDING' => $this->configurationManager->GetApacheIPBinding(), + 'TALK_PORT' => $this->configurationManager->GetTalkPort(), + 'TURN_DOMAIN' => $this->configurationManager->GetTurnDomain(), + 'NEXTCLOUD_MOUNT' => $this->configurationManager->GetNextcloudMount(), + 'BACKUP_RESTORE_PASSWORD' => $this->configurationManager->GetBorgRestorePassword(), + 'CLAMAV_ENABLED' => $this->configurationManager->isClamavEnabled() ? 'yes' : '', + 'TALK_RECORDING_ENABLED' => $this->configurationManager->isTalkRecordingEnabled() ? 'yes' : '', + 'ONLYOFFICE_ENABLED' => $this->configurationManager->isOnlyofficeEnabled() ? 'yes' : '', + 'COLLABORA_ENABLED' => $this->configurationManager->isCollaboraEnabled() ? 'yes' : '', + 'TALK_ENABLED' => $this->configurationManager->isTalkEnabled() ? 'yes' : '', + 'UPDATE_NEXTCLOUD_APPS' => ($this->configurationManager->isDailyBackupRunning() && $this->configurationManager->areAutomaticUpdatesEnabled()) ? 'yes' : '', + 'TIMEZONE' => $this->configurationManager->GetTimezone() === '' ? 'Etc/UTC' : $this->configurationManager->GetTimezone(), + 'COLLABORA_DICTIONARIES' => $this->configurationManager->GetCollaboraDictionaries() === '' ? 'de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru' : $this->configurationManager->GetCollaboraDictionaries(), + 'IMAGINARY_ENABLED' => $this->configurationManager->isImaginaryEnabled() ? 'yes' : '', + 'FULLTEXTSEARCH_ENABLED' => $this->configurationManager->isFulltextsearchEnabled() ? 'yes' : '', + 'DOCKER_SOCKET_PROXY_ENABLED' => $this->configurationManager->isDockerSocketProxyEnabled() ? 'yes' : '', + 'NEXTCLOUD_UPLOAD_LIMIT' => $this->configurationManager->GetNextcloudUploadLimit(), + 'NEXTCLOUD_MEMORY_LIMIT' => $this->configurationManager->GetNextcloudMemoryLimit(), + 'NEXTCLOUD_MAX_TIME' => $this->configurationManager->GetNextcloudMaxTime(), + 'BORG_RETENTION_POLICY' => $this->configurationManager->GetBorgRetentionPolicy(), + 'FULLTEXTSEARCH_JAVA_OPTIONS' => $this->configurationManager->GetFulltextsearchJavaOptions(), + 'NEXTCLOUD_TRUSTED_CACERTS_DIR' => $this->configurationManager->GetTrustedCacertsDir(), + 'ADDITIONAL_DIRECTORIES_BACKUP' => $this->configurationManager->GetAdditionalBackupDirectoriesString() !== '' ? 'yes' : '', + 'BORGBACKUP_HOST_LOCATION' => $this->configurationManager->GetBorgBackupHostLocation(), + 'APACHE_MAX_SIZE' => (string)($this->configurationManager->GetApacheMaxSize()), + 'COLLABORA_SECCOMP_POLICY' => $this->configurationManager->GetCollaboraSeccompPolicy(), + 'NEXTCLOUD_STARTUP_APPS' => $this->configurationManager->GetNextcloudStartupApps(), + 'NEXTCLOUD_ADDITIONAL_APKS' => $this->configurationManager->GetNextcloudAdditionalApks(), + 'NEXTCLOUD_ADDITIONAL_PHP_EXTENSIONS' => $this->configurationManager->GetNextcloudAdditionalPhpExtensions(), + 'INSTALL_LATEST_MAJOR' => $this->configurationManager->shouldLatestMajorGetInstalled() ? 'yes' : '', + 'REMOVE_DISABLED_APPS' => $this->configurationManager->shouldDisabledAppsGetRemoved() ? 'yes' : '', + // Allow to get local ip-address of database container which allows to talk to it even in host mode (the container that requires this needs to be started first then) + 'AIO_DATABASE_HOST' => gethostbyname('nextcloud-aio-database'), + // Allow to get local ip-address of caddy container and add it to trusted proxies automatically + 'CADDY_IP_ADDRESS' => in_array('caddy', $this->configurationManager->GetEnabledCommunityContainers(), true) ? gethostbyname('nextcloud-aio-caddy') : '', + 'WHITEBOARD_ENABLED' => $this->configurationManager->isWhiteboardEnabled() ? 'yes' : '', + default => $this->configurationManager->GetRegisteredSecret($placeholder), + }; + } + private function isContainerUpdateAvailable(string $id): string { $container = $this->containerDefinitionFetcher->GetContainerById($id); @@ -535,7 +598,7 @@ readonly class DockerActionManager { if ($container->GetUpdateState() === VersionState::Different) { $updateAvailable = '1'; } - foreach ($container->dependsOn as $dependency) { + foreach ($container->GetDependsOn() as $dependency) { $updateAvailable .= $this->isContainerUpdateAvailable($dependency); } return $updateAvailable; @@ -543,7 +606,7 @@ readonly class DockerActionManager { public function isAnyUpdateAvailable(): bool { // return early if instance is not installed - if (!$this->configurationManager->wasStartButtonClicked) { + if (!$this->configurationManager->wasStartButtonClicked()) { return false; } $id = 'nextcloud-aio-apache'; @@ -559,10 +622,10 @@ readonly class DockerActionManager { $container = $this->containerDefinitionFetcher->GetContainerById($id); $backupVolumes = ''; - foreach ($container->backupVolumes as $backupVolume) { + foreach ($container->GetBackupVolumes() as $backupVolume) { $backupVolumes .= $backupVolume . ' '; } - foreach ($container->dependsOn as $dependency) { + foreach ($container->GetDependsOn() as $dependency) { $backupVolumes .= $this->getBackupVolumes($dependency); } return $backupVolumes; @@ -578,10 +641,10 @@ readonly class DockerActionManager { $container = $this->containerDefinitionFetcher->GetContainerById($id); $nextcloudExecCommands = ''; - foreach ($container->nextcloudExecCommands as $execCommand) { + foreach ($container->GetNextcloudExecCommands() as $execCommand) { $nextcloudExecCommands .= $execCommand . PHP_EOL; } - foreach ($container->dependsOn as $dependency) { + foreach ($container->GetDependsOn() as $dependency) { $nextcloudExecCommands .= $this->GetNextcloudExecCommands($dependency); } return $nextcloudExecCommands; @@ -713,7 +776,7 @@ readonly class DockerActionManager { public function sendNotification(Container $container, string $subject, string $message, string $file = '/notify.sh'): void { if ($this->GetContainerStartingState($container) === ContainerState::Running) { - $containerName = $container->identifier; + $containerName = $container->GetIdentifier(); // schedule the exec $url = $this->BuildApiUrl(sprintf('containers/%s/exec', urlencode($containerName))); @@ -838,14 +901,14 @@ readonly class DockerActionManager { // Add a secondary alias for domaincheck container, to keep it as similar to actual apache controller as possible. // If a reverse-proxy is relying on container name as hostname this allows it to operate as usual and still validate the domain // The domaincheck container and apache container are never supposed to be active at the same time because they use the same APACHE_PORT anyway, so this doesn't add any new constraints. - $alias = ($container->identifier === 'nextcloud-aio-domaincheck') ? 'nextcloud-aio-apache' : ''; + $alias = ($container->GetIdentifier() === 'nextcloud-aio-domaincheck') ? 'nextcloud-aio-apache' : ''; - $this->ConnectContainerIdToNetwork($container->identifier, $container->internalPorts, alias: $alias); + $this->ConnectContainerIdToNetwork($container->GetIdentifier(), $container->GetInternalPort(), alias: $alias); - if ($container->identifier === 'nextcloud-aio-apache' || $container->identifier === 'nextcloud-aio-domaincheck') { - $apacheAdditionalNetwork = $this->configurationManager->getApacheAdditionalNetwork(); + if ($container->GetIdentifier() === 'nextcloud-aio-apache' || $container->GetIdentifier() === 'nextcloud-aio-domaincheck') { + $apacheAdditionalNetwork = $this->configurationManager->GetApacheAdditionalNetwork(); if ($apacheAdditionalNetwork !== '') { - $this->ConnectContainerIdToNetwork($container->identifier, $container->internalPorts, $apacheAdditionalNetwork, false, $alias); + $this->ConnectContainerIdToNetwork($container->GetIdentifier(), $container->GetInternalPort(), $apacheAdditionalNetwork, false, $alias); } } } @@ -854,9 +917,9 @@ readonly class DockerActionManager { if ($forceStopContainer) { $maxShutDownTime = 10; } else { - $maxShutDownTime = $container->maxShutdownTime; + $maxShutDownTime = $container->GetMaxShutdownTime(); } - $url = $this->BuildApiUrl(sprintf('containers/%s/stop?t=%s', urlencode($container->identifier), $maxShutDownTime)); + $url = $this->BuildApiUrl(sprintf('containers/%s/stop?t=%s', urlencode($container->GetIdentifier()), $maxShutDownTime)); try { $this->guzzleClient->post($url); } catch (RequestException $e) { @@ -946,7 +1009,7 @@ readonly class DockerActionManager { } public function GetAndGenerateSecretWrapper(string $secretId): string { - return $this->configurationManager->getAndGenerateSecret($secretId); + return $this->configurationManager->GetAndGenerateSecret($secretId); } public function isNextcloudImageOutdated(): bool { diff --git a/php/templates/components/container-state.twig b/php/templates/components/container-state.twig index 07580e66..8375d033 100644 --- a/php/templates/components/container-state.twig +++ b/php/templates/components/container-state.twig @@ -3,24 +3,24 @@ {% if c.GetStartingState().value == 'starting' %} - {{ c.displayName }} - (Starting) + {{ c.GetDisplayName() }} + (Starting) {% elseif c.GetRunningState().value == 'running' %} - {{ c.displayName }} - (Running) + {{ c.GetDisplayName() }} + (Running) {% else %} - {{ c.displayName }} - (Stopped) + {{ c.GetDisplayName() }} + (Stopped) {% endif %} - {% if c.documentation != '' %} - (docs) + {% if c.GetDocumentation() != '' %} + (docs) {% endif %} {% if c.GetUiSecret() != '' %}
- Show password for {{ c.displayName }} + Show password for {{ c.GetDisplayName() }}
{% endif %} diff --git a/php/templates/containers.twig b/php/templates/containers.twig index 8e437bc2..c318e8a6 100644 --- a/php/templates/containers.twig +++ b/php/templates/containers.twig @@ -17,8 +17,7 @@
- {% set aio_version = include('includes/aio-version.twig') %} -

Nextcloud AIO v{{ aio_version }}

+

Nextcloud AIO v12.4.0

{# Add 2nd tab warning #} @@ -27,7 +26,7 @@ {# js for optional containers and additional containers forms #} - + {% set hasBackupLocation = borg_backup_host_location or borg_remote_repo %} {% set isAnyRunning = false %} @@ -46,19 +45,19 @@ {% endif %} {% for container in containers %} - {% if container.displayName != '' and container.GetRunningState().value == 'running' %} + {% if container.GetDisplayName() != '' and container.GetRunningState().value == 'running' %} {% set isAnyRunning = true %} {% endif %} - {% if container.displayName != '' and container.GetRestartingState().value == 'restarting' %} + {% if container.GetDisplayName() != '' and container.GetRestartingState().value == 'restarting' %} {% set isAnyRestarting = true %} {% endif %} - {% if container.identifier == 'nextcloud-aio-watchtower' and container.GetRunningState().value == 'running' %} + {% if container.GetIdentifier() == 'nextcloud-aio-watchtower' and container.GetRunningState().value == 'running' %} {% set isWatchtowerRunning = true %} {% endif %} - {% if container.identifier == 'nextcloud-aio-domaincheck' and container.GetRunningState().value == 'running' %} + {% if container.GetIdentifier() == 'nextcloud-aio-domaincheck' and container.GetRunningState().value == 'running' %} {% set isDomaincheckRunning = true %} {% endif %} - {% if container.identifier == 'nextcloud-aio-apache' and container.GetStartingState().value == 'starting' %} + {% if container.GetIdentifier() == 'nextcloud-aio-apache' and container.GetStartingState().value == 'starting' %} {% set isApacheStarting = true %} {% endif %} {% endfor %} @@ -281,7 +280,7 @@
    {# @var containers \AIO\Container\Container[] #} {% for container in containers %} - {% if container.displayName != '' %} + {% if container.GetDisplayName() != '' %} {% include 'components/container-state.twig' with {'c': container} only %} {% endif %} {% endfor %} diff --git a/php/templates/includes/aio-version.twig b/php/templates/includes/aio-version.twig deleted file mode 100644 index 1b62f917..00000000 --- a/php/templates/includes/aio-version.twig +++ /dev/null @@ -1 +0,0 @@ -12.6.1 diff --git a/php/templates/includes/optional-containers.twig b/php/templates/includes/optional-containers.twig index eabcb139..b4764592 100644 --- a/php/templates/includes/optional-containers.twig +++ b/php/templates/includes/optional-containers.twig @@ -9,105 +9,6 @@ -

    Office Suite

    - {% if isAnyRunning == false %} -

    Choose your preferred office suite. Only one can be enabled at a time.

    - {% endif %} -
    - - - - - - -
    - {% if isAnyRunning == false %} -
    - - -
    - {% endif %} - -

    Additional Optional Containers

    +

    + + +

    - +

    + + +

    - +

    Minimal system requirements: When any optional container is enabled, at least 2GB RAM, a dual-core CPU and 40GB system storage are required. When enabling ClamAV, Nextcloud Talk Recording-server or Fulltextsearch, at least 3GB RAM are required. For Talk Recording-server additional 2 vCPUs are required. When enabling everything, at least 5GB RAM and a quad-core CPU are required. Recommended are at least 1GB more RAM than the minimal requirement. For further advice and recommendations see this documentation

    {% if isAnyRunning == true %} - - + + @@ -228,46 +156,46 @@ {% endif %} {% if is_collabora_enabled == true and isAnyRunning == false and was_start_button_clicked == true %} -

    Nextcloud Office dictionaries

    +

    Collabora dictionaries

    {% if collabora_dictionaries == "" %} -

    In order to get the correct dictionaries in Nextcloud Office, you may configure the dictionaries below:

    +

    In order to get the correct dictionaries in Collabora, you may configure the dictionaries below:

    - +

    You need to make sure that the dictionaries that you enter are valid. An example is de_DE en_GB en_US es_ES fr_FR it nl pt_BR pt_PT ru.

    {% else %} -

    The dictionaries for Nextcloud Office are currently set to {{ collabora_dictionaries }}. You can reset them again by clicking on the button below.

    +

    The dictionaries for Collabora are currently set to {{ collabora_dictionaries }}. You can reset them again by clicking on the button below.

    - +
    {% endif %} -

    Additional Nextcloud Office options

    +

    Additional Collabora options

    {% if collabora_additional_options == "" %} -

    You can configure additional options for Nextcloud Office below.

    +

    You can configure additional options for collabora below.

    (This can be used for configuring the net.content_security_policy and more. Make sure to submit the value!)

    - +

    You need to make sure that the options that you enter are valid. An example is --o:net.content_security_policy=frame-ancestors *.example.com:*;.

    {% else %} -

    The additioinal options for Nextcloud Office are currently set to {{ collabora_additional_options }}. You can reset them again by clicking on the button below.

    +

    The additioinal options for Collabora are currently set to {{ collabora_additional_options }}. You can reset them again by clicking on the button below.

    - +
    {% endif %} {% endif %} diff --git a/php/templates/layout.twig b/php/templates/layout.twig index 79c615d9..4d842e3d 100644 --- a/php/templates/layout.twig +++ b/php/templates/layout.twig @@ -1,7 +1,7 @@ AIO - + diff --git a/php/tests/tests/initial-setup.spec.js b/php/tests/tests/initial-setup.spec.js index 1f21f011..c88cd8e3 100644 --- a/php/tests/tests/initial-setup.spec.js +++ b/php/tests/tests/initial-setup.spec.js @@ -32,12 +32,12 @@ test('Initial setup', async ({ page: setupPage }) => { await containersPage.locator('#talk').uncheck(); await containersPage.getByRole('checkbox', { name: 'Whiteboard' }).uncheck(); await containersPage.getByRole('checkbox', { name: 'Imaginary' }).uncheck(); - await containersPage.getByText('Disable office suite').click(); - await containersPage.getByRole('button', { name: 'Save changes' }).last().click(); + await containersPage.getByRole('checkbox', { name: 'Collabora' }).uncheck(); + await containersPage.getByRole('button', { name: 'Save changes' }).click(); await expect(containersPage.locator('#talk')).not.toBeChecked() await expect(containersPage.getByRole('checkbox', { name: 'Whiteboard' })).not.toBeChecked() await expect(containersPage.getByRole('checkbox', { name: 'Imaginary' })).not.toBeChecked() - await expect(containersPage.locator('#office-none')).toBeChecked() + await expect(containersPage.getByRole('checkbox', { name: 'Collabora' })).not.toBeChecked() // Reject invalid time zones await containersPage.locator('#timezone').click(); diff --git a/php/tests/tests/restore-instance.spec.js b/php/tests/tests/restore-instance.spec.js index 696a4376..e93cf340 100644 --- a/php/tests/tests/restore-instance.spec.js +++ b/php/tests/tests/restore-instance.spec.js @@ -74,7 +74,7 @@ test('Restore instance', async ({ page: setupPage }) => { dialog.accept() }); await containersPage.getByRole('button', { name: 'Start and update containers' }).click(); - await expect(containersPage.getByRole('link', { name: 'Open your Nextcloud ↗' })).toBeVisible({ timeout: 8 * 60 * 1000 }); + await expect(containersPage.getByRole('link', { name: 'Open your Nextcloud ↗' })).toBeVisible({ timeout: 5 * 60 * 1000 }); await expect(containersPage.getByRole('main')).toContainText(initialNextcloudPassword); // Verify that containers are all stopped diff --git a/readme.md b/readme.md index 66059954..bcbf7d57 100644 --- a/readme.md +++ b/readme.md @@ -340,7 +340,7 @@ Although it does not seems like it is the case but from AIO perspective a Cloudf For a reverse proxy example guide for Tailscale, see this guide by [@Perseus333](https://github.com/Perseus333): https://github.com/nextcloud/all-in-one/discussions/6817 ### How to get Nextcloud running using the ACME DNS-challenge? -You can install AIO behind an external reverse proxy where is also documented how to get it running using the ACME DNS-challenge for getting a valid certificate for AIO. See the [reverse proxy documentation](./reverse-proxy.md). (Meant is the `Caddy with ACME DNS-challenge` section). Also see https://github.com/dani-garcia/vaultwarden/wiki/Running-a-private-vaultwarden-instance-with-Let%27s-Encrypt-certs#getting-a-custom-caddy-build for additional docs on this topic. +You can install AIO in reverse proxy mode where is also documented how to get it running using the ACME DNS-challenge for getting a valid certificate for AIO. See the [reverse proxy documentation](./reverse-proxy.md). (Meant is the `Caddy with ACME DNS-challenge` section). Also see https://github.com/dani-garcia/vaultwarden/wiki/Running-a-private-vaultwarden-instance-with-Let%27s-Encrypt-certs#getting-a-custom-caddy-build for additional docs on this topic. ### How to run Nextcloud locally? No domain wanted, or wanting intranet access within your LAN. If you do not want to open Nextcloud to the public internet, you may have a look at the following documentation on how to set it up locally: [local-instance.md](./local-instance.md), but keep in mind you're still required to have https working properly. diff --git a/reverse-proxy.md b/reverse-proxy.md index bdeb3244..50a6bccd 100644 --- a/reverse-proxy.md +++ b/reverse-proxy.md @@ -564,14 +564,19 @@ Note: this will cause that a non root user can bind privileged ports. Second, see these screenshots for a working config: -image +![grafik](https://github.com/user-attachments/assets/c32c8fe8-7417-4f8f-9625-24b95651e630) -image +![grafik](https://github.com/user-attachments/assets/f14bba5c-69ce-4514-a2ac-5e5d7fb97792) -image + -image +![grafik](https://github.com/user-attachments/assets/75d7f539-35d1-4a3e-8c51-43123f698893) +![grafik](https://github.com/user-attachments/assets/e494edb5-8b70-4d45-bc9b-374219230041) + +`proxy_set_header Accept-Encoding $http_accept_encoding;` + +⚠️ **Please note:** Nextcloud will complain that X-XXS-Protection is set to the wrong value, this is intended by NPMplus.
    ⚠️ **Please note:** look into [this](#adapting-the-sample-web-server-configurations-below) to adapt the above example configuration. diff --git a/zizmor.yml b/zizmor.yml index 7601baa4..a991eaa5 100644 --- a/zizmor.yml +++ b/zizmor.yml @@ -4,5 +4,7 @@ rules: dangerous-triggers: ignore: - build_images.yml - artipacked: - disable: true + unpinned-uses: + config: + policies: + actions/*: ref-pin