From 12e129f1f637b553420d9bb4e5bf4bab0af4c2ae Mon Sep 17 00:00:00 2001
From: Zoey
Date: Thu, 16 Apr 2026 17:20:50 +0200
Subject: [PATCH] aio-interface: improve headers (#7690)
Signed-off-by: Zoey
Signed-off-by: Simon L.
Signed-off-by: Pablo Zmdl
Co-authored-by: Simon L.
Co-authored-by: Pablo Zmdl
---
.github/workflows/playwright-on-push.yml | 4 ++
Containers/apache/Caddyfile | 7 ++-
Containers/mastercontainer/acme.Caddyfile | 2 +
Containers/mastercontainer/headers.Caddyfile | 27 ++++++++++
Containers/mastercontainer/internal.Caddyfile | 2 +
php/public/click-handlers.js | 27 ++++++++++
php/public/forms.js | 9 ----
php/public/img/collabora.svg | 3 ++
php/public/img/office-none.svg | 3 ++
php/public/img/onlyoffice.svg | 3 ++
php/public/logs.css | 49 +++++++++++++++++
php/public/scroll-into-view.js | 9 ++++
php/public/toggle-dark-mode.js | 5 +-
php/src/Controller/DockerController.php | 12 +----
php/templates/containers.twig | 22 ++++----
.../includes/community-containers.twig | 2 +-
.../includes/optional-containers.twig | 16 ++----
php/templates/layout.twig | 6 ++-
php/templates/log.twig | 52 +------------------
php/templates/login.twig | 2 +-
20 files changed, 162 insertions(+), 100 deletions(-)
create mode 100644 Containers/mastercontainer/headers.Caddyfile
create mode 100644 php/public/click-handlers.js
create mode 100644 php/public/img/collabora.svg
create mode 100644 php/public/img/office-none.svg
create mode 100644 php/public/img/onlyoffice.svg
create mode 100644 php/public/logs.css
create mode 100644 php/public/scroll-into-view.js
diff --git a/.github/workflows/playwright-on-push.yml b/.github/workflows/playwright-on-push.yml
index 961c1603..15f0821d 100644
--- a/.github/workflows/playwright-on-push.yml
+++ b/.github/workflows/playwright-on-push.yml
@@ -68,6 +68,8 @@ jobs:
--publish 8080:8080 \
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
--volume ./php:/var/www/docker-aio/php \
+ --volume ./Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile \
+ --volume ./Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--env SKIP_DOMAIN_VALIDATION=true \
--env APACHE_PORT=11000 \
@@ -97,6 +99,8 @@ jobs:
--publish 8080:8080 \
--volume nextcloud_aio_mastercontainer:/mnt/docker-aio-config \
--volume ./php:/var/www/docker-aio/php \
+ --volume ./Containers/mastercontainer/internal.Caddyfile:/internal.Caddyfile \
+ --volume ./Containers/mastercontainer/headers.Caddyfile:/headers.Caddyfile \
--volume /var/run/docker.sock:/var/run/docker.sock:ro \
--env SKIP_DOMAIN_VALIDATION=false \
--env APACHE_PORT=11000 \
diff --git a/Containers/apache/Caddyfile b/Containers/apache/Caddyfile
index a15a9c19..9992c78c 100644
--- a/Containers/apache/Caddyfile
+++ b/Containers/apache/Caddyfile
@@ -17,8 +17,11 @@
https://{$ADDITIONAL_TRUSTED_DOMAIN}:443,
http://{$APACHE_HOST}.nextcloud-aio:23973, # For Collabora callback and WOPI requests, see containers.json
{$PROTOCOL}://{$NC_DOMAIN}:{$APACHE_PORT} {
- header -Server
- header -X-Powered-By
+ header {
+ -Server
+ -X-Powered-By
+ -Via
+ }
# Collabora
route /browser/* {
diff --git a/Containers/mastercontainer/acme.Caddyfile b/Containers/mastercontainer/acme.Caddyfile
index 0d5e84fe..3a6710c1 100644
--- a/Containers/mastercontainer/acme.Caddyfile
+++ b/Containers/mastercontainer/acme.Caddyfile
@@ -33,6 +33,8 @@ http://:80 {
}
https://:8443 {
+ import headers.Caddyfile
+
@denied {
path /api/auth/login /api/auth/getlogin
remote_host nextcloud-aio-nextcloud
diff --git a/Containers/mastercontainer/headers.Caddyfile b/Containers/mastercontainer/headers.Caddyfile
new file mode 100644
index 00000000..0f55bf7b
--- /dev/null
+++ b/Containers/mastercontainer/headers.Caddyfile
@@ -0,0 +1,27 @@
+header {
+ # CSP limits which features can be used. By default we allow nothing and only allow required options. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Security-Policy
+ # default-src 'none'; Allow nothing by default
+ # script-src-elem/style-src-elem 'self'; Only allow loading css/js files from same origin (AIO itself) while blocking all inline css/js
+ # img-src 'self'; Only allow loading images from same origin (from AIO itself)
+ # connect-src 'self'; Allow fetch to only connect same origin (to AIO itself)
+ # frame-src 'self'; Allow AIO to only embed itself "what can be embedded"
+ # base-uri 'none'; This does not fallback to default-src, AIO does not use the html base tag
+ # form-action 'self'; Html forms are only allowed to submit to AIO and not cross origin
+ # frame-ancestors 'self'; Only allow AIO itself to embed it self "who can embed"
+ # upgrade-insecure-requests; Upgrade all http embedings to https
+ # require-trusted-types-for 'script'; trusted-types 'none'; Blocks DOM changes via js
+ Content-Security-Policy "default-src 'none'; script-src-elem 'self'; style-src-elem 'self'; img-src 'self'; connect-src 'self'; frame-src 'self'; base-uri 'none'; form-action 'self'; frame-ancestors 'self'; upgrade-insecure-requests; require-trusted-types-for 'script'; trusted-types 'none';"
+ X-Content-Type-Options "nosniff" # This forces the browser to use the MIME type of the Content-Type header. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Content-Type-Options
+ X-Frame-Options "SAMEORIGIN" # Only allow AIO itself to embed itself, this is also enforced as part of the CSP frame-ancestors. See https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Headers/X-Frame-Options
+ X-Permitted-Cross-Domain-Policies "none" # We block all cross origin request, including ones from Adobe Acrobat or Microsoft Silverlight and Adobe Flash Player. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Permitted-Cross-Domain-Policies
+ X-DNS-Prefetch-Control "off" # Tells the browser to not pre-fetch the DNS of linked pages. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-DNS-Prefetch-Control
+ Referrer-Policy "no-referrer" # Tells the browser to never sent a Referer header. See https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Headers/Referrer-Policy
+ X-Robots-Tag "noindex, nofollow" # Tells web crawlers to not index this page. See https://developer.mozilla.org/de/docs/Web/HTTP/Reference/Headers/X-Robots-Tag
+ Cross-Origin-Opener-Policy "same-origin"; # AIO does not use any popup, still we can isolate its BCG if it is opened as a pop up by another page. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Opener-Policy
+ Cross-Origin-Embedder-Policy "require-corp"; # Harder rules for cross origin embeds. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cross-Origin-Embedder-Policy
+ Cross-Origin-Resource-Policy "same-origin"; # Only allow the same origin to load resources. See https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Cross-Origin_Resource_Policy
+
+ -Server
+ -X-Powered-By
+ -Via
+}
diff --git a/Containers/mastercontainer/internal.Caddyfile b/Containers/mastercontainer/internal.Caddyfile
index e1809f55..9890acc0 100644
--- a/Containers/mastercontainer/internal.Caddyfile
+++ b/Containers/mastercontainer/internal.Caddyfile
@@ -24,6 +24,8 @@
}
https://:8080 {
+ import headers.Caddyfile
+
@denied {
path /api/auth/login /api/auth/getlogin
remote_host nextcloud-aio-nextcloud
diff --git a/php/public/click-handlers.js b/php/public/click-handlers.js
new file mode 100644
index 00000000..88aa0d4a
--- /dev/null
+++ b/php/public/click-handlers.js
@@ -0,0 +1,27 @@
+document.addEventListener("DOMContentLoaded", () => {
+ document.querySelectorAll('input[data-confirm]').forEach((element) => {
+ element.addEventListener('click', (event) => {
+ if (!confirm(element.dataset.confirm)) {
+ event.preventDefault();
+ }
+ });
+ });
+
+
+ document.querySelectorAll('input[data-input-show-password]').forEach((element) => {
+ element.addEventListener('input', (element) => {
+ let passwordField = element
+ if (passwordField.type === "password" && passwordField.value !== "") {
+ passwordField.type = "text";
+ } else if (passwordField.type === "text" && passwordField.value === "") {
+ passwordField.type = "password";
+ }
+ });
+ });
+
+ document.querySelectorAll('[data-stop-event-propagation="true"]').forEach((element) => {
+ element.addEventListener('click', (event) => {
+ event.stopPropagation();
+ });
+ });
+});
diff --git a/php/public/forms.js b/php/public/forms.js
index 46cde081..b37fdcdb 100644
--- a/php/public/forms.js
+++ b/php/public/forms.js
@@ -1,14 +1,5 @@
"use strict";
-function showPassword(id) {
- let passwordField = document.getElementById(id);
- if (passwordField.type === "password" && passwordField.value !== "") {
- passwordField.type = "text";
- } else if (passwordField.type === "text" && passwordField.value === "") {
- passwordField.type = "password";
- }
-}
-
(function (){
let lastError;
diff --git a/php/public/img/collabora.svg b/php/public/img/collabora.svg
new file mode 100644
index 00000000..eb032cdd
--- /dev/null
+++ b/php/public/img/collabora.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/php/public/img/office-none.svg b/php/public/img/office-none.svg
new file mode 100644
index 00000000..1ee12a79
--- /dev/null
+++ b/php/public/img/office-none.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/php/public/img/onlyoffice.svg b/php/public/img/onlyoffice.svg
new file mode 100644
index 00000000..eb032cdd
--- /dev/null
+++ b/php/public/img/onlyoffice.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/php/public/logs.css b/php/public/logs.css
new file mode 100644
index 00000000..04f6be1a
--- /dev/null
+++ b/php/public/logs.css
@@ -0,0 +1,49 @@
+html, body {
+ height: 100%;
+ overflow: hidden;
+ padding: 0;
+ margin: 0;
+}
+pre {
+ height: 100%;
+ overflow: auto;
+ margin: 0;
+ padding: 1rem;
+ box-sizing: border-box;
+}
+#floating-box {
+ position: fixed;
+ top: 1rem;
+ right: 1rem;
+ max-width: calc(100vw - 2rem);
+ z-index: 10;
+ display: flex;
+ justify-content: end;
+ align-items: center;
+}
+#autoloading-box {
+ display: grid;
+ gap: 0.5rem;
+ font-size: large;
+ border: solid thin gray;
+ background-color: #f9f9f9;
+ width: 10rem;
+ padding: 0.5rem 1rem;
+ margin: 0 0 0 1rem;
+}
+.loader {
+ opacity: 1;
+ width: 40px;
+ height: 40px;
+ align-self: inherit;
+}
+@starting-style {
+ .loader {
+ opacity: 0;
+ }
+}
+.loader.hidden {
+ display: none;
+ opacity: 0;
+ transition: opacity 1s, display 1s allow-discrete;
+}
\ No newline at end of file
diff --git a/php/public/scroll-into-view.js b/php/public/scroll-into-view.js
new file mode 100644
index 00000000..2c676911
--- /dev/null
+++ b/php/public/scroll-into-view.js
@@ -0,0 +1,9 @@
+const observer = new MutationObserver((records) => {
+ const node = records[0]?.addedNodes[0];
+ // Text nodes also appear here but can't be scrolled to, so we have to check for the
+ // function being present.
+ if (node && typeof(node.scrollIntoView) === 'function') {
+ node.scrollIntoView();
+ }
+});
+observer.observe(document, {childList: true, subtree: true});
diff --git a/php/public/toggle-dark-mode.js b/php/public/toggle-dark-mode.js
index 8eeba013..44eff9ff 100644
--- a/php/public/toggle-dark-mode.js
+++ b/php/public/toggle-dark-mode.js
@@ -32,4 +32,7 @@ function setThemeIcon(theme) {
setThemeToDOM(getSavedTheme());
// Apply theme when the page loads
-document.addEventListener('DOMContentLoaded', () => setThemeIcon(getSavedTheme()));
+document.addEventListener('DOMContentLoaded', () => {
+ setThemeIcon(getSavedTheme())
+ document.querySelector('button#theme-toggle')?.addEventListener('click', () => toggleTheme());
+});
diff --git a/php/src/Controller/DockerController.php b/php/src/Controller/DockerController.php
index e7359c7b..5942e093 100644
--- a/php/src/Controller/DockerController.php
+++ b/php/src/Controller/DockerController.php
@@ -381,17 +381,7 @@ readonly class DockerController {
-
+
diff --git a/php/templates/containers.twig b/php/templates/containers.twig
index fcf87c1a..17405256 100644
--- a/php/templates/containers.twig
+++ b/php/templates/containers.twig
@@ -153,7 +153,7 @@
{% endif %}
@@ -178,7 +178,7 @@
{% endfor %}
Exclude previews from restore which will speed up the restore process but will trigger a scan of the preview folder as soon as the Nextcloud container starts the next time
-
+
{% endif %}
{% elseif borg_backup_mode == 'restore' %}
@@ -366,7 +366,7 @@
{% if bypass_container_update == true %}
{% endif %}
-
+
{% endif %}
{% endif %}
@@ -413,7 +413,7 @@
{% endif %}
@@ -478,7 +478,7 @@
{% if has_backup_run_once == true %}
@@ -490,7 +490,7 @@
Backup restore
@@ -503,7 +503,7 @@
{{ restore_time }} UTC
{% endfor %}
-
+
Update backup list
@@ -570,7 +570,7 @@
-
+
{% endif %}
{% if has_backup_run_once == true %}
@@ -587,8 +587,8 @@
Click here to change your AIO passphrase
You can change your AIO passphrase below:
You need to make sure that the timezone that you enter is valid. An example is Europe/Berlin . You can get valid values by looking at the 'TZ identifier' column of this list: click here . The default is Etc/UTC if nothing is entered.
{% else %}
diff --git a/php/templates/includes/community-containers.twig b/php/templates/includes/community-containers.twig
index 66cceb2b..da1dd26d 100644
--- a/php/templates/includes/community-containers.twig
+++ b/php/templates/includes/community-containers.twig
@@ -37,6 +37,6 @@
{% endfor %}
-
+
diff --git a/php/templates/includes/optional-containers.twig b/php/templates/includes/optional-containers.twig
index 08f98634..b33f2292 100644
--- a/php/templates/includes/optional-containers.twig
+++ b/php/templates/includes/optional-containers.twig
@@ -41,11 +41,9 @@
Best support for legacy files
{% if isAnyRunning == false %}
-
+
Learn more
-
-
-
+
{% endif %}
@@ -76,11 +74,9 @@
Limited ODF compatibility
{% if isAnyRunning == false %}
-
+
Learn more
-
-
-
+
{% endif %}
@@ -99,9 +95,7 @@
{% endif %}
>
-
-
-
+
Disable office suite
diff --git a/php/templates/layout.twig b/php/templates/layout.twig
index ab3095c4..55fb6e5e 100644
--- a/php/templates/layout.twig
+++ b/php/templates/layout.twig
@@ -1,10 +1,12 @@
-
+
+
AIO
+
@@ -15,7 +17,7 @@
-
+
diff --git a/php/templates/log.twig b/php/templates/log.twig
index 044c6386..297498fc 100644
--- a/php/templates/log.twig
+++ b/php/templates/log.twig
@@ -2,57 +2,7 @@
-
+
diff --git a/php/templates/login.twig b/php/templates/login.twig
index 1c5420c2..88432e48 100644
--- a/php/templates/login.twig
+++ b/php/templates/login.twig
@@ -11,7 +11,7 @@
{% if is_login_allowed == true %}
Log in using your Nextcloud AIO passphrase: