Files
computer-playbook/roles/web-app-nextcloud/tasks/addons/integration_discourse.yml
kevinveenbirkenbachandClaude Opus 5 d926dd0832 fix(nextcloud): keep an onion Discourse URL on http in the settings bundle
The integration_discourse settings page rewrites what the user types, and the
rewrite is unconditional. src/components/PersonalSettings.vue:217-224 upstream:

    if (this.state.url !== '' && !this.state.url.startsWith('https://')) {
      if (this.state.url.startsWith('http://')) {
        this.state.url = this.state.url.replace('http://', 'https://')

An onion service carries no certificate and is served over plain http through
Tor, so on such a node the address is rewritten to an endpoint that does not
exist. Run 31658270172, job 94319317649 shows all three steps: the trace's
action log records the spec typing `http://forum.<onion>/`, the PUT to
/apps/integration_discourse/config carries `{"values":{"url":"https://forum.
<onion>/"}}`, and the connect navigation to `https://forum.<onion>//user-api-
key/new` fails with network status -1, which Playwright reports as
`Received string: "chrome-error://chromewebdata/"`.

The app ships compiled JS, so the fix has to reach the bundle rather than the
source. It is measured against the file the appstore actually installs --
nextcloud-releases/integration_discourse v3.3.0, whose
integration_discourse-personalSettings.mjs is the exact filename the trace
loaded from the container; the nextcloud/integration_discourse releases stop at
v2.0.1 in 2023 and still ship .js, which is why the earlier reading of that repo
was misleading.

Verified rather than argued:

  - the anchor `startsWith("https://")&&(` occurs exactly once in that file
  - after the patch the guard reads
    `…startsWith("https://")&&!this.state.url.includes(".onion")&&(…`
    and `node --check` accepts the bundle as valid ESM
  - the anchor no longer exists afterwards, so a second application is a no-op:
    idempotency is structural, not a marker check
  - the task was run through a real ansible-playbook against a real copy of the
    bundle with only the exec prefixes stubbed: first run changed=1 and patched,
    second run changed=0, so changed_when reports honestly
  - busybox sed (alpine:3, "This is not GNU sed version 4.0") produces the same
    result as GNU sed, which matters because the image is 34-fpm-alpine

`occ app:getpath` locates the app instead of hardcoding custom_apps, which
appears nowhere in the repo and which the appstore may move. The `grep -q … ||
exit 0` makes the task both idempotent and safe when the file is absent.

Clearnet is untouched: the added condition is false for every non-onion URL, so
the upgrade still happens where TLS is available.

Not verified: what `app:getpath` returns in this installation. If the path is
wrong the task no-ops silently -- the same shape as the WordPress healthcheck,
where msmtp fails and a passing curl hides it. A `test -f` with a loud failure
would close that, and the layer behind the redirect has never been reached by a
green run.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 02:02:30 +02:00

21 lines
1.2 KiB
YAML

---
- name: "🩹 Let the Discourse settings keep an onion URL on http"
# Exception: sed reads a bare & in the replacement as the whole match, so the
# && of the injected condition MUST stay escaped as \&\& or the guard breaks.
# TODO: this patches a vendored bundle and belongs upstream. onInput() in
# src/components/PersonalSettings.vue:217-224 rewrites http:// to https://
# unconditionally, which breaks an onion forum that cannot carry TLS (CI run
# 31658270172, job 94319317649: typed http://forum.<onion>/, stored https://,
# navigation failed with status -1). Send the .onion guard to
# nextcloud/integration_discourse and empty this file once a release ships it.
ansible.builtin.shell: |
app_dir="$({{ NEXTCLOUD_DOCKER_EXEC_OCC }} app:getpath integration_discourse)"
bundle="${app_dir}/js/integration_discourse-personalSettings.mjs"
{{ NEXTCLOUD_DOCKER_EXEC }} grep -q 'startsWith("https://")&&(' "$bundle" || exit 0
{{ NEXTCLOUD_DOCKER_EXEC }} sed -i 's|startsWith("https://")&&(|startsWith("https://")\&\&!this.state.url.includes(".onion")\&\&(|' "$bundle"
echo PATCHED
args:
executable: /bin/bash
register: nextcloud_discourse_onion_patch
changed_when: "'PATCHED' in nextcloud_discourse_onion_patch.stdout"