diff --git a/.claude/context.md b/.claude/context.md deleted file mode 100644 index 49871f09..00000000 --- a/.claude/context.md +++ /dev/null @@ -1,153 +0,0 @@ -# Euro-Office AIO — Work Context - -## Goal -Ship Nextcloud AIO with **EuroOffice as the default document editor** (replacing Collabora). -This is the euro-office-public fork of `nextcloud/all-in-one`. Current phase: **testing**. - ---- - -## What We've Done - -### `php/src/Data/ConfigurationManager.php` -- `isEuroofficeEnabled` default: `false` → `true` (+ added missing `(bool)` cast) -- `isCollaboraEnabled` default: `true` → `false` -- `getNextcloudStartupApps()`: added `eurooffice` to the default app list -- Added `performMigrations()`: one-time migration (flag `eurooffice_default_migration_v1`) that forces existing installs to switch to EuroOffice on next mastercontainer start - -### `php/public/index.php` -- Calls `$configurationManager->performMigrations()` at bootstrap (before route handling) - -### `Containers/apache/Caddyfile` — EuroOffice SDK 404 fix -**Root cause:** EuroOffice nginx maps `$http_x_forwarded_prefix` → `$the_prefix` and uses it to construct SDK asset URLs (e.g. `/eurooffice/sdkjs/...`). Without the header, `$the_prefix` is empty so the browser requests `/sdkjs/...` which Caddy routes to Nextcloud → 404. - -**Fix:** Send `X-Forwarded-Prefix` as a separate header instead of appending the path to `X-Forwarded-Host`: -``` -route /eurooffice/* { - uri strip_prefix /eurooffice - reverse_proxy {$EUROOFFICE_HOST}:80 { - header_up X-Forwarded-Host {http.request.hostport} - header_up X-Forwarded-Prefix /eurooffice - } -} -``` -Note: OnlyOffice block uses the old pattern (`X-Forwarded-Host host/onlyoffice`) and was left unchanged — OO worked with it. This fix is also needed upstream in `nextcloud/all-in-one`. - -### `Containers/nextcloud/entrypoint.sh` — EuroOffice internal URL and preview fix - -**Root cause summary:** Containers resolve `nextcloud.test` → `127.0.0.1` (Docker reads host `/etc/hosts` which has `127.0.0.1 nextcloud.test`). So any container→container call using the public domain fails. This affects: -1. NC → EuroOffice converter calls (preview generation, document editing callbacks) -2. EuroOffice → NC file download calls (EuroOffice needs to fetch the document to convert it) - -Three fixes baked into the entrypoint EuroOffice block: - -**Fix 1 — DocumentServerInternalUrl** (NC → EuroOffice, bypasses public domain): -```bash -php /var/www/html/occ config:app:set eurooffice DocumentServerInternalUrl --value="http://$EUROOFFICE_HOST:80/" -``` -- Must have trailing slash — `getDocumentServerInternalUrl()` returns raw value, concatenated directly with `"converter"` in `DocumentService::getConvertedUri()` (line 124) -- `$EUROOFFICE_HOST` is the Docker container name (e.g. `nextcloud-aio-eurooffice`) before it gets rewritten to `$NC_DOMAIN/eurooffice` - -**Fix 2 — StorageUrl** (EuroOffice → NC, bypasses public domain): -```bash -APACHE_CONTAINER_HOST=$(echo "$EUROOFFICE_HOST" | sed 's/-eurooffice$/-apache/') -php /var/www/html/occ config:app:set eurooffice StorageUrl --value="http://$APACHE_CONTAINER_HOST.nextcloud-aio:23973/" -``` -- Port 23973 matches Caddy's `http://{$APACHE_HOST}.nextcloud-aio:23973` server block (the WOPI/callback port), which routes to Nextcloud at `127.0.0.1:8000` -- Port 11000 does NOT work even though it's open — Caddy rejects requests where `Host: nextcloud-aio-apache:11000` doesn't match the `nextcloud.test:11000` binding -- Must have trailing slash — `getStorageUrl()` returns raw value; `str_replace("https://nextcloud.test/", storageUrl, $fileUrl)` strips the `/` from the origin, producing `...23973apps/` (broken URL) if StorageUrl has no trailing slash - -**Fix 3 — enabledPreviewProviders** (EuroOffice class missing from explicit allowlist): -```bash -if ! php /var/www/html/occ config:system:get enabledPreviewProviders | grep -q "Eurooffice"; then - php /var/www/html/occ config:system:set enabledPreviewProviders 50 --value="OCA\Eurooffice\Preview" -fi -``` -- NC has `enabledPreviewProviders` explicitly set; `registerPreviewProvider()` in `Application.php` is insufficient — the class must also be in this allowlist or the provider is silently skipped -- FQCN: `OCA\Eurooffice\Preview` (namespace `OCA\Eurooffice`, class `Preview`) -- occ stores with `\\` in config.php (PHP string literal for single backslash) — this is correct -- Index 50 chosen to avoid collision with AIO's seeded range (indices 1–7 on fresh install, 23 for Imaginary). Previous `wc -l` approach would have produced index 7 on a standard install, silently overwriting the Krita preview provider. - -### Local `aio-apache:beta` image -Built from repo to test the Caddyfile fix: -```bash -docker buildx build --file Containers/apache/Dockerfile --tag ghcr.io/nextcloud-releases/aio-apache:beta --load Containers/apache -``` -AIO uses the `:beta` tag in this test environment; local image takes precedence over remote pull. - -After the build, `/Caddyfile` inside the image still had the old content (build context was captured before the edit was saved). The runtime `/tmp/Caddyfile` was manually correct. Fixed with: -```bash -docker exec nextcloud-aio-apache caddy reload --config /tmp/Caddyfile -``` - ---- - -## Status as of 2026-06-08 - -- ✅ **EuroOffice documents open** — Caddyfile X-Forwarded-Prefix fix working, SDK assets load correctly -- ✅ **Preview generation works** — preview PNG files stored at `/mnt/ncdata/appdata_*/preview/.../107/` (confirmed valid) -- ⚠️ **Preview display** — browser/service worker caches stale 404; hard reload (`Cmd+Shift+R`) clears it -- ⬜ **Commit all changes** — entrypoint.sh, Caddyfile, ConfigurationManager.php, index.php -- ⬜ **Upstream PR** to `nextcloud/all-in-one` for the Caddyfile `X-Forwarded-Prefix` fix -- ⬜ **Docker Desktop restart** — still needed for `daemon.json` DNS change; currently working around it with internal URLs - ---- - -## Caveats & Local Environment Notes - -### Docker DNS — containers resolving `nextcloud.test` - -**Why internal URLs are needed:** `/etc/hosts` on the macOS host has `127.0.0.1 nextcloud.test`. Docker embeds these in container DNS, so containers resolve `nextcloud.test` → `127.0.0.1` (the container's own loopback) rather than the host Caddy. This breaks any container→host→container call. - -**Workaround (in place):** Both `DocumentServerInternalUrl` and `StorageUrl` bypass `nextcloud.test` entirely by using Docker-internal container names. - -**Permanent fix (not yet applied):** Restart Docker Desktop to load `daemon.json` (`"dns": ["192.168.97.1"]`). Then dnsmasq at `192.168.97.1` will resolve `.test` → `0.250.250.254` (macOS `host.docker.internal`) and `nextcloud.test` will resolve correctly from containers. - -Setup: -- **dnsmasq** (Homebrew): `/opt/homebrew/etc/dnsmasq.conf` has: - ``` - address=/.test/0.250.250.254 - listen-address=127.0.0.1,192.168.97.1 - ``` - (`192.168.97.1` is the Docker bridge gateway — containers use it as DNS) -- **`~/.docker/daemon.json`**: `"dns": ["192.168.97.1"]` — containers get dnsmasq as DNS resolver -- **`/etc/hosts`**: `127.0.0.1 nextcloud.test` — browser resolves directly (Caddy only listens on loopback, not the Docker bridge) -- Docker Desktop restart required for daemon.json to take effect — **still pending** - -### Apache container — patching Caddyfile at runtime -- `/Caddyfile` is baked into the image; cannot be edited in a running container (image rootfs is read-only for that path) -- `/tmp/Caddyfile` is the runtime copy generated by `start.sh`; it is writable -- Caddy loads from `/tmp/Caddyfile` at startup (`caddy run --config /tmp/Caddyfile`) -- To apply a Caddyfile fix without a full container restart: - ```bash - # edit /tmp/Caddyfile, then: - docker exec nextcloud-aio-apache caddy reload --config /tmp/Caddyfile - ``` - -### EuroOffice NC connector app -- App ID: `eurooffice`, FQCN for preview provider: `OCA\Eurooffice\Preview` -- Available on the Nextcloud appstore: https://apps.nextcloud.com/apps/eurooffice (v11.0.0, NC 33/34) -- Added to `STARTUP_APPS` so it installs automatically on first Nextcloud start -- Sibling repo: `/Users/jamesmanuel/PhpstormProjects/euro-office-public/eurooffice-nextcloud` -- Uses OnlyOffice `/converter` API (not WOPI) for preview generation and document editing -- JWT: shared secret configured via `jwt_secret` in both `config:system:set eurooffice jwt_secret` and `config:app:set eurooffice jwt_secret`; header is `AuthorizationJwt` - -### AIO UI & access -- Mastercontainer: `https://localhost:9090` -- Nextcloud: `https://nextcloud.test` -- Stop/start containers via the AIO UI — not `docker stop` directly -- Upstream beta tag being tested: `v13.2.0` (first version with EuroOffice support merged via PR #8052) - -### Local images pulled -- `ghcr.io/euro-office/documentserver:v9.3.1-beta.1` — 7.14 GB -- `ghcr.io/nextcloud-releases/all-in-one:latest` — 386 MB -- `ghcr.io/nextcloud-releases/aio-apache:beta` — 289 MB (locally built) - -### Building locally -```bash -# apache image (Caddyfile changes): -docker buildx build --file Containers/apache/Dockerfile --tag ghcr.io/nextcloud-releases/aio-apache:beta --load Containers/apache - -# mastercontainer (PHP/config changes): -docker buildx build --file Containers/mastercontainer/Dockerfile --tag ghcr.io/nextcloud-releases/all-in-one:develop --load . -# then add ?bypass_mastercontainer_update to the URL to skip self-update prompt -``` diff --git a/.claude/plan.md b/.claude/plan.md deleted file mode 100644 index ca47cfa1..00000000 --- a/.claude/plan.md +++ /dev/null @@ -1,187 +0,0 @@ -# EuroOffice Default Editor — Implementation Plan - -**Status:** Changes implemented, tested locally. Ready to commit. -**Phase:** Testing → Commit → Build → (upstream Caddyfile PR optional) - ---- - -## Security Review (Completed) - -**Verdict: no regressions.** The four changed files introduce no new attack surface. - -- **Caddyfile `X-Forwarded-Prefix /eurooffice`** — literal string set by Caddy `header_up`, not - user-controlled. Overwrites any client-supplied value before it reaches EuroOffice nginx. -- **`X-Forwarded-Host {http.request.hostport}`** — appears in the EuroOffice route and was already - present in existing OnlyOffice and Nextcloud server blocks. Caddy only routes requests matching its - server-block bindings, and Nextcloud enforces `trusted_domains` downstream. Considered and - dismissed; not a reflective-Host injection issue in this deployment model. -- **Internal HTTP (DocumentServerInternalUrl / StorageUrl)** — identical threat model to the existing - Collabora WOPI callback path. `http://nextcloud-aio-apache.nextcloud-aio:23973` is the documented - internal callback address (used by Collabora in containers.json lines 394/405). Same Docker - network isolation, same JWT signing (`AuthorizationJwt`). No new port opened. -- **`performMigrations()` in index.php** — runs an idempotent config read on every HTTP request until - the migration flag is set for the first time. Not a security concern; minor performance overhead - on first-boot only. - ---- - -## Wider Implications - -### 1. Migration blast-radius (decision required) - -`performMigrations()` runs on every mastercontainer boot. When `eurooffice_default_migration_v1` is -unset it force-sets: -- `isCollaboraEnabled = false` -- `isOnlyofficeEnabled = false` -- `isEuroofficeEnabled = true` - -**This overrides an admin's explicit prior choice**, not just an unset default. An existing install -that deliberately runs Collabora will have its editor swapped silently on the next image pull. - -The flag (`eurooffice_default_migration_v1`) prevents re-fighting an admin who switches back after -migration. But there is no rollback: once migrated, Collabora/OnlyOffice must be re-enabled manually. - -**Open question for James:** Is the silent force-switch the intended behaviour for all existing forks -of this repo, or should it only apply when no editor has been explicitly set? - -If the answer is "force-switch is correct" → current code is right. -If "preserve explicit Collabora installs" → change the migration to check whether an explicit choice -was already made (e.g., only migrate if `isCollaboraEnabled` is still at its factory default). - -### 2. Preview provider index fix (bug fixed) - -`occ config:system:set enabledPreviewProviders` in AIO seeds indices 1–7 on fresh install, and also -uses index 23 for Imaginary. The previous approach of computing the next index via `wc -l` would -produce 7 on a standard install, which collides with the Krita entry at index 7 — silently removing -Krita previews. - -**Fixed:** replaced dynamic `wc -l` with fixed index 50. AIO never exceeds index 23 in its own -seeding; 50 is safe for the foreseeable set of providers. - -### 3. Local-only validation gap - -All testing was done in a local environment where `nextcloud.test` resolves to `127.0.0.1` inside -containers (host `/etc/hosts` is embedded into Docker DNS). The internal URLs work around this. In a -production install where DNS resolves correctly, the public-domain code path would be used instead -of the internal Docker URLs — but the code we added runs only when `$EUROOFFICE_HOST` matches the -`nextcloud-.*-eurooffice` pattern, which is the AIO default naming. That pattern is stable. - -**What is reasoned but not yet tested on a clean prod-like install:** that port 23973 routes -EuroOffice→NC callbacks correctly end-to-end without the local DNS workaround in play. The port is -the documented Collabora WOPI ingress (confirmed in containers.json:394) so the reasoning is sound. - ---- - -## Commits - -Three atomic commits in logical order: - -### Commit 1 — Caddyfile X-Forwarded-Prefix fix - -``` -fix(apache): send X-Forwarded-Prefix for EuroOffice SDK assets - -EuroOffice nginx uses $http_x_forwarded_prefix to construct SDK -asset URLs (e.g. /eurooffice/sdkjs/...). Without this header the -prefix is empty and the browser requests /sdkjs/... which Caddy -routes to Nextcloud → 404. - -Send X-Forwarded-Prefix as a separate header (not appended to -X-Forwarded-Host as OnlyOffice does) to match EuroOffice nginx -expectations. X-Forwarded-Host continues to carry host only. -``` - -**Files:** `Containers/apache/Caddyfile` - -Note: this fix is also applicable upstream to `nextcloud/all-in-one` as a standalone PR (no other -changes needed). - -### Commit 2 — Make EuroOffice the default editor - -``` -feat: make EuroOffice the default editor for new and existing installs - -- isEuroofficeEnabled default: false → true -- isCollaboraEnabled default: true → false -- Add eurooffice to STARTUP_APPS -- performMigrations(): one-time force-switch for existing installs - (guarded by eurooffice_default_migration_v1 flag to prevent - re-fighting an admin who switches back) -- Call performMigrations() at index.php bootstrap -``` - -**Files:** `php/src/Data/ConfigurationManager.php`, `php/public/index.php` - -### Commit 3 — Configure EuroOffice internal URLs and preview provider - -``` -fix(nextcloud): configure EuroOffice internal URLs and preview provider - -Three fixes applied in entrypoint.sh when $EUROOFFICE_HOST matches -the default AIO container naming pattern: - -1. DocumentServerInternalUrl → http://$EUROOFFICE_HOST:80/ - Bypasses the public domain (which containers cannot resolve in - many setups) for NC→EuroOffice converter calls. Trailing slash - required; DocumentService.php concatenates the raw value with - "converter" (no separator). - -2. StorageUrl → http://$APACHE_CONTAINER_HOST.nextcloud-aio:23973/ - Bypasses the public domain for EuroOffice→NC file fetch calls. - Port 23973 is the Collabora WOPI ingress (server block matches - the container FQDN; port 11000 rejects the wrong Host header). - Trailing slash required; str_replace strips the / from the origin. - -3. enabledPreviewProviders index 50 → OCA\Eurooffice\Preview - NC's allowlist is explicit; registerPreviewProvider() alone is - insufficient. Index 50 avoids collision with AIO's seeded range - (1-7, 23). -``` - -**Files:** `Containers/nextcloud/entrypoint.sh` - ---- - -## Build and Deploy - -After committing, the apache image must be rebuilt for the Caddyfile change to take effect: - -```bash -# Rebuild apache image -docker buildx build \ - --file Containers/apache/Dockerfile \ - --tag ghcr.io/nextcloud-releases/aio-apache:beta \ - --load \ - Containers/apache - -# Rebuild mastercontainer (for ConfigurationManager + index.php changes) -docker buildx build \ - --file Containers/mastercontainer/Dockerfile \ - --tag ghcr.io/nextcloud-releases/all-in-one:develop \ - --load . -``` - -Then restart the AIO stack via the AIO UI at `https://localhost:9090`. -Use `?bypass_mastercontainer_update` in the URL to skip self-update prompt. - ---- - -## Upstream PR (Optional) - -The Caddyfile fix (commit 1) is a clean, standalone bugfix that applies to -`nextcloud/all-in-one` regardless of EuroOffice. The other two commits are -fork-specific and should not go upstream. - -Before opening the upstream PR, confirm that OnlyOffice's `X-Forwarded-Host -host/onlyoffice` pattern is intentional for OO (it works differently) so the -PR can be scoped to EuroOffice only. - ---- - -## Remaining Items - -- [ ] Docker Desktop restart — apply `daemon.json` `"dns": ["192.168.97.1"]` for - permanent DNS fix. Non-urgent: internal URL workaround is in place. -- [ ] Decision on migration behaviour (see §1 above) — confirm or adjust - `performMigrations()` before tagging a release. -- [ ] Validate on a clean install without the local DNS workaround.