#!/usr/bin/env bash # integrate-into-nodered.sh — lägg Collabora-noder i BEFINTLIG nextcloud-node-red (:1880) # Anropas av install.sh / bootstrap-install.sh (standardläge). set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PKG_SRC="${PKG_SRC:-$(cd "${SCRIPT_DIR}/.." && pwd)}" NODERED_DIR="${NODERED_DIR:-/home/ncdeploy/nextcloud-node-red}" NODERED_CONTAINER="${NODERED_CONTAINER:-nextcloud-node-red}" NODERED_PORT="${NODERED_PORT:-1880}" CODE_CONTAINER="${CODE_CONTAINER:-code}" COLLABORA_IP="${COLLABORA_IP:-172.17.0.2}" MARKER="nextcloud-collabora-nodered" log() { printf '[integrate] %s\n' "$*"; } warn() { printf '[integrate] VARNING: %s\n' "$*" >&2; } die() { printf '[integrate] 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 "sudo/root krävs för: $*" fi } runtime_user() { if [ -n "${INSTALL_USER:-}" ]; then echo "${INSTALL_USER}" elif id ncdeploy >/dev/null 2>&1; then echo "ncdeploy" else id -un fi } # Docker build context — .dockerignore + rättigheter (undviker permission denied) DOCKERIGNORE_MARKER="nextcloud-collabora-nodered" patch_dockerignore() { local di="${NODERED_DIR}/.dockerignore" local snippet="${PKG_SRC}/integrate/dockerignore.snippet" if [ ! -f "${di}" ]; then log "Skapar .dockerignore" touch "${di}" fi if ! grep -q "${DOCKERIGNORE_MARKER}" "${di}" 2>/dev/null; then log "Patchar .dockerignore (exkluderar oläsbara lokala artefakter)" { echo "" echo "# ${DOCKERIGNORE_MARKER}" if [ -f "${snippet}" ]; then cat "${snippet}" else echo "nodes/odds-scraper/odds-scraper" echo ".git" echo ".vscode" echo "**/*.deb" echo "winboat*" echo "**/node_modules" fi } >> "${di}" fi # Dynamiskt: alla filer under nodes/ som inte ens root kan läsa → ignorera local unreadable while IFS= read -r relpath; do [ -n "${relpath}" ] || continue if ! grep -qxF "${relpath}" "${di}" 2>/dev/null; then log " + .dockerignore: ${relpath} (oläsbar)" echo "${relpath}" >> "${di}" fi done < <(run_as_root find "${NODERED_DIR}/nodes" -type f ! -readable -printf '%P\n' 2>/dev/null || true) } fix_nodered_build_permissions() { local user group user="$(runtime_user)" group="$(id -gn "${user}" 2>/dev/null || echo "${user}")" if [ ! -d "${NODERED_DIR}/nodes" ]; then return 0 fi log "Fixar ägande och läsrättigheter för ${NODERED_DIR} (innehåll orört)" run_as_root chown -R "${user}:${group}" "${NODERED_DIR}" run_as_root find "${NODERED_DIR}/nodes" -type d -exec chmod u+rwx,go+rx {} + 2>/dev/null || true run_as_root find "${NODERED_DIR}/nodes" -type f -exec chmod u+rw,go+r {} + 2>/dev/null || true # Binärer som ska vara körbara run_as_root find "${NODERED_DIR}/nodes" -type f \( -name "*.sh" -o -name "odds-scraper" \) -exec chmod u+rx,go+rx {} + 2>/dev/null || true } # Simulera Docker build context — måste lyckas innan build verify_build_context() { local di="${NODERED_DIR}/.dockerignore" local errfile errfile="$(mktemp)" log "Verifierar Docker build context..." patch_dockerignore if ! run_as_root tar -cf /dev/null \ --exclude-from="${di}" \ -C "${NODERED_DIR}" . 2>"${errfile}"; then warn "Build context-fel:" sed 's/^/[integrate] /' "${errfile}" >&2 || true # Sista utväg: tvinga bort kända problemfil if [ -f "${NODERED_DIR}/nodes/odds-scraper/odds-scraper" ]; then echo "nodes/odds-scraper/odds-scraper" >> "${di}" log "Tvingade nodes/odds-scraper/odds-scraper in i .dockerignore" fi if ! run_as_root tar -cf /dev/null \ --exclude-from="${di}" \ -C "${NODERED_DIR}" . 2>"${errfile}"; then rm -f "${errfile}" die "Build context oläsbar efter .dockerignore — se ${di}" fi fi rm -f "${errfile}" log "Build context OK" } compose_cmd() { if docker compose version >/dev/null 2>&1; then echo "docker compose" else echo "docker-compose" fi } # Alltid root för compose build — samma användare som kan läsa hela trädet + .dockerignore compose_run() { local compose compose="$(compose_cmd)" cd "${NODERED_DIR}" run_as_root env DOCKER_BUILDKIT=1 ${compose} "$@" } resolve_paths() { if [ "$(id -u)" -eq 0 ] && [ -d "/home/ncdeploy/nextcloud-node-red" ]; then NODERED_DIR="${NODERED_DIR:-/home/ncdeploy/nextcloud-node-red}" PKG_SRC="${PKG_SRC:-/home/ncdeploy/nextcloud-collabora-nodered}" fi } stop_sidecar_if_any() { if docker ps -a --format '{{.Names}}' | grep -qx "nextcloud-collabora-nodered"; then warn "Stoppar felaktig sidecar-container nextcloud-collabora-nodered (skulle inte skapas)" docker stop nextcloud-collabora-nodered 2>/dev/null || true docker rm nextcloud-collabora-nodered 2>/dev/null || true fi } preflight() { [ -d "${PKG_SRC}" ] || die "Paketkälla saknas: ${PKG_SRC}" [ -f "${PKG_SRC}/package.json" ] || die "package.json saknas i ${PKG_SRC}" [ -d "${NODERED_DIR}" ] || die "nextcloud-node-red saknas: ${NODERED_DIR}" [ -f "${NODERED_DIR}/Dockerfile" ] || die "Dockerfile saknas i ${NODERED_DIR}" [ -f "${NODERED_DIR}/entrypoint.sh" ] || die "entrypoint.sh saknas i ${NODERED_DIR}" if ! docker ps --format '{{.Names}}' | grep -qx "${NODERED_CONTAINER}"; then die "Container '${NODERED_CONTAINER}' körs inte — starta befintlig nextcloud-node-red först" fi if ! docker ps --format '{{.Names}}' | grep -qx "${CODE_CONTAINER}"; then die "CODE-container '${CODE_CONTAINER}' körs inte" fi } copy_node_package() { local dest="${NODERED_DIR}/nodes/nextcloud-collabora" log "Kopierar noder → ${dest}" mkdir -p "${dest}" rsync -a --delete \ "${PKG_SRC}/package.json" \ "${PKG_SRC}/lib/" \ "${PKG_SRC}/nodes/" \ "${dest}/" } patch_dockerfile() { local df="${NODERED_DIR}/Dockerfile" if grep -q "${MARKER}" "${df}"; then log "Dockerfile redan patchad" return 0 fi log "Patchar Dockerfile" cat >> "${df}" <<'EOF' # nextcloud-collabora-nodered — Collabora/Office-noder (WebDAV + CODE REST) RUN mkdir -p /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora COPY nodes/nextcloud-collabora/package.json /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora/ COPY nodes/nextcloud-collabora/lib /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora/lib COPY nodes/nextcloud-collabora/nodes /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora/nodes RUN cd /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora && npm install --omit=dev EOF } patch_entrypoint() { local ep="${NODERED_DIR}/entrypoint.sh" if grep -q "node-red-contrib-nextcloud-collabora" "${ep}"; then log "entrypoint.sh redan patchad" return 0 fi log "Patchar entrypoint.sh" # Infoga före sista exec/npm start om möjligt, annars append if grep -q "node-red-contrib-nextcloud-ocs" "${ep}"; then sed -i '/node-red-contrib-nextcloud-ocs/a\ cp -a /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora /data/node_modules/' "${ep}" else cat >> "${ep}" <<'EOF' # nextcloud-collabora-nodered mkdir -p /data/node_modules cp -a /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora /data/node_modules/ EOF fi } connect_code_network() { log "Ansluter ${NODERED_CONTAINER} till Docker bridge (CODE)" if docker network inspect bridge --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null | grep -q "${NODERED_CONTAINER}"; then log "Redan ansluten till bridge" else docker network connect bridge "${NODERED_CONTAINER}" 2>/dev/null || warn "Kunde inte ansluta bridge — kontrollera manuellt" fi } rebuild_nodered() { log "Bygger om ${NODERED_CONTAINER} (samma instans, port ${NODERED_PORT})..." fix_nodered_build_permissions if [ "${FORCE_REBUILD:-0}" = "1" ]; then compose_run build --no-cache else compose_run build fi compose_run up -d } wait_nodered() { log "Väntar på Node-RED :${NODERED_PORT}..." for i in $(seq 1 30); do if curl -sf "http://127.0.0.1:${NODERED_PORT}" >/dev/null 2>&1; then log "Node-RED svarar på http://127.0.0.1:${NODERED_PORT}" return 0 fi sleep 2 done warn "Node-RED svarade inte inom 60 s" } verify_code_from_nodered() { local code_ip code_ip="$(docker inspect -f '{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "${CODE_CONTAINER}" 2>/dev/null | head -1)" [ -n "${code_ip}" ] || code_ip="${COLLABORA_IP}" local url="http://${code_ip}:9980/hosting/capabilities" log "Testar CODE från ${NODERED_CONTAINER}: ${url}" if docker exec "${NODERED_CONTAINER}" curl -sf "${url}" >/dev/null 2>&1; then log "OK: CODE nås från befintlig Node-RED-container" else warn "CODE nås inte — kör: docker network connect bridge ${NODERED_CONTAINER}" warn "nc-collab-config collaboraUrl=http://${code_ip}:9980" fi } main() { resolve_paths log "Integrerar Collabora-noder i befintlig nextcloud-node-red" log "Paket: ${PKG_SRC}" log "Node-RED: ${NODERED_DIR} (container ${NODERED_CONTAINER})" stop_sidecar_if_any preflight copy_node_package patch_dockerfile patch_entrypoint connect_code_network rebuild_nodered wait_nodered verify_code_from_nodered local host_ip host_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1")" cat <