Initial commit: Nextcloud Node-RED Docker image and custom nodes

This commit is contained in:
newkle3r
2026-05-15 14:50:48 +02:00
commit fd7cc695f7
44 changed files with 3936 additions and 0 deletions
+81
View File
@@ -0,0 +1,81 @@
# Architecture
## Two components
### 1. `nextcloud-node-red` (Docker + nodes)
A thin wrapper around the official [`nodered/node-red`](https://hub.docker.com/r/nodered/node-red) image:
1. **Build time:** `nodes/nextcloud-ocs/` is copied to `/opt/nextcloud-nodes/node-red-contrib-nextcloud-ocs`.
2. **Run time:** `entrypoint.sh` runs as container `ENTRYPOINT`, copies that tree into `/data/node_modules/node-red-contrib-nextcloud-ocs`, then starts `node-red --userDir /data`.
Node-RED discovers contrib nodes from `{userDir}/node_modules/*/package.json``node-red.nodes` map.
### 2. `nodered-embed` (Nextcloud app)
PHP app registered as `nodered_embed`:
- **Navigation** entry opens a full-page iframe to the configured Node-RED URL.
- **CSPListener** relaxes Content-Security-Policy so Nextcloud may embed Node-RED (`frame-src`) and Node-RED may embed Nextcloud pages if needed (`frame-ancestors`).
- **Admin settings** store `nodered_url`, optional Docker container name, and a Docker-management flag (UI only for now).
## Node implementation pattern
Each functional node follows the same structure:
```
collectives.js → OPERATIONS hash (operationId → method, path, body fields)
collectives.html → Editor UI, dropdown of operations, config node picker
nextcloud-config.js → Credential node (baseUrl, username, password)
```
On input:
1. Resolve `msg.operation` or the configured default operation.
2. Substitute path placeholders (`{id}`, `{collectiveId}`, …) from config + `msg.*`.
3. Build query string and JSON body from configured fields + `msg` overrides.
4. HTTP request with `OCS-APIRequest: true`, `Accept: application/json`, Basic Auth.
5. Parse JSON into `msg.payload`, set `msg.statusCode`, `node.send(msg)`.
**ocs-api** is the escape hatch: you supply any OCS path and method without adding a new operation to the hash.
## Data persistence
| Path | Contents |
|------|----------|
| `/data/flows.json` | Flows (Docker volume `node-red-data`) |
| `/data/flows_cred.json` | Encrypted credentials |
| `/data/node_modules/node-red-contrib-nextcloud-ocs/` | Copy of custom nodes (refreshed on each start) |
The named volume **masks** anything copied into `/data` at image build time. That is why the entrypoint always re-copies from `/opt`.
## Network
```
Node-RED container (--network host)
→ https://cloud.example.com (same host or LAN)
```
On Ubuntu, a bridge network often breaks `localhost` / hostname resolution (`127.0.1.1` in `/etc/hosts`). Host networking avoids that for co-located Nextcloud + Node-RED.
## Authentication flow
```
Editor: nextcloud-config node
→ stored in flows_cred.json (encrypted)
Runtime: each API node
→ RED.nodes.getNode(config.nextcloud)
→ credentials.username + credentials.password
→ Authorization: Basic base64(user:app-password)
```
Use **app passwords**, not the account login password.
## Versioning
Package version in `nodes/nextcloud-ocs/package.json` (e.g. `0.8.0`) should be bumped when adding nodes or breaking operation ids. After deploy, verify inside the container:
```bash
docker exec nextcloud-node-red cat /data/node_modules/node-red-contrib-nextcloud-ocs/package.json
```
+138
View File
@@ -0,0 +1,138 @@
# Deployment guide
## Target layout (example)
| Service | Host | Port |
|---------|------|------|
| Nextcloud | `https://cloud.example.com` | 443 |
| Node-RED | same VM, `192.168.1.26` | 1880 |
Node-RED must be reachable from users browsers (for the iframe) and from itself (for API calls to Nextcloud).
## 1. Deploy Node-RED (Docker)
On the Ubuntu VM:
```bash
cd /home/ncadmin/nextcloud-node-red # or your clone path
git pull # get latest nodes + entrypoint
docker compose build --no-cache
docker compose up -d
```
Or without Compose:
```bash
docker build --no-cache -t nextcloud-node-red:latest .
docker rm -f nextcloud-node-red 2>/dev/null || true
docker run -d \
--name nextcloud-node-red \
--network host \
-v node-red-data:/data \
--restart unless-stopped \
nextcloud-node-red:latest
```
Verify:
```bash
docker logs -n 30 nextcloud-node-red
# Expect: [entrypoint] Installing nextcloud nodes...
# User directory : /data
curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:1880/
# Expect: 200
```
### Nextcloud URL in config nodes
Inside Node-RED, set **Nextcloud URL** to something the **container** can resolve:
| Scenario | URL |
|----------|-----|
| Nextcloud on same VM, host networking | `https://127.0.0.1` or `https://cloud.example.com` |
| Nextcloud on another host | `https://192.168.1.x` or public hostname |
Test from inside the container:
```bash
docker exec nextcloud-node-red wget -qO- --no-check-certificate \
https://cloud.example.com/status.php
```
## 2. Deploy Nextcloud app
```bash
sudo cp -r nodered-embed /var/www/nextcloud/apps/
sudo chown -R www-data:www-data /var/www/nextcloud/apps/nodered-embed
sudo -u www-data php /var/www/nextcloud/occ app:enable nodered_embed
```
Configure in **Settings → Administration → Node-RED Embed**:
- **Node-RED URL:** `http://192.168.1.26:1880` (use the address clients use; not `127.0.0.1` unless only local admins use it)
## 3. Reverse proxy (optional)
If Node-RED is behind nginx/Apache with TLS:
- Ensure the proxy does **not** send `X-Frame-Options: DENY` (blocks iframe embed).
- Point Nextcloud admin URL to the public HTTPS URL, e.g. `https://nodered.example.com`.
If Nextcloud is behind a proxy, CSP host extraction uses the hostname from `nodered_url` — use the same hostname users load in the iframe.
## 4. Firewall
Allow **1880/tcp** (or your mapped port) from Nextcloud users networks if they open the embed from LAN/VPN.
## 5. Updating custom nodes
After changing files under `nodes/nextcloud-ocs/`:
```bash
docker builder prune -af # optional, avoids stale COPY cache
docker build --no-cache -t nextcloud-node-red:latest .
docker restart nextcloud-node-red
```
Confirm version:
```bash
docker exec nextcloud-node-red cat \
/data/node_modules/node-red-contrib-nextcloud-ocs/package.json | grep version
```
### When to delete the volume
Delete `node-red-data` only if:
- Palette shows wrong/old nodes after rebuild, or
- Entrypoint copy failed with permission errors from old root-owned files
**Warning:** removes all flows and credentials.
```bash
docker rm -f nextcloud-node-red
docker volume rm node-red-data
docker compose up -d
```
## 6. Backup
Back up the Docker volume:
```bash
docker run --rm -v node-red-data:/data -v $(pwd):/backup alpine \
tar czf /backup/node-red-data-backup.tar.gz -C /data .
```
Restore by extracting into a new volume before first start.
## 7. Production checklist
- [ ] Set `credentialSecret` in Node-RED settings (`/data/settings.js`)
- [ ] Use app passwords with minimal needed scopes
- [ ] TLS on Nextcloud; consider TLS on Node-RED if exposed beyond LAN
- [ ] Restrict who can access Node-RED (firewall / VPN / admin-only NC group)
- [ ] Enable Nextcloud app only for trusted admins if flows can access sensitive data
+135
View File
@@ -0,0 +1,135 @@
# Node reference
Package: **`node-red-contrib-nextcloud-ocs`** (see `nodes/nextcloud-ocs/package.json`).
All API nodes require a **nextcloud-config** configuration node unless noted.
## nextcloud-config
Configuration node — not placed on flows alone.
| Field | Description |
|-------|-------------|
| Name | Label in the editor |
| Nextcloud URL | Base URL, e.g. `https://cloud.example.com` |
| Username | Nextcloud account name |
| Password | **App password** (stored encrypted) |
## ocs-api
Generic OCS/HTTP caller.
| Config | Default |
|--------|---------|
| Endpoint | `/ocs/v2.php/cloud/user` |
| Method | `GET` |
**Runtime:** `msg.endpoint`, `msg.method`, `msg.body`, `msg.headers`.
## collectives
Nextcloud **Collectives** app. Operations use ids like `collective:list`, `page:create`, `share:updatePageShare`, etc.
Categories include: collectives, pages, trash, shares, templates, tags, user settings, sessions, public (token) APIs, search.
~80 operations — see `collectives.js` `OPERATIONS` for the full list.
## file-operations
| Operation | Protocol |
|-----------|----------|
| `list`, `get`, `upload`, `mkdir`, `move`, `copy`, `delete` | WebDAV `/remote.php/dav/files/{user}/…` |
| `listShares`, `createShare`, `deleteShare` | OCS sharing API |
| `fileInfo`, `favorites` | OCS files API |
Path placeholders: `{user}`, `{folder}`, `{path}`, `{id}`.
## mail
Mail app OCS endpoints (send, list messages, accounts, etc.). See `mail.js`.
## tables
Tables app — tables, views, rows, columns, shares. See `tables.js`.
## talk
Talk (Spreed) — rooms, messages, reactions, polls, bots, breakout rooms, etc. Largest node (~80+ operations). See `talk.js`.
## webhooks
Webhook Listeners app:
| Operation | Method |
|-----------|--------|
| `webhook:list` | GET |
| `webhook:get` | GET |
| `webhook:create` | POST |
| `webhook:update` | POST |
| `webhook:delete` | DELETE |
| `webhook:deleteByApp` | DELETE |
## dashboard
Dashboard widgets API. See `dashboard.js`.
## dav
DAV-related OCS (direct links, out-of-office). See `dav.js`.
## core
Nextcloud **core** OCS and related APIs (~50 operations), including:
- Status, capabilities, app passwords
- Navigation, profile, hover card
- Collaboration resources, references, previews, avatars
- CSRF, login flow v2, wipe, OCM discovery
- Unified search, task processing, text processing
- Translation, text-to-image, teams
Operation ids use prefixes: `status:`, `capabilities:`, `appPassword:`, `search:`, `task:`, etc. See `core.js`.
## oauth2
OAuth2 client management OCS. See `oauth2.js`.
## provisioning
User and group provisioning API. See `provisioning.js`.
## filesharing
Dedicated file sharing OCS (beyond WebDAV in file-operations). See `filesharing.js`.
## userstatus
User status (emoji/message). See `userstatus.js`.
## settings
User and admin settings OCS. See `settings.js`.
---
## Common message properties
| Property | Used by |
|----------|---------|
| `msg.operation` | All operation-based nodes |
| `msg.payload` | Response body (output) |
| `msg.statusCode` | HTTP status (output) |
| `msg.error` | Error string on failure |
Path parameters are typically available as `msg.<name>` matching the placeholder (e.g. `msg.collectiveId`, `msg.pageId`, `msg.webhookId`).
Body fields configured in the editor can often be overridden via `msg.body` object or node-specific `msg.body*` fields — check each nodes `buildBody` / handler in the `.js` file.
## Adding a new operation
1. Add entry to `OPERATIONS` in the relevant `.js` file.
2. Add dropdown option and fields in the matching `.html` file.
3. Bump `version` in `package.json`.
4. Rebuild Docker image and restart container.
For one-off APIs, use **ocs-api** instead of extending a dedicated node.
+166
View File
@@ -0,0 +1,166 @@
# Troubleshooting
## Nodes missing from palette
### Symptom
Node-RED runs but Nextcloud nodes do not appear under **Manage palette → Nodes**.
### Checks
1. **Entrypoint ran**
```bash
docker logs nextcloud-node-red 2>&1 | head -20
```
Expect:
```
[entrypoint] Installing nextcloud nodes into /data...
[entrypoint] Source: /opt/nextcloud-nodes/node-red-contrib-nextcloud-ocs
[entrypoint] Target: /data/node_modules/node-red-contrib-nextcloud-ocs
```
If you only see the Node-RED banner with no `[entrypoint]` lines, the image may still use `CMD` instead of `ENTRYPOINT ["/entrypoint.sh"]` — rebuild from current `Dockerfile`.
2. **Module installed in userDir**
```bash
docker exec nextcloud-node-red ls /data/node_modules/node-red-contrib-nextcloud-ocs
docker exec nextcloud-node-red cat /data/node_modules/node-red-contrib-nextcloud-ocs/package.json
```
3. **User directory**
Logs must show `User directory : /data`. If it shows `/usr/src/node-red/.node-red/`, entrypoint did not start Node-RED with `--userDir /data`.
4. **Stale volume (old package version)**
Compare `/opt` vs `/data`:
```bash
docker exec nextcloud-node-red sh -c \
'grep version /opt/nextcloud-nodes/node-red-contrib-nextcloud-ocs/package.json; \
grep version /data/node_modules/node-red-contrib-nextcloud-ocs/package.json'
```
If `/opt` is newer, restart should copy on boot. If `/data` stays old, remove volume (loses flows):
```bash
docker rm -f nextcloud-node-red
docker volume rm node-red-data
docker run -d --name nextcloud-node-red --network host \
-v node-red-data:/data --restart unless-stopped nextcloud-node-red:latest
```
5. **Browser cache**
Hard refresh (Ctrl+F5) or open Node-RED in a private window.
---
## Container restart loop
### Symptom
`docker ps` shows `Restarting`; logs repeat entrypoint lines.
### Cause
Old `entrypoint.sh` tried to copy missing `settings.js`:
```
cp: can't stat '/usr/src/node-red/.node-red/settings.js': No such file or directory
```
### Fix
Use current `entrypoint.sh` (starts `node-red --userDir /data` only). Rebuild:
```bash
docker build --no-cache -t nextcloud-node-red:latest .
```
---
## `No Nextcloud configuration node selected`
Flow nodes reference a deleted or unconfigured **nextcloud-config** node. Open each red Nextcloud node and re-select the config node, then Deploy.
---
## API errors / connection refused
| Issue | Fix |
|-------|-----|
| `ECONNREFUSED` to localhost | Use `--network host` or use LAN IP / hostname, not `127.0.0.1` from bridge network |
| SSL errors | Self-signed: nodes set `rejectUnauthorized: false`; or install proper certs |
| 401 Unauthorized | Use **app password**, correct username |
| 404 on OCS path | App not installed/enabled on Nextcloud (Collectives, Talk, etc.) |
Test from container:
```bash
docker exec nextcloud-node-red wget -qO- --header="OCS-APIRequest: true" \
--user='USER:APP_PASSWORD' \
https://cloud.example.com/ocs/v2.php/cloud/user
```
---
## Docker build shows old files
Build context must include updated `nodes/nextcloud-ocs/`:
```bash
grep version nodes/nextcloud-ocs/package.json # on host before build
docker build --no-cache -t nextcloud-node-red:latest .
```
`docker builder prune -af` clears COPY layer cache.
---
## Nextcloud iframe blank
1. Browser DevTools → Console: CSP violations.
2. Confirm **nodered_embed** is enabled and admin URL matches iframe target host.
3. `CSPListener` adds `frame-src` for the hostname from `nodered_url`.
4. Reverse proxy must not set `X-Frame-Options: DENY` on Node-RED.
```bash
curl -I https://cloud.example.com | grep -i frame
curl -I http://192.168.1.26:1880 | grep -i frame
```
---
## Permission denied on entrypoint copy
Old files in volume owned by root. Fix:
```bash
docker rm -f nextcloud-node-red
docker volume rm node-red-data
# recreate container
```
Or fix ownership once:
```bash
docker exec -u root nextcloud-node-red chown -R node-red:node-red /data
```
---
## Verify node discovery manually
```bash
docker exec nextcloud-node-red node -e "
const p=require('/data/node_modules/node-red-contrib-nextcloud-ocs/package.json');
console.log(p.version, Object.keys(p['node-red'].nodes).length, 'nodes');
"
```
Expected: version from `package.json` and **16** node types.