7.3.1. Nextcloud ExApps
ExApps, Glossar, öffnet in neuem Tab
sind Nextcloud-Apps, die nicht im PHP-Server laufen, sondern als eigener Container. AppAPI, Glossar, öffnet in neuem Tab
steuert Deploy und Lebenszyklus; HaRP, Glossar, öffnet in neuem Tab
ist der Deploy Daemon, Glossar, öffnet in neuem Tab
und der Proxy unter /exapps/. Die Go-Libraries darunter kapseln Auth, OCS, Glossar, öffnet in neuem Tab
und Dateizugriff – Lifecycle-Routen und UI-Registrierung bleiben in der ExApp.
Englische Fachbegriffe sind im Glossar erklärt.
HaRP übernimmt zwei Rollen:
- Deploy Daemon – AppAPI baut aus
info.xmlund den Deploy-Optionen eine Container-Anfrage und POSTet sie an HaRP. HaRP spricht mit dem Docker Engine (containers/create, dannstart). - Proxy unter
/exapps/– Browser und AppAPI erreichen die ExApp über denselben Pfad. Der Tunnel zur ExApp läuft über FRP, Glossar, öffnet in neuem Tab (Standardport8782).
AppAPI entscheidet, was deployed wird. Bei einem Daemon vom Typ docker-install startet HaRP einen Container aus dem einen Image in <docker-install>. Es gibt kein Feld in info.xml für Compose-Dateien, Sidecars oder ein zweites Image.
flowchart LR Browser --> Proxy Proxy -->|"/exapps/"| HaRP HaRP -->|FRP| ExApp ExApp -->|"OCS / WebDAV<br/>AppAPI-Header"| Nextcloud ExApp -->|"Authorization: Bearer<br/>User Token"| Microservice
| ExApp | Microservice, Glossar, öffnet in neuem Tab | |
|---|---|---|
| Registriert bei Nextcloud | ja (AppAPI) | nein |
Erreichbar über HaRP /exapps/ |
ja | nein |
Sieht AUTHORIZATION-APP-API |
ja | nein |
| Auth zum Aufrufer | AppAPI-Header mit User-Id und APP_SECRET |
kurzlebiger User Token, Glossar, öffnet in neuem Tab (go-usertoken) |
Die ExApp ist die Nextcloud-Seite. Microservices (Datenbanken, Fachdienste) liegen in einem eigenen Compose und hängen am selben Docker-Netz wie der Daemon (--net).
Der Nutzer ist bereits in Nextcloud eingeloggt. Der Browser kommt über HaRP zur ExApp. AppAPI setzt die Header:
| Header | Inhalt |
|---|---|
AA-VERSION |
AppAPI-Protokollversion |
EX-APP-ID |
ExApp-Id |
EX-APP-VERSION |
ExApp-Version |
AUTHORIZATION-APP-API |
Base64 von userId:secret |
userId ist die Nextcloud-Login-Id – der Requesting User, Glossar, öffnet in neuem Tab
. secret ist APP_SECRET der ExApp. Gruppen, Anzeigename und Dateirechte stecken nicht im Header – die ExApp holt sie mit Folgeaufrufen als dieser Nutzer.
Ausgehende Aufrufe nach Nextcloud nutzen dieselbe Header-Form (Credentials.AuthHeaders, WithUser). Wer APP_SECRET hat, kann als beliebige User-Id sprechen; der Secret bleibt daher nur auf der ExApp.
Details und Prüfungsschritte: go-nc-exapp.
Über OCS (JSON) typisch:
- ExApp Preferences, Glossar, öffnet in neuem Tab – ein String pro Schlüssel und Nutzer für diese ExApp
- Notifications, Glossar, öffnet in neuem Tab – eine Glocke-Benachrichtigung pro Aufruf an genau einen Recipient, Glossar, öffnet in neuem Tab
- Groups – Gruppen des Nutzers, Mitglieder einer Gruppe, Gruppenliste (Rechte des Requesting Users gelten; siehe Users and Groups, Glossar, öffnet in neuem Tab )
Dateizugriff als Requesting User läuft über Nextcloud Files (WebDAV). Dafür: go-nc-files (hängt an go-nc-exapp für die AppAPI Credentials, Glossar, öffnet in neuem Tab ).
Beim Enable registriert die ExApp per OCS Top-Menü und Script, z. B.:
// PUT /enabled?enabled=1
ocsJSON(http.MethodPost, "/ocs/v2.php/apps/app_api/api/v1/ui/top-menu", map[string]any{
"name": "checkdns",
"displayName": "CheckDNS",
"icon": "img/app.svg",
"adminRequired": adminRequired, // "0" oder "1"
})
ocsJSON(http.MethodPost, "/ocs/v2.php/apps/app_api/api/v1/ui/script", map[string]any{
"type": "top_menu",
"name": "checkdns",
"path": "js/checkdns-main",
})
adminRequired steuert die Top Menu Visibility, Glossar, öffnet in neuem Tab
(wer das Icon sieht). go-nc-exapp liefert TopMenuAdminRequired aus der Deploy-Env TOP_MENU_ADMIN_REQUIRED; die OCS-Registrierung selbst bleibt in der ExApp. Optional gibt es eine Files-ähnliche App Navigation, Glossar, öffnet in neuem Tab
in go-nc-exapp.
AppAPI spricht die ExApp direkt an (nicht der Browser-Nutzer):
| Route | Rolle |
|---|---|
GET /heartbeat |
Health; AppAPI wartet bis zu ~90 s nach dem Start |
POST /init |
Initialisierung; Fortschritt bis 100 |
PUT /enabled?enabled=1|0 |
UI registrieren bzw. abmelden |
Access Gate, Glossar, öffnet in neuem Tab und Gruppenprüfungen (Required Groups, Glossar, öffnet in neuem Tab ) überspringen diese Pfade. Die Secret-Prüfung auf User-Routen bleibt davon unberührt. go-nc-exapp implementiert die Lifecycle-Handler nicht – die ExApp tut das selbst.
Microservices sehen keine AppAPI-Header. Die ExApp mintet nach erfolgreicher AppAPI-Prüfung einen kurzlebigen RS256-Token (sub = User-Id, optional groups) und schickt Authorization: Bearer …. Siehe go-usertoken.
Zwei Nextcloud-URLs – oft verwechselt:
| Name | Wo gesetzt | Wer ruft an |
|---|---|---|
| Daemon Nextcloud URL | app_api:daemon:register Argument nextcloud_url |
AppAPI → {URL}/exapps/… (Heartbeat, Steuerung) |
NC_INSTANCE_URL |
HaRP-Environment | ExApp-Container → Nextcloud (OCS, WebDAV) |
Sie sind gleich, wenn derselbe Host Nextcloud ausliefert und /exapps/ an HaRP weiterleitet. Sie differieren, wenn ein Reverse Proxy /exapps/ besitzt.
Typisches Shared-Network-Layout (Platzhalter anpassen):
| Dienst | Rolle |
|---|---|
| Reverse Proxy (z. B. Caddy) | Öffentlicher Einstieg: / → Nextcloud, /exapps/ → HaRP |
| Nextcloud | PHP-Instanz, oft ohne eigenen Host-Port |
| HaRP | Hostname z. B. appapi-harp; Docker-Socket gemountet; NC_INSTANCE_URL zeigt auf Nextcloud im Netz |
| Netz | gemeinsames Docker-Netz, z. B. YOUR_NETWORK |
ExApps stehen nicht in diesem Compose – AppAPI/HaRP startet sie auf derselben Docker-Engine.
Caddy hinter einem öffentlichen Host:
https://cloud.example {
handle /exapps/* {
reverse_proxy appapi-harp:8780 {
transport http {
read_timeout 1800s
}
}
}
handle {
reverse_proxy nextcloud:80
}
}
1800s entspricht dem HaRP-Default HP_TIMEOUT_SERVER. Im Docker-Netz ist der Upstream appapi-harp:8780 (oder der Hostname deines HaRP-Containers).
Nur lokal ohne TLS, Port am Proxy:
:8080 {
handle /exapps/* {
reverse_proxy appapi-harp:8780 {
transport http {
read_timeout 1800s
}
}
}
handle {
reverse_proxy nextcloud:80
}
}
- HaRP HTTP (ExApps):
8780(HP_EXAPPS_ADDRESS) - FRP:
8782– ExApp-Container müssen den erreichen - Im Shared-Network-Layout bleiben
8780/8782oft unveröffentlicht auf dem Host; nur der Reverse Proxy publiziert den Einstieg - Lab ohne TLS vor FRP:
HP_FRP_DISABLE_TLS=true - Hinter einem Proxy:
HP_TRUSTED_PROXY_IPSsetzen (z. B. dein Proxy-Subnetz)
Beispiel-Zuordnung der beiden URLs:
- Daemon Nextcloud URL =
http://proxy.example:8080(nur der Proxy leitet/exapps/an HaRP) - HaRP
NC_INSTANCE_URL=http://nextcloud(ExApp → Nextcloud im Docker-Netz)
Heartbeat-Fehler nach „Test deploy“ bedeuten meist: die Daemon-URL zeigt nicht auf die Front, die /exapps/ an HaRP schickt.
AppAPI ist ab Nextcloud 30.0.1 dabei. Falls aus:
# Host / klassisch
sudo -E -u www-data php occ app:enable app_api
# Nextcloud im Container
docker exec -u www-data nextcloud php occ app:enable app_api
Generisches Beispiel (Shared Network, Platzhalter ersetzen):
docker exec -u www-data nextcloud php occ app_api:daemon:register \
harp_proxy_docker "HaRP Proxy (Docker)" docker-install http \
appapi-harp:8780 "https://cloud.example" \
--net YOUR_NETWORK \
--harp \
--harp_frp_address "appapi-harp:8782" \
--harp_shared_key "YOUR_HARP_SHARED_KEY" \
--set-default
Klassisch auf dem Host (ohne docker exec):
sudo -E -u www-data php occ app_api:daemon:register \
harp_proxy_docker "HaRP Proxy (Docker)" docker-install http \
appapi-harp:8780 "https://cloud.example" \
--net YOUR_NETWORK \
--harp \
--harp_frp_address "appapi-harp:8782" \
--harp_shared_key "YOUR_HARP_SHARED_KEY" \
--set-default
Wichtige Argumente: accepts-deploy-id = docker-install, Host = HaRP von Nextcloud aus erreichbar, --harp plus --harp_shared_key und --harp_frp_address, --set-default für Installs ohne expliziten Daemon-Namen. Shared Key = HP_SHARED_KEY in der HaRP-Umgebung.
docker exec -u www-data nextcloud php occ app_api:daemon:list
UI: Administration → AppAPI → Deploy daemons. Vorlage HaRP Proxy (Docker) für Shared Network; HaRP Proxy (Host) bei publizierten Ports. Danach Test deploy.
docker-install ohne --harp ist deprecated (Entfernung vorgesehen in Nextcloud 35). Neue Daemons mit --harp.
Image-Koordinaten stehen in appinfo/info.xml. Beispiel CheckDNS:
<external-app>
<docker-install>
<registry>gitea.neitzel.de</registry>
<image>konrad/checkdns</image>
<image-tag>0.1.14</image-tag>
</docker-install>
</external-app>
Das Image muss für die Engine unter HaRP pullbar sein (oder lokal unter genau diesem Namen getaggt).
# Registrieren (Pfad zu info.xml muss im Nextcloud-Container existieren,
# oder https://-URL). App-Id und Daemon-Name anpassen.
docker exec -u www-data nextcloud php occ app_api:app:register your-app-id harp_proxy_docker \
--info-xml /path/inside/container/appinfo/info.xml \
--wait-finish \
--env NAME=VALUE
docker exec -u www-data nextcloud php occ app_api:app:list
docker exec -u www-data nextcloud php occ app_api:app:disable your-app-id
docker exec -u www-data nextcloud php occ app_api:app:enable your-app-id
docker exec -u www-data nextcloud php occ app_api:app:update your-app-id \
--info-xml /path/inside/container/appinfo/info.xml
docker exec -u www-data nextcloud php occ app_api:app:unregister your-app-id
# unregister behält das Persistenz-Volume; --rm-data löscht es; --force bei Cleanup-Fehlern
--env und --mount wiederholen; sie gelten für Optionen, die die ExApp in info.xml deklariert. UI: Apps → ExApp → Install; Deploy options setzen Env/Mounts vor dem Deploy.
Daemon entfernen erst, wenn keine ExApps mehr daran hängen: app_api:daemon:unregister <daemon-config-name>.
Das Top-Menü-Feld icon ist ein Pfad relativ zur ExApp (z. B. img/app.svg). Die ExApp liefert die Datei selbst (GET /img/app.svg). Für Navigations-Einträge in der optionalen App-Shell kann go-nc-exapp Core-SVGs unter /core/img/… referenzieren (NextcloudIcons) – das sind Nextcloud-Core-Pfade, keine eingebetteten Bytes in der Library.
<registry>, <image>, <image-tag> bilden den Pull-Namen, z. B. gitea.neitzel.de/konrad/{exapp-id} mit explizitem Tag (kein automatisches latest). Lokal denselben Namen taggen, dann kann AppAPI ohne Push deployen.
AppAPI setzt u. a. auf dem Container:
| Variable | Bedeutung |
|---|---|
AA_VERSION |
AppAPI-Protokollversion |
APP_SECRET |
Shared Secret für AUTHORIZATION-APP-API |
APP_ID / APP_DISPLAY_NAME / APP_VERSION |
Identität |
APP_HOST / APP_PORT |
Listen-Ziel aus Sicht AppAPI |
APP_PERSISTENT_STORAGE |
Volume über Updates hinweg |
NEXTCLOUD_URL |
Nextcloud-URL für die ExApp |
Zusätzliche Produkt-Variablen deklarierst du unter <environment-variables> und setzt sie per --env oder Deploy options (REQUIRED_GROUPS, TOP_MENU_ADMIN_REQUIRED, …). Env-Änderungen wirken nach Container-Neustart; Top-Menü zusätzlich nach Disable/Enable bzw. Update, weil die Registrierung bei PUT /enabled läuft.
Datenbanken, Caches und Microservices gehören in ein eigenes docker-compose.yml, Netz external: true mit dem Daemon---net (z. B. YOUR_NETWORK). Die ExApp bekommt die URL als deklarierte Deploy-Variable, z. B. --env LISTS_URL=http://lists:8080. info.xml beschreibt das nicht.
- 7.3.1.1. go-nc-exapp – AppAPI-Auth, OCS, Preferences, Notifications, Groups, Access Gate, Top Menu Visibility, optionale App Navigation und Dialog, Glossar, öffnet in neuem Tab
Repo: gitea.neitzel.de/konrad/go-nc-exapp - 7.3.1.2. go-nc-files – Dateizugriff (lokal oder WebDAV), Saved Default, Visit-/Working-Folder
Repo: gitea.neitzel.de/konrad/go-nc-files - Sibling: 7.3.2. go-usertoken – User Tokens ExApp → Microservice
Repo: gitea.neitzel.de/konrad/go-usertoken
Klonen über SSH-Port 2222, z. B. ssh://git@gitea.neitzel.de:2222/konrad/go-nc-exapp.git. Für go get ohne öffentlichen Proxy: GOPRIVATE=gitea.neitzel.de.