865f66112a
Preserve lib/ and nodes/ tree instead of flattening, validate layout before Docker build, repair broken entrypoint patches, reconnect bridge after compose up, and detect CODE via bridge network HTTPS.
351 lines
12 KiB
Bash
Executable File
351 lines
12 KiB
Bash
Executable File
#!/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/ncadmin/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 ncadmin >/dev/null 2>&1; then
|
|
echo "ncadmin"
|
|
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 '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 "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/ncadmin/nextcloud-node-red" ]; then
|
|
NODERED_DIR="${NODERED_DIR:-/home/ncadmin/nextcloud-node-red}"
|
|
PKG_SRC="${PKG_SRC:-/home/ncadmin/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
|
|
}
|
|
|
|
assert_package_layout() {
|
|
local dest="$1"
|
|
local missing=0
|
|
|
|
if [ ! -f "${dest}/package.json" ]; then
|
|
warn "Saknas: ${dest}/package.json"
|
|
missing=1
|
|
fi
|
|
if [ ! -d "${dest}/lib" ] || [ -z "$(ls -A "${dest}/lib" 2>/dev/null)" ]; then
|
|
warn "Saknas eller tom: ${dest}/lib/"
|
|
missing=1
|
|
fi
|
|
if [ ! -d "${dest}/nodes" ] || [ -z "$(ls -A "${dest}/nodes" 2>/dev/null)" ]; then
|
|
warn "Saknas eller tom: ${dest}/nodes/"
|
|
missing=1
|
|
fi
|
|
if [ "${missing}" -ne 0 ]; then
|
|
die "Paketlayout fel efter kopiering — förväntat package.json + lib/ + nodes/ under ${dest}"
|
|
fi
|
|
}
|
|
|
|
copy_node_package() {
|
|
local dest="${NODERED_DIR}/nodes/nextcloud-collabora"
|
|
log "Kopierar paket → ${dest} (lib/ + nodes/, inte platt)"
|
|
|
|
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 "Paketlayout OK: ${dest}/{package.json,lib/,nodes/}"
|
|
}
|
|
|
|
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"
|
|
|
|
# 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 "Rensar trasig collabora-patch i 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 redan patchad (Collabora install_nodes)"
|
|
return 0
|
|
fi
|
|
|
|
log "Patchar entrypoint.sh (install_nodes för 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
|
|
}
|
|
|
|
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
|
|
verify_build_context
|
|
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"
|
|
}
|
|
|
|
code_bridge_ip() {
|
|
docker inspect -f '{{range $k,$v := .NetworkSettings.Networks}}{{if eq $k "bridge"}}{{$v.IPAddress}}{{end}}{{end}}' "${CODE_CONTAINER}" 2>/dev/null
|
|
}
|
|
|
|
verify_code_from_nodered() {
|
|
local code_ip
|
|
code_ip="$(code_bridge_ip)"
|
|
[ -n "${code_ip}" ] || code_ip="${COLLABORA_IP}"
|
|
# CODE lyssnar på HTTPS internt (HTTP ger tom svar)
|
|
local url="https://${code_ip}:9980/hosting/capabilities"
|
|
log "Testar CODE från ${NODERED_CONTAINER}: ${url}"
|
|
if docker exec "${NODERED_CONTAINER}" curl -sfk "${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=https://${code_ip}:9980 (verifyTls=false)"
|
|
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
|
|
rebuild_nodered
|
|
connect_code_network
|
|
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=https://172.17.0.2:9980 (verifyTls=false)
|
|
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 "$@"
|