2026-08-22 15:23:33 +02:00
|
|
|
#!/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; }
|
|
|
|
|
|
2026-08-22 15:28:06 +02:00
|
|
|
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 måste kunna läsa nodes/ (inkl. odds-scraper — rör inte innehållet)
|
|
|
|
|
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
|
|
|
|
|
if run_as_root test -r "${NODERED_DIR}/nodes/odds-scraper" 2>/dev/null; then
|
|
|
|
|
: # root kan läsa
|
|
|
|
|
else
|
|
|
|
|
warn "nodes/odds-scraper oläsbar — fixar läsrättigheter för build (innehåll orört)"
|
|
|
|
|
fi
|
|
|
|
|
log "Säkerställer läsbar Docker build context under ${NODERED_DIR}/nodes"
|
|
|
|
|
run_as_root find "${NODERED_DIR}/nodes" -type d -exec chmod a+rx {} + 2>/dev/null || true
|
|
|
|
|
run_as_root find "${NODERED_DIR}/nodes" -type f -exec chmod a+r {} + 2>/dev/null || true
|
|
|
|
|
# Alternativt: ge ncdeploy ägarskap om katalogen ägs av root
|
|
|
|
|
if [ "$(stat -c '%U' "${NODERED_DIR}" 2>/dev/null)" = "root" ]; then
|
|
|
|
|
run_as_root chown -R "${user}:${group}" "${NODERED_DIR}"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
compose_cmd() {
|
|
|
|
|
if docker compose version >/dev/null 2>&1; then
|
|
|
|
|
echo "docker compose"
|
|
|
|
|
else
|
|
|
|
|
echo "docker-compose"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# sudo docker compose: root läser hela build context (fixar permission denied på odds-scraper)
|
|
|
|
|
compose_run() {
|
|
|
|
|
local compose
|
|
|
|
|
compose="$(compose_cmd)"
|
|
|
|
|
cd "${NODERED_DIR}"
|
|
|
|
|
if [ "$(id -u)" -eq 0 ]; then
|
|
|
|
|
${compose} "$@"
|
|
|
|
|
else
|
|
|
|
|
run_as_root ${compose} "$@"
|
|
|
|
|
fi
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-22 15:23:33 +02:00
|
|
|
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})..."
|
2026-08-22 15:28:06 +02:00
|
|
|
fix_nodered_build_permissions
|
2026-08-22 15:23:33 +02:00
|
|
|
if [ "${FORCE_REBUILD:-0}" = "1" ]; then
|
2026-08-22 15:28:06 +02:00
|
|
|
compose_run build --no-cache
|
2026-08-22 15:23:33 +02:00
|
|
|
else
|
2026-08-22 15:28:06 +02:00
|
|
|
compose_run build
|
2026-08-22 15:23:33 +02:00
|
|
|
fi
|
2026-08-22 15:28:06 +02:00
|
|
|
compose_run up -d
|
2026-08-22 15:23:33 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 <<EOF
|
|
|
|
|
|
|
|
|
|
Integrering klar — samma Node-RED på port ${NODERED_PORT}
|
|
|
|
|
UI: http://${host_ip}:${NODERED_PORT}
|
|
|
|
|
Noder: nc-collab-config, collabora-bridge, nc-office, ...
|
|
|
|
|
|
|
|
|
|
Nästa steg:
|
|
|
|
|
1. nc-collab-config → app-lösenord, collaboraUrl=http://172.17.0.2:9980
|
|
|
|
|
2. Importera flows/example-flows.json från ${PKG_SRC} (valfritt)
|
|
|
|
|
3. Test: capabilities → ncFileToPdf
|
|
|
|
|
|
|
|
|
|
Ingen ny container skapad. odds-scraper orörd.
|
|
|
|
|
|
|
|
|
|
EOF
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
main "$@"
|