From 7a9d421181e509b733f6dacdc058bee713a8cc8b Mon Sep 17 00:00:00 2001 From: Hannes Ahlin Date: Sat, 22 Aug 2026 15:02:38 +0200 Subject: [PATCH] Add bootstrap-install.sh for VM-agnostic git-based deployment Co-authored-by: Cursor --- .env.example | 8 +- .gitattributes | 1 + README.md | 90 ++++++---------- bootstrap-install.sh | 212 +++++++++++++++++++++++++++++++++++++ docker-compose.yml | 2 +- docs/INFRASTRUCTURE.md | 11 +- install.sh | 22 ++-- lib/bridge.js | 2 +- nodes/collabora-convert.js | 2 +- 9 files changed, 275 insertions(+), 75 deletions(-) create mode 100644 bootstrap-install.sh diff --git a/.env.example b/.env.example index 69bc29c..36ddf6f 100644 --- a/.env.example +++ b/.env.example @@ -1,9 +1,7 @@ -# Miljö för /home/ncadmin/nextcloud-collabora-nodered -# CODE-container "code" på Docker bridge 172.17.0.2 — INTE host.docker.internal på Linux - +# Genereras av bootstrap-install.sh eller manuellt +NC_HOST=cloud.hannesahlin.se +NC_HOST_IP=192.168.1.26 NEXTCLOUD_URL=https://cloud.hannesahlin.se COLLABORA_URL=http://172.17.0.2:9980 TZ=Europe/Stockholm NODERED_CREDENTIAL_SECRET=byt-den-här-hemligheten-vid-produktion - -# App-lösenord konfigureras i Node-RED UI (nc-collab-config), inte här. diff --git a/.gitattributes b/.gitattributes index b5584a5..d41585b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,4 +1,5 @@ *.sh text eol=lf entrypoint.sh text eol=lf install.sh text eol=lf +bootstrap-install.sh text eol=lf package.sh text eol=lf diff --git a/README.md b/README.md index 479213a..a0a6057 100644 --- a/README.md +++ b/README.md @@ -1,82 +1,60 @@ # nextcloud-collabora-nodered -Node-RED sidecar (port **1881**) för Nextcloud Office + Collabora CODE — WebDAV, richdocuments OCS och `/cool` REST. +Node-RED sidecar (port **1881**) med Collabora/Nextcloud Office-noder. **Version 1.0 — 22 augusti 2026** -## Miljö (cloud / 192.168.1.26) - -| Tjänst | Adress | -|--------|--------| -| Nextcloud | https://cloud.hannesahlin.se | -| CODE (intern) | http://172.17.0.2:9980 (container `code` på Docker `bridge`) | -| Sidecar Node-RED | http://192.168.1.26:1881 | -| Befintlig OCS Node-RED | http://192.168.1.26:1880 (orört) | -| Gitea | https://kode.hannesahlin.se | - -Detaljer: [docs/INFRASTRUCTURE.md](docs/INFRASTRUCTURE.md) - -## Snabbstart (Ubuntu) +## Snabbaste installation (valfri VM) + +```bash +curl -fsSL https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered/raw/branch/main/bootstrap-install.sh | bash +``` + +Skriptet: +- installerar git/curl/Docker om de saknas +- klonar från **Gitea** (`n3wk/nextcloud-collabora-nodered`) +- detekterar CODE-IP på Docker `bridge` +- bygger och startar Node-RED sidecar med alla noder +- lämnar `:1880`, odds-scraper och CODE orörda + +### Miljövariabler (valfritt) + +```bash +GIT_REPO_URL=https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered.git \ +INSTALL_DIR=/home/ncadmin/nextcloud-collabora-nodered \ +NC_HOST=cloud.hannesahlin.se \ +NC_HOST_IP=192.168.1.26 \ +bash bootstrap-install.sh +``` + +## Manuell installation (repo redan klonat) ```bash -git clone https://kode.hannesahlin.se//nextcloud-collabora-nodered.git /home/ncadmin/nextcloud-collabora-nodered cd /home/ncadmin/nextcloud-collabora-nodered -cp .env.example .env -chmod +x install.sh ./install.sh ``` -`install.sh` verifierar att CODE redan kör, ansluter sidecar till Docker `bridge`, och testar `/hosting/capabilities`. +## Repo -## Överföring från Windows - -```bash -./package.sh -scp artifacts/nextcloud-collabora-nodered-1.0.0.tar.gz ncadmin@192.168.1.26:/home/ncadmin/ -``` - -På servern: - -```bash -mkdir -p ~/nextcloud-collabora-nodered -tar -xzf ~/nextcloud-collabora-nodered-1.0.0.tar.gz -C ~/nextcloud-collabora-nodered -cd ~/nextcloud-collabora-nodered && ./install.sh -``` - -## Noder - -| Nod | Syfte | -|-----|-------| -| `nc-collab-config` | Miljö + app-lösenord | -| `collabora-bridge` | Agent-pipelines (börja här) | -| `nc-office` | richdocuments OCS | -| `collabora-convert` | `/cool/convert-to` (låg nivå) | -| `collabora-extract` | `/cool/extract-document-structure` | -| `collabora-transform` | `/cool/transform-document-structure` | -| `collabora-wopi` | discovery, capabilities, editor-URL | +- **Gitea:** https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered +- **SSH clone:** `ssh -p 2222 git@kode.hannesahlin.se:n3wk/nextcloud-collabora-nodered.git` ## Dokumentation -- [docs/AGENT-MANUAL.md](docs/AGENT-MANUAL.md) — assistentmanual -- [docs/INFRASTRUCTURE.md](docs/INFRASTRUCTURE.md) — Docker/nätverk på servern +- [docs/AGENT-MANUAL.md](docs/AGENT-MANUAL.md) +- [docs/INFRASTRUCTURE.md](docs/INFRASTRUCTURE.md) -## Gränser +## Noder -- Skriver till `/Dokument/OfficeOut/` som standard -- Port 9980 exponeras **inte** utanför loopback på host -- Sidecar når CODE via Docker **`bridge`** (`172.17.0.2`), inte `host.docker.internal` -- `nextcloud-node-red:1880` och odds-scraper **orörda** +`nc-collab-config` · `collabora-bridge` · `nc-office` · `collabora-convert` · `collabora-extract` · `collabora-transform` · `collabora-wopi` -## Git push (Gitea) +## Utveckling / push ```bash -git remote add origin https://kode.hannesahlin.se//nextcloud-collabora-nodered.git -# eller SSH: git@kode.hannesahlin.se:/nextcloud-collabora-nodered.git (port 2222) +git remote add origin https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered.git git push -u origin main ``` -Skapa tomt repo i Gitea UI först om det behövs. - ## Licens MIT diff --git a/bootstrap-install.sh b/bootstrap-install.sh new file mode 100644 index 0000000..abc863a --- /dev/null +++ b/bootstrap-install.sh @@ -0,0 +1,212 @@ +#!/usr/bin/env bash +# bootstrap-install.sh — full installation på valfri VM-variant +# +# Laddar ned från Gitea, installerar Docker vid behov, bygger Node-RED sidecar +# med alla Collabora-noder och startar på port 1881. +# +# One-liner (efter repo finns på Gitea): +# curl -fsSL https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered/raw/branch/main/bootstrap-install.sh | bash +# +# Med miljövariabler: +# GIT_REPO_URL=https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered.git \ +# INSTALL_DIR=/home/ncadmin/nextcloud-collabora-nodered \ +# bash bootstrap-install.sh +# +set -euo pipefail + +VERSION="1.0.0" +GIT_REPO_URL="${GIT_REPO_URL:-https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered.git}" +GIT_BRANCH="${GIT_BRANCH:-main}" +INSTALL_DIR="${INSTALL_DIR:-${HOME}/nextcloud-collabora-nodered}" +NC_HOST="${NC_HOST:-cloud.hannesahlin.se}" +NC_URL="${NC_URL:-https://${NC_HOST}}" +CODE_CONTAINER="${CODE_CONTAINER:-code}" +CODE_PORT="${CODE_PORT:-9980}" +NODERED_PORT="${NODERED_PORT:-1881}" +SKIP_APT="${SKIP_APT:-0}" +SKIP_DOCKER_INSTALL="${SKIP_DOCKER_INSTALL:-0}" + +log() { printf '[bootstrap] %s\n' "$*"; } +warn() { printf '[bootstrap] VARNING: %s\n' "$*" >&2; } +die() { printf '[bootstrap] FEL: %s\n' "$*" >&2; exit 1; } + +run_as_root() { + if [ "$(id -u)" -eq 0 ]; then + "$@" + elif command -v sudo >/dev/null 2>&1; then + sudo "$@" + else + die "Root/sudo krävs för: $*" + fi +} + +detect_host_ip() { + if [ -n "${NC_HOST_IP:-}" ]; then + echo "${NC_HOST_IP}" + return + fi + local ip + ip="$(hostname -I 2>/dev/null | awk '{print $1}' || true)" + if [ -n "${ip}" ]; then + echo "${ip}" + return + fi + ip="$(ip -4 route get "${NC_HOST}" 2>/dev/null | awk '{for(i=1;i<=NF;i++) if($i=="src") print $(i+1)}' || true)" + if [ -n "${ip}" ]; then + echo "${ip}" + return + fi + echo "192.168.1.26" +} + +detect_code_ip() { + if [ -n "${COLLABORA_IP:-}" ]; then + echo "${COLLABORA_IP}" + return + fi + if command -v docker >/dev/null 2>&1 && docker ps --format '{{.Names}}' | grep -qx "${CODE_CONTAINER}"; then + docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${CODE_CONTAINER}" 2>/dev/null | head -1 + return + fi + echo "172.17.0.2" +} + +install_apt_packages() { + [ "${SKIP_APT}" = "1" ] && return 0 + if ! command -v apt-get >/dev/null 2>&1; then + warn "apt-get saknas — installera git, curl och docker manuellt" + return 0 + fi + log "Installerar grundpaket (git, curl, ca-certificates)..." + run_as_root apt-get update -qq + run_as_root apt-get install -y -qq git curl ca-certificates rsync +} + +install_docker() { + if [ "${SKIP_DOCKER_INSTALL}" = "1" ]; then + return 0 + fi + if command -v docker >/dev/null 2>&1 && docker info >/dev/null 2>&1; then + log "Docker finns redan" + return 0 + fi + log "Installerar Docker..." + if command -v apt-get >/dev/null 2>&1; then + run_as_root apt-get install -y -qq docker.io docker-compose-plugin 2>/dev/null || true + fi + if ! command -v docker >/dev/null 2>&1 || ! docker info >/dev/null 2>&1; then + log "Kör get.docker.com..." + curl -fsSL https://get.docker.com | run_as_root sh + fi + local target_user="${SUDO_USER:-${USER}}" + if [ -n "${target_user}" ] && [ "${target_user}" != "root" ]; then + run_as_root usermod -aG docker "${target_user}" 2>/dev/null || true + warn "Användaren ${target_user} lades till i docker-gruppen — logga ut/in om 'permission denied'" + fi +} + +clone_or_update_repo() { + if [ -d "${INSTALL_DIR}/.git" ]; then + log "Uppdaterar befintlig klon i ${INSTALL_DIR}..." + git -C "${INSTALL_DIR}" fetch origin + git -C "${INSTALL_DIR}" checkout "${GIT_BRANCH}" + git -C "${INSTALL_DIR}" pull --ff-only origin "${GIT_BRANCH}" || warn "git pull misslyckades — fortsätter med lokalt innehåll" + return 0 + fi + if [ -d "${INSTALL_DIR}" ] && [ "$(ls -A "${INSTALL_DIR}" 2>/dev/null | wc -l)" -gt 0 ]; then + die "${INSTALL_DIR} finns men är inte en git-klon — flytta bort eller sätt INSTALL_DIR" + fi + log "Klonar ${GIT_REPO_URL} → ${INSTALL_DIR}" + mkdir -p "$(dirname "${INSTALL_DIR}")" + git clone --branch "${GIT_BRANCH}" --depth 1 "${GIT_REPO_URL}" "${INSTALL_DIR}" +} + +write_env_file() { + local host_ip code_ip collab_url + host_ip="$(detect_host_ip)" + code_ip="$(detect_code_ip)" + collab_url="http://${code_ip}:${CODE_PORT}" + + log "VM host-IP för ${NC_HOST}: ${host_ip}" + log "CODE URL: ${collab_url} (container ${CODE_CONTAINER})" + + cat > "${INSTALL_DIR}/.env" </dev/null || echo "byt-den-här-hemligheten-vid-produktion")} +EOF +} + +preflight_code() { + if ! command -v docker >/dev/null 2>&1; then + die "Docker saknas efter installation" + fi + if ! docker ps --format '{{.Names}}' | grep -qx "${CODE_CONTAINER}"; then + die "CODE-container '${CODE_CONTAINER}' körs inte på denna VM. Starta befintlig CODE — skapa inte ny via detta skript." + fi + local cap + cap="$(docker exec "${CODE_CONTAINER}" curl -sf "http://127.0.0.1:${CODE_PORT}/hosting/capabilities" 2>/dev/null | head -c 80 || true)" + if [ -z "${cap}" ]; then + warn "CODE svarar inte på capabilities inuti containern — kontrollera aliasgroup och post_allow" + else + log "CODE capabilities OK inuti container ${CODE_CONTAINER}" + fi +} + +main() { + log "nextcloud-collabora-nodered bootstrap v${VERSION}" + log "Repo: ${GIT_REPO_URL} (${GIT_BRANCH})" + log "Installationskatalog: ${INSTALL_DIR}" + + if [ "$(id -u)" -eq 0 ] && [ -z "${SUDO_USER:-}" ]; then + warn "Körs som root — rekommenderat: ncadmin eller annan docker-användare" + fi + + install_apt_packages + install_docker + clone_or_update_repo + write_env_file + preflight_code + + log "Kör lokal install.sh..." + chmod +x "${INSTALL_DIR}/install.sh" + INSTALL_DIR="${INSTALL_DIR}" \ + CODE_CONTAINER="${CODE_CONTAINER}" \ + COLLABORA_IP="$(detect_code_ip)" \ + bash "${INSTALL_DIR}/install.sh" + + local host_ip nodered_url + host_ip="$(detect_host_ip)" + nodered_url="http://${host_ip}:${NODERED_PORT}" + + cat </dev/null | head -1)" if [ -n "${code_ip}" ] && [ "${code_ip}" != "${COLLABORA_IP}" ]; then - log "OBS: CODE IP är ${code_ip} (inte ${COLLABORA_IP}). Uppdatera COLLABORA_URL i .env om nödvändigt." + log "OBS: CODE IP är ${code_ip} (inte ${COLLABORA_IP}). Uppdaterar COLLABORA_URL i .env." if grep -q '^COLLABORA_URL=' .env; then sed -i "s|^COLLABORA_URL=.*|COLLABORA_URL=http://${code_ip}:9980|" .env fi fi +# extra_hosts: uppdatera NC_HOST_IP om vi kan detektera +if [ -f .env ] && grep -q '^NC_HOST_IP=' .env; then + host_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || true)" + if [ -n "${host_ip}" ]; then + sed -i "s|^NC_HOST_IP=.*|NC_HOST_IP=${host_ip}|" .env + fi +fi + log "Bygger och startar sidecar (port 1881, nätverk bridge + collab_default)..." if [ "${FORCE_REBUILD:-0}" = "1" ]; then ${COMPOSE} build --no-cache @@ -84,26 +92,24 @@ done log "Testar CODE capabilities från sidecar..." collab_url="$(grep '^COLLABORA_URL=' .env | cut -d= -f2- || echo "http://${COLLABORA_IP}:9980")" +host_ip="$(grep '^NC_HOST_IP=' .env 2>/dev/null | cut -d= -f2- || hostname -I | awk '{print $1}')" if docker exec nextcloud-collabora-nodered curl -sf "${collab_url}/hosting/capabilities" >/dev/null 2>&1; then log "OK: CODE nås på ${collab_url}" else log "VARNING: Kunde inte nå ${collab_url}/hosting/capabilities från sidecar." - log " Kontrollera: docker network inspect bridge | grep -A2 code" + log " Kontrollera: docker network inspect bridge | grep -A2 ${CODE_CONTAINER}" log " Alternativ: docker network connect bridge nextcloud-collabora-nodered" fi cat </nextcloud-collabora-nodered.git - git push -u origin main - -Orört: nextcloud-node-red :1880 (odds_network), odds-scraper, Gitea-databaslösen. +Repo: https://kode.hannesahlin.se/n3wk/nextcloud-collabora-nodered +Orört: nextcloud-node-red :1880, odds-scraper, Gitea-databaslösen. EOF log "Klart." diff --git a/lib/bridge.js b/lib/bridge.js index 4b0e2a8..26d7df6 100644 --- a/lib/bridge.js +++ b/lib/bridge.js @@ -80,7 +80,7 @@ async function ncFileConvert(config, msg, format) { if (msg.compareNcPath) { const cmp = await webdavGet(config, msg.compareNcPath); extraFields.push({ - name: 'file', + name: 'compare', buffer: cmp.buffer, filename: cmp.filename }); diff --git a/nodes/collabora-convert.js b/nodes/collabora-convert.js index 7e73d8d..0de467f 100644 --- a/nodes/collabora-convert.js +++ b/nodes/collabora-convert.js @@ -30,7 +30,7 @@ module.exports = function (RED) { } if (msg.compareNcPath) { const cmp = await webdavGet(cfg, msg.compareNcPath); - extraFields.push({ name: 'file', buffer: cmp.buffer, filename: cmp.filename }); + extraFields.push({ name: 'compare', buffer: cmp.buffer, filename: cmp.filename }); } const result = await convertTo(cfg, {