5ca7b4d4e0
- collaboraUrl -> https://code:9980 överallt (config-node, example-flows, .env.example, docker-compose.yml, install.sh, resolve_collabora_url); http://code:9980 failar när CODE kör SSL - verifyTls=false är nu ett krav (self-signed internt) i hjälptext/dok - integrate: connect_nodered_bridge efter container-up (docker network connect bridge, ignorerar already connected) + verifiering via docker exec curl -sk https://code:9980/hosting/capabilities (HTTP 200) - Publik URL förblir https://docs.domain.name i docs
563 lines
21 KiB
Bash
Executable File
563 lines
21 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# integrate-into-nodered.sh — add Collabora nodes to EXISTING nextcloud-node-red (:1880)
|
|
# Called by install.sh / bootstrap-install.sh (default mode).
|
|
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:-}"
|
|
NEXTCLOUD_URL="${NEXTCLOUD_URL:-https://cloud.domain.name}"
|
|
MARKER="nextcloud-collabora-nodered"
|
|
|
|
log() { printf '[integrate] %s\n' "$*"; }
|
|
warn() { printf '[integrate] WARNING: %s\n' "$*" >&2; }
|
|
die() { printf '[integrate] ERROR: %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 required for: $*"
|
|
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 "Creating .dockerignore"
|
|
touch "${di}"
|
|
fi
|
|
|
|
if ! grep -q "${DOCKERIGNORE_MARKER}" "${di}" 2>/dev/null; then
|
|
log "Patching .dockerignore (exclude unreadable local artifacts)"
|
|
{
|
|
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} (unreadable)"
|
|
echo "${relpath}" >> "${di}"
|
|
fi
|
|
done < <(run_as_root find "${NODERED_DIR}/nodes" -type f ! -readable -printf 'nodes/%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 "Fixing ownership and read permissions for ${NODERED_DIR} (content unchanged)"
|
|
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 "Verifying 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 error:"
|
|
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 "Forced nodes/odds-scraper/odds-scraper into .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 still unreadable after .dockerignore — see ${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} "$@"
|
|
}
|
|
|
|
code_bridge_ip() {
|
|
docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{if eq $k "bridge"}}{{$v.IPAddress}}{{end}}{{end}}' "${CODE_CONTAINER}" 2>/dev/null
|
|
}
|
|
|
|
# Host-LAN-IP för extra_hosts (detektera; fallback 192.168.1.10)
|
|
detect_host_lan_ip() {
|
|
local ip=""
|
|
if command -v hostname >/dev/null 2>&1; then
|
|
ip="$(hostname -I 2>/dev/null | tr ' ' '\n' \
|
|
| grep -E '^(192\.168|10)\.[0-9]+\.[0-9]+\.[0-9]+$' | head -n1 || true)"
|
|
fi
|
|
if [ -z "${ip}" ] && command -v ip >/dev/null 2>&1; then
|
|
ip="$(ip -4 addr show scope global 2>/dev/null | awk '/inet /{print $2}' | cut -d/ -f1 \
|
|
| grep -E '^(192\.168|10)\.' | head -n1 || true)"
|
|
fi
|
|
if [ -z "${ip}" ]; then
|
|
warn "Kunde inte detektera host-LAN-IP — fallback 192.168.1.10"
|
|
ip="192.168.1.10"
|
|
fi
|
|
HOST_LAN_IP="${ip}"
|
|
log "Host-LAN-IP: ${HOST_LAN_IP}"
|
|
}
|
|
|
|
# Skriv NC_HOST_IP i nextcloud-node-red/.env så compose-substitutionen plockar upp den
|
|
write_nodered_env_ip() {
|
|
local envfile="${NODERED_DIR}/.env"
|
|
local existing=""
|
|
if [ -f "${envfile}" ]; then
|
|
existing="$(grep -E '^NC_HOST_IP=[[:alnum:].:]+' "${envfile}" 2>/dev/null | tail -n1 | cut -d= -f2 || true)"
|
|
fi
|
|
if [ -n "${existing}" ]; then
|
|
log "${envfile} har redan NC_HOST_IP=${existing} — behåller"
|
|
HOST_LAN_IP="${existing}"
|
|
return 0
|
|
fi
|
|
if [ -f "${envfile}" ] && grep -q '^NC_HOST_IP=' "${envfile}" 2>/dev/null; then
|
|
run_as_root sed -i "s|^NC_HOST_IP=.*|NC_HOST_IP=${HOST_LAN_IP}|" "${envfile}"
|
|
else
|
|
run_as_root bash -c "echo 'NC_HOST_IP=${HOST_LAN_IP}' >> '${envfile}'"
|
|
fi
|
|
log "NC_HOST_IP=${HOST_LAN_IP} → ${envfile}"
|
|
}
|
|
|
|
detect_code_ip() {
|
|
local ip
|
|
ip="$(code_bridge_ip)"
|
|
if [ -n "${ip}" ]; then
|
|
COLLABORA_IP="${ip}"
|
|
log "CODE bridge-IP: ${COLLABORA_IP} (container ${CODE_CONTAINER})"
|
|
return 0
|
|
fi
|
|
if [ -n "${COLLABORA_IP}" ]; then
|
|
warn "Could not read bridge IP — using COLLABORA_IP=${COLLABORA_IP}"
|
|
return 0
|
|
fi
|
|
die "CODE bridge IP missing — set COLLABORA_IP or ensure ${CODE_CONTAINER} runs on Docker bridge"
|
|
}
|
|
|
|
detect_nodered_dir() {
|
|
if [ -d "${NODERED_DIR}" ] && [ -f "${NODERED_DIR}/Dockerfile" ]; then
|
|
return 0
|
|
fi
|
|
|
|
local from_compose
|
|
from_compose="$(docker inspect -f '{{index .Config.Labels "com.docker.compose.project.working_dir"}}' "${NODERED_CONTAINER}" 2>/dev/null || true)"
|
|
if [ -n "${from_compose}" ] && [ -f "${from_compose}/Dockerfile" ]; then
|
|
NODERED_DIR="${from_compose}"
|
|
log "Node-RED directory from compose label: ${NODERED_DIR}"
|
|
return 0
|
|
fi
|
|
|
|
for candidate in \
|
|
"/home/ncdeploy/nextcloud-node-red" \
|
|
"/opt/nextcloud-node-red"; do
|
|
if [ -f "${candidate}/Dockerfile" ]; then
|
|
NODERED_DIR="${candidate}"
|
|
log "Node-RED directory found: ${NODERED_DIR}"
|
|
return 0
|
|
fi
|
|
done
|
|
|
|
die "NODERED_DIR missing or invalid — set path to existing nextcloud-node-red (Dockerfile + entrypoint.sh)"
|
|
}
|
|
|
|
resolve_paths() {
|
|
if [ -f "${PKG_SRC}/.env" ]; then
|
|
set -a
|
|
# shellcheck disable=SC1091
|
|
. "${PKG_SRC}/.env"
|
|
set +a
|
|
fi
|
|
|
|
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
|
|
|
|
detect_nodered_dir
|
|
detect_code_ip
|
|
detect_host_lan_ip
|
|
write_nodered_env_ip
|
|
}
|
|
|
|
stop_sidecar_if_any() {
|
|
if docker ps -a --format '{{.Names}}' | grep -qx "nextcloud-collabora-nodered"; then
|
|
warn "Stopping stray sidecar container nextcloud-collabora-nodered (should not exist)"
|
|
docker stop nextcloud-collabora-nodered 2>/dev/null || true
|
|
docker rm nextcloud-collabora-nodered 2>/dev/null || true
|
|
fi
|
|
}
|
|
|
|
preflight() {
|
|
[ -d "${PKG_SRC}" ] || die "Package source missing: ${PKG_SRC}"
|
|
[ -f "${PKG_SRC}/package.json" ] || die "package.json missing in ${PKG_SRC}"
|
|
[ -d "${NODERED_DIR}" ] || die "nextcloud-node-red missing: ${NODERED_DIR}"
|
|
[ -f "${NODERED_DIR}/Dockerfile" ] || die "Dockerfile missing in ${NODERED_DIR}"
|
|
[ -f "${NODERED_DIR}/entrypoint.sh" ] || die "entrypoint.sh missing in ${NODERED_DIR}"
|
|
|
|
if ! docker ps --format '{{.Names}}' | grep -qx "${NODERED_CONTAINER}"; then
|
|
die "Container '${NODERED_CONTAINER}' is not running — start existing nextcloud-node-red first"
|
|
fi
|
|
if ! docker ps --format '{{.Names}}' | grep -qx "${CODE_CONTAINER}"; then
|
|
die "CODE container '${CODE_CONTAINER}' is not running"
|
|
fi
|
|
}
|
|
|
|
assert_package_layout() {
|
|
local dest="$1"
|
|
local missing=0
|
|
|
|
if [ ! -f "${dest}/package.json" ]; then
|
|
warn "Missing: ${dest}/package.json"
|
|
missing=1
|
|
fi
|
|
if [ ! -d "${dest}/lib" ] || [ -z "$(ls -A "${dest}/lib" 2>/dev/null)" ]; then
|
|
warn "Missing or empty: ${dest}/lib/"
|
|
missing=1
|
|
fi
|
|
if [ ! -d "${dest}/nodes" ] || [ -z "$(ls -A "${dest}/nodes" 2>/dev/null)" ]; then
|
|
warn "Missing or empty: ${dest}/nodes/"
|
|
missing=1
|
|
fi
|
|
if [ "${missing}" -ne 0 ]; then
|
|
die "Wrong package layout after copy — expected package.json + lib/ + nodes/ under ${dest}"
|
|
fi
|
|
}
|
|
|
|
copy_node_package() {
|
|
local dest="${NODERED_DIR}/nodes/nextcloud-collabora"
|
|
log "Copying package → ${dest} (lib/ + nodes/, not flat)"
|
|
|
|
rm -rf "${dest}"
|
|
mkdir -p "${dest}/lib" "${dest}/nodes"
|
|
|
|
cp -a "${PKG_SRC}/package.json" "${dest}/"
|
|
cp -a "${PKG_SRC}/lib/." "${dest}/lib/"
|
|
cp -a "${PKG_SRC}/nodes/." "${dest}/nodes/"
|
|
|
|
if [ -d "${PKG_SRC}/locales" ] && [ -n "$(ls -A "${PKG_SRC}/locales" 2>/dev/null)" ]; then
|
|
mkdir -p "${dest}/locales"
|
|
cp -a "${PKG_SRC}/locales/." "${dest}/locales/"
|
|
fi
|
|
|
|
assert_package_layout "${dest}"
|
|
log "Package layout OK: ${dest}/{package.json,lib/,nodes/}"
|
|
}
|
|
|
|
patch_dockerfile() {
|
|
local df="${NODERED_DIR}/Dockerfile"
|
|
if grep -q "${MARKER}" "${df}"; then
|
|
log "Dockerfile already patched"
|
|
return 0
|
|
fi
|
|
log "Patching 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"
|
|
|
|
# Rensa trasig tidigare patch (rå cp-rader utan install_nodes)
|
|
if grep -q 'cp -a /opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora' "${ep}"; then
|
|
log "Removing broken collabora patch from entrypoint.sh"
|
|
sed -i '/cp -a \/opt\/nextcloud-nodes\/node-red-contrib-nextcloud-collabora/d' "${ep}"
|
|
fi
|
|
|
|
if grep -q 'install_nodes.*Nextcloud Collabora' "${ep}"; then
|
|
log "entrypoint.sh already patched (Collabora install_nodes)"
|
|
return 0
|
|
fi
|
|
|
|
log "Patching entrypoint.sh (install_nodes for Collabora)"
|
|
if grep -q 'install_nodes.*Nextcloud OCS' "${ep}"; then
|
|
sed -i '/install_nodes.*Nextcloud OCS/a\
|
|
install_nodes "/opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora" "/data/node_modules/node-red-contrib-nextcloud-collabora" "Nextcloud Collabora"' "${ep}"
|
|
elif grep -q 'install_nodes.*Odds Scraper' "${ep}"; then
|
|
sed -i '/install_nodes.*Odds Scraper/a\
|
|
install_nodes "/opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora" "/data/node_modules/node-red-contrib-nextcloud-collabora" "Nextcloud Collabora"' "${ep}"
|
|
else
|
|
cat >> "${ep}" <<'EOF'
|
|
|
|
# nextcloud-collabora-nodered
|
|
install_nodes "/opt/nextcloud-nodes/node-red-contrib-nextcloud-collabora" "/data/node_modules/node-red-contrib-nextcloud-collabora" "Nextcloud Collabora"
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
# Krav: docker network connect <nodered-nät> code — ignorerar already connected
|
|
connect_code_network() {
|
|
local net
|
|
net="$(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{printf "%s\n" $k}}{{end}}' "${NODERED_CONTAINER}" 2>/dev/null \
|
|
| grep -v '^bridge$' | head -n1)"
|
|
[ -n "${net}" ] || die "No Node-RED compose network on ${NODERED_CONTAINER}"
|
|
|
|
if docker network inspect "${net}" --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null | grep -qw "${CODE_CONTAINER}"; then
|
|
log "Already connected: ${CODE_CONTAINER} on ${net}"
|
|
return 0
|
|
fi
|
|
|
|
log "Connecting ${CODE_CONTAINER} → ${net}"
|
|
if ! docker network connect "${net}" "${CODE_CONTAINER}" 2>/dev/null; then
|
|
if docker network inspect "${net}" --format '{{range .Containers}}{{.Name}} {{end}}' 2>/dev/null | grep -qw "${CODE_CONTAINER}"; then
|
|
log "Already connected (race): ${CODE_CONTAINER} on ${net}"
|
|
else
|
|
warn "Could not connect ${CODE_CONTAINER} → ${net} — COLLABORA_URL fallar tillbaka till CODE-IP"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
compose_service_name() {
|
|
local svc
|
|
svc="$(docker inspect -f '{{index .Config.Labels "com.docker.compose.service"}}' "${NODERED_CONTAINER}" 2>/dev/null || true)"
|
|
echo "${svc:-node-red}"
|
|
}
|
|
|
|
# Recreate utan 'docker compose down' — volymer (node-red-data) bevaras.
|
|
# ExtraHosts måste vara ifyllda efteråt.
|
|
recreate_nodered_service() {
|
|
local service extra
|
|
service="$(compose_service_name)"
|
|
|
|
log "Recreating ${service} (--no-deps --force-recreate, volymer bevaras)..."
|
|
if ! compose_run up -d --no-deps --force-recreate "${service}"; then
|
|
warn "force-recreate misslyckades — rm + up --no-deps (volymen node-red-data ligger kvar)"
|
|
run_as_root docker rm -f "${NODERED_CONTAINER}" 2>/dev/null || true
|
|
compose_run up -d --no-deps "${service}"
|
|
fi
|
|
|
|
for _ in $(seq 1 10); do
|
|
extra="$(docker inspect -f '{{len .HostConfig.ExtraHosts}}' "${NODERED_CONTAINER}" 2>/dev/null || echo 0)"
|
|
[ "${extra}" != "0" ] && break
|
|
sleep 1
|
|
done
|
|
if [ -z "${extra:-0}" ] || [ "${extra}" = "0" ]; then
|
|
die "ExtraHosts är [] på ${NODERED_CONTAINER} efter recreate — kontrollera extra_hosts i ${NODERED_DIR}/docker-compose.yml"
|
|
fi
|
|
log "ExtraHosts (${extra}): $(docker inspect -f '{{json .HostConfig.ExtraHosts}}' "${NODERED_CONTAINER}")"
|
|
}
|
|
|
|
# COLLABORA_URL: intern CODE kör HTTPS (ssl.enable=true, self-signed) —
|
|
# https://code:9980 primärt, fallback https://<CODE-IP>:9980.
|
|
# http://code:9980 failar när CODE har SSL påslaget.
|
|
# Aldrig 127.0.0.1/host.docker.internal — CODE kör inte på Node-RED-värden.
|
|
resolve_collabora_url() {
|
|
local url="${COLLABORA_URL:-https://code:9980}"
|
|
case "${url}" in
|
|
http://*|http://127.0.0.1:*|http://localhost:*|http://host.docker.internal:*|\
|
|
https://127.0.0.1:*|https://localhost:*|https://host.docker.internal:*|"")
|
|
warn "Otillåten COLLABORA_URL '${url}' — tvingar https://code:9980"
|
|
url="https://code:9980"
|
|
;;
|
|
esac
|
|
|
|
# Fallback till CODE-IP om 'code' inte kan resolvas från nod-containern
|
|
if ! docker exec "${NODERED_CONTAINER}" getent hosts code >/dev/null 2>&1; then
|
|
local ip
|
|
ip="$(docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{$v.IPAddress}} {{end}}' "${CODE_CONTAINER}" 2>/dev/null | awk '{print $1}')"
|
|
[ -n "${ip}" ] || ip="${COLLABORA_IP:-}"
|
|
if [ -n "${ip}" ]; then
|
|
warn "'code' inte resolvbart från ${NODERED_CONTAINER} — fallback https://${ip}:9980"
|
|
url="https://${ip}:9980"
|
|
else
|
|
die "Varken 'code'-DNS eller CODE-IP tillgänglig — kan inte sätta COLLABORA_URL"
|
|
fi
|
|
fi
|
|
|
|
COLLABORA_URL="${url}"
|
|
log "COLLABORA_URL=${COLLABORA_URL}"
|
|
}
|
|
|
|
# Node-RED-containern på default bridge — krävs för att nå https://code:9980.
|
|
# Körs EFTER container-up: recreate nollställer manuella network connect.
|
|
# Ignorerar "already connected".
|
|
connect_nodered_bridge() {
|
|
log "Connecting ${NODERED_CONTAINER} → bridge"
|
|
local out rc=0
|
|
out="$(docker network connect bridge "${NODERED_CONTAINER}" 2>&1)" || rc=$?
|
|
if [ "${rc}" -eq 0 ]; then
|
|
log "Connected: ${NODERED_CONTAINER} on bridge"
|
|
elif printf '%s' "${out}" | grep -qi 'already exists\|already connected'; then
|
|
log "Already connected: ${NODERED_CONTAINER} on bridge"
|
|
else
|
|
warn "docker network connect bridge ${NODERED_CONTAINER} misslyckades: ${out}"
|
|
fi
|
|
}
|
|
|
|
rebuild_nodered() {
|
|
log "Rebuilding ${NODERED_CONTAINER} (same instance, port ${NODERED_PORT})..."
|
|
fix_nodered_build_permissions
|
|
verify_build_context
|
|
if [ "${FORCE_REBUILD:-0}" = "1" ]; then
|
|
compose_run build --no-cache
|
|
else
|
|
compose_run build
|
|
fi
|
|
recreate_nodered_service
|
|
}
|
|
|
|
wait_nodered() {
|
|
log "Waiting for 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 responding at http://127.0.0.1:${NODERED_PORT}"
|
|
return 0
|
|
fi
|
|
sleep 2
|
|
done
|
|
warn "Node-RED did not respond within 60 s"
|
|
}
|
|
|
|
verify_code_from_nodered() {
|
|
# Krav 7: getent i containern != 127.0.0.1/127.0.1.1 + curl mot CODE och Nextcloud
|
|
local fail=0 h ip_out
|
|
for h in cloud.domain.name docs.domain.name host.docker.internal code; do
|
|
ip_out="$(docker exec "${NODERED_CONTAINER}" getent hosts "${h}" 2>/dev/null | awk '{print $1}' | head -n1 || true)"
|
|
if [ -z "${ip_out}" ]; then
|
|
warn "getent ${h}: INGET SVAR"; fail=1
|
|
elif [ "${ip_out#127.}" != "${ip_out}" ] || [ "${ip_out}" = "::1" ]; then
|
|
warn "getent ${h}: loopback (${ip_out}) — extra_hosts/translering trasig"; fail=1
|
|
else
|
|
log "getent ${h}: ${ip_out}"
|
|
fi
|
|
done
|
|
|
|
# Verifiera enligt serverns verklighet: CODE kör HTTPS internt,
|
|
# https://code:9980 ska ge HTTP 200 från Node-RED på bridge.
|
|
local code_status
|
|
code_status="$(docker exec "${NODERED_CONTAINER}" \
|
|
curl -sk -o /dev/null -w '%{http_code}' https://code:9980/hosting/capabilities 2>/dev/null || echo 000)"
|
|
if [ "${code_status}" = "200" ]; then
|
|
log "OK: CODE nås från Node-RED: https://code:9980/hosting/capabilities (HTTP 200)"
|
|
else
|
|
warn "CODE inte nåbar: https://code:9980/hosting/capabilities (HTTP ${code_status})"
|
|
warn "Kör: docker network connect bridge ${NODERED_CONTAINER} — http://code:9980 failar, CODE kräver HTTPS"
|
|
fail=1
|
|
fi
|
|
|
|
if docker exec "${NODERED_CONTAINER}" curl -sfk https://cloud.domain.name/status.php >/dev/null 2>&1; then
|
|
log "OK: Nextcloud nås från Node-RED: https://cloud.domain.name/status.php"
|
|
else
|
|
warn "Nextcloud inte nåbar från Node-RED via https://cloud.domain.name/status.php"
|
|
fail=1
|
|
fi
|
|
|
|
return "${fail}"
|
|
}
|
|
|
|
main() {
|
|
resolve_paths
|
|
log "Integrating Collabora nodes into existing nextcloud-node-red"
|
|
log "Package: ${PKG_SRC}"
|
|
log "Node-RED: ${NODERED_DIR} (container ${NODERED_CONTAINER})"
|
|
|
|
stop_sidecar_if_any
|
|
preflight
|
|
copy_node_package
|
|
patch_dockerfile
|
|
patch_entrypoint
|
|
rebuild_nodered
|
|
connect_nodered_bridge
|
|
connect_code_network
|
|
resolve_collabora_url
|
|
wait_nodered
|
|
if ! verify_code_from_nodered; then
|
|
warn "Verifiering hade fel — se varningarna ovan"
|
|
fi
|
|
|
|
local host_ip nc_url
|
|
host_ip="$(hostname -I 2>/dev/null | awk '{print $1}' || echo "127.0.0.1")"
|
|
nc_url="${NEXTCLOUD_URL:-https://cloud.domain.name}"
|
|
|
|
cat <<EOF
|
|
|
|
Integrating done — same Node-RED on port ${NODERED_PORT}
|
|
UI: http://${host_ip}:${NODERED_PORT}
|
|
Palette: "nextcloud collabora" (6 nodes + config)
|
|
|
|
Create config first (gear → nc-collab-config):
|
|
Nextcloud: ${nc_url}
|
|
Collabora: ${COLLABORA_URL} (verifyTls=false — self-signed internt)
|
|
Public WOPI: ${nc_url}
|
|
Auth: username + app password
|
|
|
|
Try: collabora-wopi (capabilities) → collabora-bridge (ncFileToPdf)
|
|
Example flow: flows/example-flows.json
|
|
|
|
EOF
|
|
}
|
|
|
|
main "$@"
|