Probleme mit der Installation in DOcker gelösst

This commit is contained in:
Sebastian Zell 2026-01-25 18:55:34 +01:00
commit 43ee2813a0
63 changed files with 12087 additions and 0 deletions

1
.abacus.donotdelete Normal file

File diff suppressed because one or more lines are too long

55
.dockerignore Normal file
View File

@ -0,0 +1,55 @@
# Git
.git
.gitignore
# Node.js
node_modules
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Build-Ausgabe (wird im Container neu gebaut)
dist
# Test-Dateien
test
*.test.ts
*.spec.ts
coverage
# IDE und Editor
.idea
.vscode
*.swp
*.swo
*~
# OS-spezifische Dateien
.DS_Store
Thumbs.db
# Dokumentation (nicht im Container benötigt)
*.md
!README.md
docs
# Beispiel-Workflows (werden als Volume gemountet)
workflows
# Umgebungsvariablen
.env
.env.*
# Archive
*.tar.gz
*.zip
# Logs
*.log
logs
# Temporäre Dateien
tmp
temp
.tmp
.cache

62
.env.docker Normal file
View File

@ -0,0 +1,62 @@
# Docker Umgebungsvariablen für n8n mit LibreBooking Node
# Kopiere diese Datei nach .env und passe die Werte an
# ============================================
# n8n Basis-Konfiguration
# ============================================
# Host und Port
N8N_HOST=localhost
N8N_PORT=5678
N8N_PROTOCOL=http
# Webhook URL (für externe Webhooks)
# Für Produktion: https://your-domain.com/
WEBHOOK_URL=http://localhost:5678/
# ============================================
# Authentifizierung (für Produktion aktivieren!)
# ============================================
N8N_BASIC_AUTH_ACTIVE=false
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=changeme_secure_password
# ============================================
# Zeitzone
# ============================================
TZ=Europe/Berlin
# ============================================
# Logging
# ============================================
# Mögliche Werte: silent, error, warn, info, debug
N8N_LOG_LEVEL=info
# ============================================
# PostgreSQL (optional, für Produktion empfohlen)
# Aktivieren mit: docker-compose --profile with-postgres up -d
# ============================================
POSTGRES_USER=n8n
POSTGRES_PASSWORD=n8n_secure_password
POSTGRES_DB=n8n
# Wenn PostgreSQL aktiv, diese Variable in docker-compose.yml hinzufügen:
# DB_TYPE=postgresdb
# DB_POSTGRESDB_HOST=postgres
# DB_POSTGRESDB_PORT=5432
# DB_POSTGRESDB_DATABASE=${POSTGRES_DB}
# DB_POSTGRESDB_USER=${POSTGRES_USER}
# DB_POSTGRESDB_PASSWORD=${POSTGRES_PASSWORD}
# ============================================
# LibreBooking Konfiguration (Optional)
# Diese können auch direkt in n8n als Credentials angelegt werden
# ============================================
# LIBREBOOKING_URL=https://booking.example.com
# LIBREBOOKING_USER=api_user
# LIBREBOOKING_PASSWORD=api_password

33
.env.example Normal file
View File

@ -0,0 +1,33 @@
# n8n LibreBooking Node - Umgebungsvariablen
#
# Kopiere diese Datei nach .env und passe die Werte an:
# cp .env.example .env
#
# n8n Authentifizierung
# WICHTIG: Ändere diese Werte für Produktion!
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=changeme
# Webhook-URL (für Produktion anpassen)
# Beispiel: https://n8n.deine-domain.de/
WEBHOOK_URL=http://localhost:5678/
# Zeitzone
TZ=Europe/Berlin
# Log-Level (debug, info, warn, error)
N8N_LOG_LEVEL=info
# Optional: Datenbank (Standard: SQLite)
# DB_TYPE=postgresdb
# DB_POSTGRESDB_HOST=localhost
# DB_POSTGRESDB_PORT=5432
# DB_POSTGRESDB_DATABASE=n8n
# DB_POSTGRESDB_USER=n8n
# DB_POSTGRESDB_PASSWORD=password
# Optional: Executions
# EXECUTIONS_DATA_SAVE_ON_ERROR=all
# EXECUTIONS_DATA_SAVE_ON_SUCCESS=all
# EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true

7
.gitignore vendored Normal file
View File

@ -0,0 +1,7 @@
node_modules/
dist/
.env
*.log
# Generated PDFs
*.pdf

70
.npmignore Normal file
View File

@ -0,0 +1,70 @@
# Source files (nur dist wird veröffentlicht)
*.ts
!*.d.ts
tsconfig.json
# Git
.git
.gitignore
.gitattributes
# Tests
test/
*.test.ts
*.spec.ts
coverage/
jest.config.js
# Docker
Dockerfile
docker-compose.yml
.dockerignore
# Entwicklung
.eslintrc.js
.eslintrc.json
.prettierrc
.prettierrc.json
.editorconfig
.vscode/
.idea/
# Dokumentation (README bleibt)
CONTRIBUTING.md
CHANGELOG.md
INSTALLATION.md
SCHNELLSTART.md
ARCHIV-INFO.md
docs/
# Beispiele
workflows/
examples/
# Skripte
install.sh
install.ps1
# OS-spezifisch
.DS_Store
Thumbs.db
# Archive
*.tar.gz
*.zip
# Logs und temp
*.log
logs/
tmp/
temp/
.tmp/
.cache/
# Umgebungsvariablen
.env
.env.*
!.env.example
# node_modules (sowieso ignoriert, aber sicherheitshalber)
node_modules/

16
.npmrc Normal file
View File

@ -0,0 +1,16 @@
# npm Configuration für LibreBooking n8n Node
# Diese Datei konfiguriert npm für dieses Projekt
# Audit-Warnungen deaktivieren
# Die Vulnerabilities kommen von n8n-workflow Dependencies und sind
# in diesem Kontext nicht kritisch (siehe SECURITY.md)
audit=false
# Fund-Nachrichten deaktivieren
fund=false
# Optional: Legacy Peer Dependencies (für ältere n8n Versionen)
# legacy-peer-deps=true
# Optional: Engine-Strict deaktivieren
# engine-strict=false

123
ARCHIV-INFO.md Normal file
View File

@ -0,0 +1,123 @@
# LibreBooking n8n Node - Archiv-Information
Dieses Archiv enthält den vollständigen LibreBooking n8n Node.
## Archiv entpacken
### Linux/macOS
```bash
# .tar.gz Archiv entpacken
tar -xzf n8n-nodes-librebooking.tar.gz
cd n8n-nodes-librebooking
```
### Windows
```powershell
# .zip Archiv entpacken
Expand-Archive -Path n8n-nodes-librebooking.zip -DestinationPath .
cd n8n-nodes-librebooking
```
Oder: Rechtsklick → "Alle extrahieren..."
## Installation
### Schnellste Methode (Linux/Mac)
```bash
chmod +x install.sh
./install.sh
n8n start
```
### Schnellste Methode (Windows)
```powershell
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\install.ps1
n8n start
```
### Mit Docker
```bash
docker-compose up -d
# Browser öffnen: http://localhost:5678
```
## Enthaltene Dateien
```
n8n-nodes-librebooking/
├── credentials/ # API-Credentials Definition
├── nodes/ # Node-Implementierungen
│ ├── LibreBooking/ # Haupt-Node
│ └── LibreBookingTrigger/ # Trigger-Node
├── custom-nodes/ # Für Docker-Integration (eigenständig)
│ ├── credentials/
│ ├── nodes/
│ ├── package.json
│ └── README.md
├── workflows/ # Beispiel-Workflows
├── test/ # Test-Scripts
├── Dockerfile # Docker Image Definition
├── Dockerfile.custom-nodes # Für Custom Nodes Integration
├── docker-compose.yml # Docker Compose Konfiguration
├── docker-compose.override.yml # Override für bestehende Installationen
├── docker-compose.example.yml # Vollständiges Beispiel
├── install.sh # Installations-Skript (Linux/Mac)
├── install.ps1 # Installations-Skript (Windows)
├── install-docker.sh # Docker-Integration Skript
├── nginx.conf # Reverse Proxy Beispiel
├── .env.docker # Docker Umgebungsvariablen
├── package.json # npm Paket-Definition
├── tsconfig.json # TypeScript Konfiguration
├── README.md # Hauptdokumentation
├── INSTALLATION.md # Detaillierte Installationsanleitung
├── DOCKER-INTEGRATION.md # Docker-Integration Anleitung
├── SCHNELLSTART.md # Kurzanleitung
├── SCHNELLSTART-DOCKER.md # Docker Kurzanleitung
├── CHANGELOG.md # Versionshistorie
├── CONTRIBUTING.md # Entwickler-Anleitung
└── LICENSE # MIT Lizenz
```
## Docker-Integration (NEU)
Für bestehende n8n Docker-Installationen:
```bash
# Automatisch
./install-docker.sh -p /pfad/zu/n8n
# Oder manuell
cp -r custom-nodes /pfad/zu/n8n/
cd /pfad/zu/n8n/custom-nodes && npm install && npm run build
docker-compose restart n8n
```
📖 Siehe **DOCKER-INTEGRATION.md** für ausführliche Anleitung.
## Dokumentation
- **README.md** - Übersicht und Schnellstart
- **INSTALLATION.md** - Detaillierte Installationsanleitung
- **DOCKER-INTEGRATION.md** - Anleitung für bestehende Docker-Installationen
- **SCHNELLSTART.md** - Ultra-Kurzanleitung für Experten
- **SCHNELLSTART-DOCKER.md** - Docker-Kurzanleitung
- **CONTRIBUTING.md** - Anleitung für Entwickler
## Support
Bei Fragen oder Problemen:
- GitHub Issues: https://github.com/your-org/n8n-nodes-librebooking/issues
## Lizenz
MIT License - siehe LICENSE Datei
---
*LibreBooking n8n Node v1.0.0*

35
CHANGELOG.md Normal file
View File

@ -0,0 +1,35 @@
# Changelog
Alle wichtigen Änderungen werden hier dokumentiert.
## [1.1.0] - 2026-01-25
### Geändert
- ⭐ **Vereinfachte Installation**: Fokus auf "auf dem Host bauen"
- Aktualisierte Dokumentation mit funktionierender Methode
- Neue npm scripts: `docker:deploy`, `docker:copy`, `docker:restart`
### Hinzugefügt
- `quick-install.sh` - Ultra-einfache Installation
- `update-node.sh` - Für Updates
- `git-commit.sh` - Git Commit Helper
- `git-cleanup.sh` - Cleanup alter Dateien
- `create-release.sh` - Release-Erstellung
- `GIT-COMMANDS.md` - Git-Befehlsreferenz
### Behoben
- TypeScript Installation Problem gelöst ("tsc not found")
- Read-only Volume Problem dokumentiert und gelöst
- npm audit Vulnerabilities dokumentiert
## [1.0.0] - 2026-01-24
### Hinzugefügt
- Vollständige LibreBooking API Integration
- 8 Ressourcen: Reservierung, Ressource, Zeitplan, Benutzer, Konto, Gruppe, Zubehör, Attribut
- Trigger Node für neue/geänderte Reservierungen
- Docker Support mit docker-compose.yml
- Automatische Installationsskripte
- Umfangreiche Dokumentation auf Deutsch
- Beispiel-Workflows
- Test-Skripte

238
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,238 @@
# Beitragen zum LibreBooking n8n Node
Vielen Dank für dein Interesse, zu diesem Projekt beizutragen! 🎉
## Inhaltsverzeichnis
- [Code of Conduct](#code-of-conduct)
- [Wie kann ich beitragen?](#wie-kann-ich-beitragen)
- [Entwicklungsumgebung einrichten](#entwicklungsumgebung-einrichten)
- [Code-Richtlinien](#code-richtlinien)
- [Pull Request Prozess](#pull-request-prozess)
- [Bug Reports](#bug-reports)
- [Feature Requests](#feature-requests)
## Code of Conduct
Dieses Projekt folgt einem [Code of Conduct](CODE_OF_CONDUCT.md). Mit deiner Teilnahme erklärst du dich einverstanden, diesen einzuhalten.
## Wie kann ich beitragen?
### Bugs melden
- Überprüfe zunächst, ob der Bug bereits gemeldet wurde
- Erstelle ein Issue mit einer klaren Beschreibung
- Füge Schritte zur Reproduktion hinzu
- Gib deine Umgebung an (OS, Node.js Version, n8n Version)
### Features vorschlagen
- Erstelle ein Issue mit dem Label "enhancement"
- Beschreibe den Use Case
- Erkläre, warum diese Funktion nützlich wäre
### Code beitragen
1. Forke das Repository
2. Erstelle einen Feature-Branch
3. Implementiere deine Änderungen
4. Schreibe Tests (falls möglich)
5. Erstelle einen Pull Request
## Entwicklungsumgebung einrichten
### Voraussetzungen
- Node.js 18.x oder höher
- npm 8.x oder höher
- n8n (global installiert)
- Git
### Setup
```bash
# Repository klonen
git clone https://github.com/DEIN-REPO/n8n-nodes-librebooking.git
cd n8n-nodes-librebooking
# Dependencies installieren
npm install
# Build ausführen
npm run build
# Für Entwicklung: Watch-Modus
npm run dev
```
### Lokales Testen
```bash
# Node mit n8n verlinken
npm link
# In n8n-Verzeichnis verlinken
cd $(npm root -g)/n8n
npm link n8n-nodes-librebooking
# n8n starten
n8n start
```
### Mit Docker testen
```bash
docker-compose up --build
```
## Code-Richtlinien
### TypeScript
- Verwende strenge Typisierung (`strict: true`)
- Vermeide `any` wo möglich
- Dokumentiere komplexe Funktionen mit JSDoc
### Formatierung
```bash
# Code formatieren
npm run format
# Linting prüfen
npm run lint
# Linting mit automatischer Korrektur
npm run lintfix
```
### Commit Messages
Wir folgen [Conventional Commits](https://www.conventionalcommits.org/):
```
feat: Neue Funktion hinzugefügt
fix: Bug behoben
docs: Dokumentation aktualisiert
style: Formatierung geändert (kein Code)
refactor: Code umstrukturiert
test: Tests hinzugefügt/geändert
chore: Build-Prozess/Tools geändert
```
Beispiele:
```
feat(reservation): Check-In Operation hinzugefügt
fix(auth): Session-Token wird jetzt korrekt erneuert
docs: Installationsanleitung aktualisiert
```
### Projektstruktur
```
n8n-nodes-librebooking/
├── credentials/ # Credential-Definitionen
│ └── LibreBookingApi.credentials.ts
├── nodes/ # Node-Definitionen
│ ├── LibreBooking/
│ │ ├── LibreBooking.node.ts
│ │ └── librebooking.svg
│ └── LibreBookingTrigger/
│ ├── LibreBookingTrigger.node.ts
│ └── librebooking.svg
├── test/ # Tests
├── workflows/ # Beispiel-Workflows
├── dist/ # Kompilierte Dateien (generiert)
└── package.json
```
## Pull Request Prozess
1. **Branch erstellen:**
```bash
git checkout -b feature/meine-funktion
```
2. **Änderungen implementieren:**
- Halte dich an die Code-Richtlinien
- Aktualisiere die Dokumentation
- Füge Tests hinzu (falls sinnvoll)
3. **Testen:**
```bash
npm run lint
npm run build
# Manuell in n8n testen
```
4. **Commit und Push:**
```bash
git add .
git commit -m "feat: Meine neue Funktion"
git push origin feature/meine-funktion
```
5. **Pull Request erstellen:**
- Beschreibe deine Änderungen
- Referenziere relevante Issues
- Warte auf Review
### PR Checkliste
- [ ] Code folgt den Richtlinien
- [ ] Linting/Formatting bestanden
- [ ] Build erfolgreich
- [ ] Dokumentation aktualisiert
- [ ] CHANGELOG.md aktualisiert
- [ ] Keine Secrets/Credentials im Code
## Bug Reports
### Template
```markdown
## Beschreibung
[Klare Beschreibung des Bugs]
## Schritte zur Reproduktion
1. ...
2. ...
3. ...
## Erwartetes Verhalten
[Was sollte passieren?]
## Tatsächliches Verhalten
[Was passiert stattdessen?]
## Umgebung
- OS: [z.B. Ubuntu 22.04]
- Node.js: [z.B. 20.10.0]
- n8n: [z.B. 1.20.0]
- LibreBooking: [z.B. 2.8.5]
## Logs/Screenshots
[Falls vorhanden]
```
## Feature Requests
### Template
```markdown
## Beschreibung
[Beschreibe die gewünschte Funktion]
## Use Case
[Warum wird diese Funktion benötigt?]
## Vorgeschlagene Lösung
[Falls du eine Idee hast]
## Alternativen
[Andere Möglichkeiten, die du in Betracht gezogen hast]
```
---
Vielen Dank für deinen Beitrag! 🙏

92
DOCKER-INTEGRATION.md Normal file
View File

@ -0,0 +1,92 @@
# Docker Integration
## Empfohlene Methode: Auf dem Host bauen
Die zuverlässigste Methode für Docker-Installationen.
### Schritt-für-Schritt
```bash
# 1. Auf dem Host
cd /pfad/zu/n8n-nodes-librebooking
npm install
npm run build
# 2. In Container kopieren
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
# 3. Neustarten
docker restart n8n
```
### Mit Skript
```bash
./quick-install.sh n8n
```
### Mit npm
```bash
npm run docker:deploy
```
---
## docker-compose.yml Beispiel
```yaml
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
container_name: n8n
restart: unless-stopped
ports:
- "5678:5678"
environment:
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true
- TZ=Europe/Berlin
volumes:
- n8n_data:/home/node/.n8n
# Optional: Custom Nodes Verzeichnis
# - ./custom-nodes:/home/node/.n8n/custom
volumes:
n8n_data:
```
---
## Für Updates
```bash
./update-node.sh n8n
# Oder
npm run docker:deploy
```
---
## Verifikation
```bash
# Dateien prüfen
docker exec n8n ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/
# Sollte zeigen:
# dist/
# package.json
# node_modules/
```
---
## Probleme?
Siehe [TROUBLESHOOTING.md](TROUBLESHOOTING.md)

41
Dockerfile Normal file
View File

@ -0,0 +1,41 @@
# Dockerfile für n8n mit LibreBooking Node
# Basiert auf dem offiziellen n8n Docker Image
FROM n8nio/n8n:latest
# Als Root-Benutzer für Installation
USER root
# Arbeitsverzeichnis für den Custom Node
WORKDIR /home/node/.n8n/custom
# Kopiere Node-Dateien
COPY package*.json ./
COPY tsconfig.json ./
COPY index.ts ./
COPY credentials/ ./credentials/
COPY nodes/ ./nodes/
# Installiere Dependencies und baue den Node
RUN npm install && \
npm run build && \
chown -R node:node /home/node/.n8n
# Zurück zum node-Benutzer
USER node
# Arbeitsverzeichnis auf n8n Standard setzen
WORKDIR /home/node
# n8n wird automatisch den Custom Node laden
ENV N8N_CUSTOM_EXTENSIONS="/home/node/.n8n/custom"
# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
CMD wget -q --spider http://localhost:5678/healthz || exit 1
# Standard n8n Port
EXPOSE 5678
# Startbefehl
CMD ["n8n", "start"]

48
Dockerfile.custom-nodes Normal file
View File

@ -0,0 +1,48 @@
# Dockerfile für Custom Nodes Integration
# Verwendet das offizielle n8n Image und fügt den LibreBooking Node hinzu
#
# Build: docker build -f Dockerfile.custom-nodes -t n8n-librebooking .
# Run: docker run -p 5678:5678 n8n-librebooking
ARG N8N_VERSION=latest
FROM n8nio/n8n:${N8N_VERSION}
# Wechsle zu root für Installationen
USER root
# Erstelle Custom Nodes Verzeichnis
RUN mkdir -p /home/node/.n8n/custom/n8n-nodes-librebooking && \
chown -R node:node /home/node/.n8n/custom
# Arbeitsverzeichnis setzen
WORKDIR /home/node/.n8n/custom/n8n-nodes-librebooking
# Kopiere Custom Node Dateien
COPY --chown=node:node custom-nodes/package.json .
COPY --chown=node:node custom-nodes/tsconfig.json .
COPY --chown=node:node custom-nodes/index.ts .
COPY --chown=node:node custom-nodes/credentials/ ./credentials/
COPY --chown=node:node custom-nodes/nodes/ ./nodes/
# Installiere Dependencies und baue den Node
RUN npm install && npm run build
# Wechsle zurück zum node User
USER node
# Arbeitsverzeichnis für n8n setzen
WORKDIR /home/node
# Umgebungsvariablen
ENV N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom \
N8N_COMMUNITY_NODES_ENABLED=true
# n8n Port
EXPOSE 5678
# Healthcheck
HEALTHCHECK --interval=30s --timeout=10s --start-period=30s --retries=3 \
CMD wget -qO- http://localhost:5678/healthz || exit 1
# Startbefehl
CMD ["n8n", "start"]

65
GIT-COMMANDS.md Normal file
View File

@ -0,0 +1,65 @@
# Git-Befehle für LibreBooking n8n Node
## Schnellbefehle
```bash
# Alle Änderungen committen
git add .
git commit -m "fix: Vereinfachte Installation"
# Push
git push origin main
```
## Release erstellen
```bash
# 1. Version in package.json anpassen (z.B. 1.1.0)
# 2. Alte Archive löschen
rm ../n8n-nodes-librebooking*.tar.gz
rm ../n8n-nodes-librebooking*.zip
# 3. Neue Archive erstellen
git archive --format=tar.gz --prefix=n8n-nodes-librebooking/ --output=../n8n-nodes-librebooking-v1.1.0.tar.gz HEAD
git archive --format=zip --prefix=n8n-nodes-librebooking/ --output=../n8n-nodes-librebooking-v1.1.0.zip HEAD
# 4. Tag erstellen
git tag -a v1.1.0 -m "Version 1.1.0 - Vereinfachte Installation"
# 5. Push
git push origin main
git push origin v1.1.0
```
## Mit Skripten
```bash
# Commit
./git-commit.sh "fix: Beschreibung der Änderung"
# Cleanup
./git-cleanup.sh
# Release
./create-release.sh
```
## Nützliche Befehle
```bash
# Status anzeigen
git status
# Änderungen anzeigen
git diff
# Log anzeigen
git log --oneline -10
# Tags anzeigen
git tag -l
# Remote anzeigen
git remote -v
```

101
INSTALLATION.md Normal file
View File

@ -0,0 +1,101 @@
# Installation
## Methode 1: Auf dem Host bauen (EMPFOHLEN) ⭐
Die zuverlässigste Methode für Docker-Installationen.
### Voraussetzungen
- Node.js 18+
- npm
- Docker
### Installation
```bash
# 1. Klonen
git clone https://github.com/your-org/n8n-nodes-librebooking.git
cd n8n-nodes-librebooking
# 2. Dependencies installieren
npm install
# 3. Bauen
npm run build
# 4. In Container kopieren
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
# 5. Container neustarten
docker restart n8n
```
### Mit Skript
```bash
./quick-install.sh n8n
```
### Mit npm scripts
```bash
npm install
npm run docker:deploy
```
---
## Methode 2: Docker mit vorgebautem dist/
Für Read-only Volumes.
```bash
# 1. Bauen
npm install
npm run build
# 2. Kopieren
cp -r dist package.json node_modules /pfad/zu/custom-nodes/n8n-nodes-librebooking/
# 3. docker-compose.yml
volumes:
- ./custom-nodes:/home/node/.n8n/custom:ro # Read-only möglich!
```
---
## Methode 3: Native Installation (ohne Docker)
```bash
# 1. Installieren
npm install
npm run build
npm link
# 2. n8n starten
n8n start
```
---
## Verifikation
Nach der Installation:
1. Öffne n8n: http://localhost:5678
2. Erstelle neuen Workflow
3. Suche nach "LibreBooking"
4. Wenn der Node erscheint → ✅ Installation erfolgreich
## Deinstallation
```bash
# Docker
docker exec n8n rm -rf /home/node/.n8n/custom/n8n-nodes-librebooking
docker restart n8n
# Native
npm unlink -g n8n-nodes-librebooking
```

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 LibreBooking n8n Node Contributors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,328 @@
# Manuelle Installation im Docker Container
Diese Anleitung beschreibt, wie Sie den LibreBooking Node manuell im Docker Container installieren, wenn die Dateien bereits kopiert wurden, aber der Node nicht in n8n erscheint.
---
## Situation
Sie haben die Dateien in den Container kopiert (z.B. nach `/opt/n8n/custom-nodes` oder `/home/node/.n8n/custom/n8n-nodes-librebooking`), aber:
- Der LibreBooking Node erscheint nicht in der n8n Node-Suche
- Die TypeScript-Dateien wurden nicht kompiliert
- Das `dist/` Verzeichnis fehlt oder ist leer
---
## Diagnose
### Schnell-Check mit einem Befehl
```bash
# Prüft Dateien und Status im Container
docker exec n8n sh -c "ls -la /home/node/.n8n/custom/*/dist/ 2>/dev/null || echo 'dist/ nicht gefunden'"
```
### Ausführlicher Check mit Skript
```bash
# Check-Skript in Container kopieren und ausführen
docker cp check-installation.sh n8n:/tmp/
docker exec n8n sh /tmp/check-installation.sh
```
### Manuell im Container prüfen
```bash
# In den Container einloggen
docker exec -it n8n sh
# Prüfen, was vorhanden ist
ls -la /home/node/.n8n/custom/
ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/
# Umgebungsvariablen prüfen
env | grep N8N
# Container verlassen
exit
```
---
## Lösung 1: Automatisch mit Skript (empfohlen)
Das einfachste ist das All-in-One-Skript:
```bash
# Auf dem Host ausführen
./fix-node-installation.sh
```
Oder mit spezifischem Container-Namen:
```bash
./fix-node-installation.sh -c mein-n8n-container
```
### Alternative: Nur das Installations-Skript
```bash
# Skript in Container kopieren
docker cp install-in-container.sh n8n:/tmp/
# Skript im Container ausführen
docker exec -it n8n sh /tmp/install-in-container.sh
# Container neustarten
docker restart n8n
```
---
## Lösung 2: Manuelle Schritte im Container
### Schritt 1: In den Container einloggen
```bash
docker exec -it n8n sh
```
### Schritt 2: Zum Custom-Node-Verzeichnis wechseln
```bash
# Standard-Pfad (offizielles n8n Image)
cd /home/node/.n8n/custom/n8n-nodes-librebooking
# Oder falls an anderem Ort:
cd /opt/n8n/custom-nodes
# oder
cd /data/custom-nodes
```
### Schritt 3: Prüfen ob Dateien vorhanden sind
```bash
ls -la
# Sollte zeigen: package.json, tsconfig.json, nodes/, credentials/
```
### Schritt 4: Dependencies installieren
```bash
npm install
```
Erwartete Ausgabe:
```
added 50 packages in 10s
```
### Schritt 5: Node kompilieren
```bash
npm run build
```
Erwartete Ausgabe:
```
> n8n-nodes-librebooking@1.0.0 build
> tsc && npm run copy:icons
```
### Schritt 6: Prüfen ob Build erfolgreich war
```bash
ls -la dist/
ls -la dist/nodes/LibreBooking/
```
Sollte zeigen:
- `dist/nodes/LibreBooking/LibreBooking.node.js`
- `dist/nodes/LibreBookingTrigger/LibreBookingTrigger.node.js`
- `dist/credentials/LibreBookingApi.credentials.js`
### Schritt 7: Container verlassen und neustarten
```bash
# Container verlassen
exit
# Container neustarten (auf dem Host)
docker restart n8n
```
---
## Lösung 3: Direkt mit docker exec (Ein-Befehl-Lösung)
Wenn Sie schnell zum Ziel kommen wollen:
```bash
# Alles in einem Befehl
docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"
# Container neustarten
docker restart n8n
```
Für anderen Pfad:
```bash
docker exec n8n sh -c "cd /opt/n8n/custom-nodes && npm install && npm run build"
docker restart n8n
```
---
## Verifizierung
### 1. Logs prüfen
```bash
# Nach LibreBooking in den Logs suchen
docker logs n8n 2>&1 | grep -i "librebooking\|custom\|node"
```
### 2. In n8n prüfen
1. Öffnen Sie n8n im Browser (z.B. `http://localhost:5678`)
2. Erstellen Sie einen neuen Workflow oder öffnen Sie einen bestehenden
3. Klicken Sie auf `+` um einen neuen Node hinzuzufügen
4. Suchen Sie nach "LibreBooking"
5. Es sollten zwei Nodes erscheinen:
- **LibreBooking** - Hauptnode für alle Operationen
- **LibreBooking Trigger** - Trigger für Reservierungen
### 3. Node-Dateien im Container prüfen
```bash
# Prüfe ob .node.js Dateien existieren
docker exec n8n find /home/node/.n8n/custom -name "*.node.js" 2>/dev/null
```
---
## Troubleshooting
### Problem: npm nicht gefunden
**Symptom:**
```
sh: npm: not found
```
**Lösung:**
Das verwendete Docker-Image enthält kein npm. Verwenden Sie ein Image mit Node.js:
```bash
# Prüfen Sie das Image
docker inspect n8n --format='{{.Config.Image}}'
# Das offizielle n8n Image (n8nio/n8n) enthält npm
# Falls Sie ein minimales Image verwenden, wechseln Sie zu n8nio/n8n
```
### Problem: Permission denied
**Symptom:**
```
EACCES: permission denied
npm ERR! could not create a lockfile
```
**Lösung:**
n8n läuft als User `node` (UID 1000). Setzen Sie die Berechtigungen:
```bash
# Auf dem Host (vor dem Kopieren)
sudo chown -R 1000:1000 custom-nodes/
# Oder im Container als root
docker exec -u root n8n chown -R node:node /home/node/.n8n/custom/
```
### Problem: Build schlägt fehl
**Symptom:**
```
error TS2307: Cannot find module 'n8n-workflow'
```
**Lösung:**
```bash
# Im Container
cd /home/node/.n8n/custom/n8n-nodes-librebooking
rm -rf node_modules package-lock.json
npm install
npm run build
```
### Problem: Node erscheint nach Neustart immer noch nicht
**Mögliche Ursachen:**
1. **Falscher Pfad**: N8N_CUSTOM_EXTENSIONS zeigt nicht auf das richtige Verzeichnis
```bash
docker exec n8n env | grep N8N_CUSTOM
# Sollte zeigen: N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
```
2. **package.json n8n-Sektion fehlt**: Die `n8n` Konfiguration in package.json muss korrekt sein
```bash
docker exec n8n cat /home/node/.n8n/custom/n8n-nodes-librebooking/package.json | grep -A10 '"n8n"'
```
3. **Container-Cache**: Container komplett neu erstellen
```bash
docker compose down
docker compose up -d
```
4. **n8n Version zu alt**: Der Node benötigt n8n >= 1.0.0
```bash
docker exec n8n n8n --version
```
### Problem: Fehler bei npm install (Netzwerk)
**Symptom:**
```
npm ERR! network request failed
```
**Lösung:**
```bash
# DNS prüfen
docker exec n8n cat /etc/resolv.conf
# npm Registry prüfen
docker exec n8n npm config get registry
# Ggf. Registry setzen
docker exec n8n npm config set registry https://registry.npmjs.org/
```
---
## Schnellreferenz
| Aktion | Befehl |
|--------|--------|
| Status prüfen | `docker exec n8n sh /tmp/check-installation.sh` |
| Installieren | `docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"` |
| Neustarten | `docker restart n8n` |
| Logs prüfen | `docker logs n8n 2>&1 \| grep -i libre` |
| Im Container | `docker exec -it n8n sh` |
---
## Weiterführende Dokumentation
- [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md) - Vollständige Docker-Anleitung
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Alle Troubleshooting-Themen
- [SCHNELLSTART-DOCKER.md](SCHNELLSTART-DOCKER.md) - Docker-Schnellstart
---
*Letzte Aktualisierung: Januar 2026*

102
README.md Normal file
View File

@ -0,0 +1,102 @@
# LibreBooking n8n Node
Integration von LibreBooking in n8n für automatisierte Reservierungs- und Ressourcenverwaltung.
## ⚡ Schnellstart (EMPFOHLEN)
**Die einfachste Methode: Auf dem Host bauen, in Docker kopieren**
```bash
# 1. Repository klonen
git clone https://github.com/your-org/n8n-nodes-librebooking.git
cd n8n-nodes-librebooking
# 2. Bauen und installieren
./quick-install.sh n8n
# Fertig! ✔
```
**Oder manuell:**
```bash
# Dependencies & Build
npm install
npm run build
# In Container kopieren
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
# Container neustarten
docker restart n8n
```
## 🛠️ npm Scripts
```bash
npm run build # Baut den Node
npm run docker:deploy # Baut, kopiert & startet Container neu
npm run docker:copy # Kopiert in Container
npm run docker:restart # Startet Container neu
```
## 📚 Dokumentation
- **[INSTALLATION.md](INSTALLATION.md)** - Alle Installationsmethoden
- **[SCHNELLSTART.md](SCHNELLSTART.md)** - Ultra-kurze Anleitung
- **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** - Problemlösung
- **[DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md)** - Docker-spezifische Anleitung
## 🔑 Credentials einrichten
1. Öffne n8n: http://localhost:5678
2. Gehe zu: **Einstellungen****Credentials** → **Add Credential**
3. Suche: **LibreBooking API**
4. Eingabe:
- **URL**: `https://deine-librebooking-url.de`
- **Benutzername**: Admin-Benutzer
- **Passwort**: Passwort
## 🌟 Features
### LibreBooking Node
- Reservierungen erstellen, bearbeiten, löschen
- Ressourcen und Verfügbarkeit verwalten
- Benutzer und Gruppen administrieren
- Zeitpläne und Zubehör konfigurieren
### LibreBooking Trigger Node
- Neue Reservierungen überwachen
- Geänderte Reservierungen erfassen
- Filter nach Ressource/Zeitplan/Benutzer
## 🔄 Updates
```bash
# Nach Änderungen oder git pull
./update-node.sh n8n
# Oder mit npm
npm run docker:deploy
```
## ❓ Problemlösung
### tsc not found?
**Lösung**: Auf dem Host bauen (siehe Schnellstart)
### Read-only Volume?
**Lösung**: dist/ in Container kopieren statt npm im Container
### npm audit Vulnerabilities?
→ Sind non-critical Dependencies von n8n-workflow. Siehe [SECURITY.md](SECURITY.md)
## 📄 Lizenz
MIT - Siehe [LICENSE](LICENSE)
## 🤝 Beitragen
Beiträge sind willkommen! Siehe [CONTRIBUTING.md](CONTRIBUTING.md)

147
SCHNELLSTART-DOCKER.md Normal file
View File

@ -0,0 +1,147 @@
# Docker Schnellstart - LibreBooking n8n Node
Schnelle Befehle für erfahrene Docker-Benutzer.
---
## Neue Installation
### Option A: Mit docker-compose (empfohlen)
```bash
# 1. Starten
cd /pfad/zu/librebooking_n8n_node
docker compose up -d
# 2. Browser öffnen
open http://localhost:5678
```
### Option B: In bestehende n8n Installation integrieren
```bash
# 1. Custom Nodes kopieren
cp -r custom-nodes /pfad/zu/n8n/
# 2. Bauen
cd /pfad/zu/n8n/custom-nodes
npm install && npm run build
# 3. docker-compose.override.yml erstellen
cat > docker-compose.override.yml << 'EOF'
version: '3.8'
services:
n8n:
volumes:
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
environment:
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true
EOF
# 4. Neustarten
docker compose restart n8n
```
---
## Node im Container bauen/reparieren
### Quick-Fix (Ein Befehl)
```bash
docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build" && docker restart n8n
```
### Mit Auto-Fix Skript
```bash
./fix-node-installation.sh
```
### Status prüfen
```bash
# Check-Skript ausführen
docker cp check-installation.sh n8n:/tmp/
docker exec n8n sh /tmp/check-installation.sh
```
---
## Häufige Befehle
| Aktion | Befehl |
|--------|--------|
| Container starten | `docker compose up -d` |
| Container stoppen | `docker compose down` |
| Container neustarten | `docker restart n8n` |
| Logs anzeigen | `docker logs -f n8n` |
| In Container einloggen | `docker exec -it n8n sh` |
| Node bauen | `docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"` |
| Status prüfen | `docker exec n8n sh /tmp/check-installation.sh` |
---
## Pfade im Container
| Beschreibung | Pfad |
|--------------|------|
| n8n Home | `/home/node/.n8n` |
| Custom Nodes | `/home/node/.n8n/custom` |
| LibreBooking Node | `/home/node/.n8n/custom/n8n-nodes-librebooking` |
| Daten-Verzeichnis | `/home/node/.n8n` |
---
## Umgebungsvariablen
```yaml
environment:
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true
- N8N_LOG_LEVEL=info
```
---
## Troubleshooting
### Node erscheint nicht
```bash
# 1. Build prüfen
docker exec n8n ls /home/node/.n8n/custom/n8n-nodes-librebooking/dist/
# 2. Falls leer - neu bauen
docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"
# 3. Neustarten
docker restart n8n
```
### Permission denied
```bash
sudo chown -R 1000:1000 custom-nodes/
```
### docker-compose: distutils Fehler (Python 3.12)
```bash
# Lösung: Docker Compose v2 verwenden
sudo apt-get install docker-compose-plugin
docker compose up -d # Beachte: ohne Bindestrich
```
---
## Detaillierte Anleitungen
- [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md) - Vollständige Docker-Dokumentation
- [MANUELLE-INSTALLATION-CONTAINER.md](MANUELLE-INSTALLATION-CONTAINER.md) - Manuelle Installation
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Alle Probleme und Lösungen
---
*Schnellstart-Guide für Docker-Profis*

40
SCHNELLSTART.md Normal file
View File

@ -0,0 +1,40 @@
# Schnellstart
## ⚡ Installation in 4 Zeilen
```bash
cd /pfad/zu/n8n-nodes-librebooking
npm install
npm run build
./quick-install.sh n8n
```
**Oder mit npm scripts:**
```bash
npm install
npm run docker:deploy
```
## 🔑 Credentials
n8n → Einstellungen → Credentials → Add → "LibreBooking API"
| Feld | Wert |
|------|------|
| URL | `https://deine-librebooking-url.de` |
| Benutzername | Admin-Benutzer |
| Passwort | Passwort |
## 🔄 Updates
```bash
git pull
./update-node.sh n8n
```
## ❓ Probleme?
- **tsc not found**: Auf dem Host bauen (npm install && npm run build)
- **Read-only Volume**: `docker cp` verwenden
- Mehr: [TROUBLESHOOTING.md](TROUBLESHOOTING.md)

234
SECURITY.md Normal file
View File

@ -0,0 +1,234 @@
# Sicherheitshinweise - LibreBooking n8n Node
Dieses Dokument erklärt die npm audit Vulnerabilities und wie man damit umgeht.
## Inhaltsverzeichnis
- [Übersicht der Vulnerabilities](#übersicht-der-vulnerabilities)
- [Warum diese Vulnerabilities existieren](#warum-diese-vulnerabilities-existieren)
- [Risikoeinschätzung](#risikoeinschätzung)
- [Empfehlungen](#empfehlungen)
- [Wie man sie beheben kann](#wie-man-sie-beheben-kann)
- [Produktionsumgebungen](#produktionsumgebungen)
---
## Übersicht der Vulnerabilities
Beim Ausführen von `npm audit` werden möglicherweise folgende Vulnerabilities angezeigt:
### Critical: form-data
```
form-data <4.0.1
Severity: critical
Prototype Pollution in form-data
https://github.com/advisories/GHSA-xxx
```
### Moderate: lodash
```
lodash <4.17.21
Severity: moderate
Prototype Pollution in lodash
https://github.com/advisories/GHSA-xxx
```
---
## Warum diese Vulnerabilities existieren
Diese Vulnerabilities kommen **nicht direkt aus diesem Projekt**, sondern sind **transitive Dependencies** von `n8n-workflow` und `n8n-core`.
### Dependency-Kette:
```
n8n-nodes-librebooking
└── n8n-workflow (devDependency für Typen)
└── axios
└── form-data (vulnerable version)
└── lodash (vulnerable version)
```
### Wichtig zu verstehen:
1. **n8n-workflow** ist nur als `devDependency` und `peerDependency` deklariert
2. In Produktion verwendet n8n seine **eigene** n8n-workflow Version
3. Die vulnerable Dependencies werden nur beim **Entwickeln** installiert
4. Diese Package werden **nicht** in das finale dist/ Verzeichnis gebündelt
---
## Risikoeinschätzung
### Für dieses Projekt: **NIEDRIGES RISIKO**
| Aspekt | Risiko | Begründung |
|--------|--------|------------|
| Entwicklung | Niedrig | form-data/lodash werden nicht direkt verwendet |
| Produktion | Sehr niedrig | Keine transtiven Dependencies werden deployed |
| n8n Runtime | Abhängig von n8n | n8n selbst muss die Vulnerabilities beheben |
### Warum niedriges Risiko:
1. **form-data Vulnerability:**
- Betrifft nur das Parsen von multipart/form-data
- Dieser Node verwendet keine File-Uploads über form-data
- Die LibreBooking API verwendet JSON für alle Requests
2. **lodash Vulnerability:**
- Betrifft `_.set()` und `_.setWith()` Funktionen
- Dieser Node verwendet keine direkten lodash Aufrufe
- Die Vulnerability erfordert Angreifer-kontrollierten Input
---
## Empfehlungen
### Für Entwickler:
1. **Warnungen ignorieren** (wenn nicht kritisch):
```bash
npm install --ignore-scripts
```
2. **Audit bei npm install deaktivieren:**
```bash
# Einmalig:
npm install --no-audit
# Permanent via .npmrc:
echo "audit=false" >> .npmrc
```
3. **Overrides verwenden** (in package.json):
```json
"overrides": {
"form-data": "^4.0.1",
"lodash": "^4.17.21"
}
```
### Für Produktionsumgebungen:
1. **n8n aktuell halten:** Die n8n-Entwickler aktualisieren regelmäßig ihre Dependencies
2. **Nur vertrauenswürdige Inputs:** Keine ungeprüften Daten an die Nodes übergeben
3. **Network Isolation:** n8n Container im isolierten Netzwerk betreiben
---
## Wie man sie beheben kann
### Option 1: Overrides in package.json (empfohlen)
Die package.json enthält bereits Overrides für bekannte Vulnerabilities:
```json
"overrides": {
"form-data": "^4.0.1",
"lodash": "^4.17.21"
}
```
### Option 2: npm audit fix (begrenzt)
```bash
# Automatische Fixes (nur kompatible Updates)
npm audit fix
# Force Fixes (VORSICHT: kann Breaking Changes einführen)
npm audit fix --force
```
**Hinweis:** `npm audit fix` kann transitive Dependencies nur begrenzt beheben.
### Option 3: Update-Skript verwenden
```bash
./update-dependencies.sh
```
Das Skript:
- Führt `npm audit fix` aus
- Aktualisiert alle Dependencies
- Testet ob alles funktioniert
- Gibt einen Report
### Option 4: Resolutions (für yarn/pnpm)
Wenn Sie yarn statt npm verwenden:
```json
"resolutions": {
"form-data": "^4.0.1",
"lodash": "^4.17.21"
}
```
---
## Produktionsumgebungen
### Best Practices:
1. **Docker Image aktuell halten:**
```bash
docker pull n8nio/n8n:latest
```
2. **Regelmäßige Updates:**
```bash
docker compose pull
docker compose up -d
```
3. **Security Scanning:**
```bash
# Image auf Vulnerabilities prüfen
docker scan n8nio/n8n:latest
# Oder mit Trivy:
trivy image n8nio/n8n:latest
```
4. **Netzwerk-Isolation:**
- n8n nicht direkt im Internet exponieren
- Reverse Proxy mit TLS verwenden
- Firewall-Regeln setzen
5. **Zugriffskontrollen:**
- Starke Passwörter verwenden
- Basic Auth oder OAuth aktivieren
- API-Keys für LibreBooking sicher speichern
### Sicherheits-Checkliste:
- [ ] n8n Version aktuell?
- [ ] Docker Image aktuell?
- [ ] TLS/HTTPS aktiviert?
- [ ] Starke Passwörter?
- [ ] Netzwerk isoliert?
- [ ] Regelmäßige Backups?
---
## Weiterführende Links
- [n8n Security Best Practices](https://docs.n8n.io/hosting/security/)
- [npm audit Documentation](https://docs.npmjs.com/cli/v8/commands/npm-audit)
- [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/)
- [Snyk Vulnerability Database](https://snyk.io/vuln/)
---
## Meldung von Sicherheitsproblemen
Wenn Sie eine Sicherheitslücke **direkt in diesem Projekt** (nicht in Dependencies) finden:
1. **Nicht öffentlich melden** (kein GitHub Issue)
2. Kontaktieren Sie uns direkt per E-Mail
3. Geben Sie Zeit für einen Fix bevor öffentliche Disclosure
---
*Letzte Aktualisierung: Januar 2026*

141
TROUBLESHOOTING.md Normal file
View File

@ -0,0 +1,141 @@
# Troubleshooting
## Inhaltsverzeichnis
1. [tsc not found](#tsc-not-found)
2. [Read-only Volume](#read-only-volume)
3. [npm audit Vulnerabilities](#npm-audit-vulnerabilities)
4. [Node nicht sichtbar](#node-nicht-sichtbar)
5. [Authentifizierung fehlgeschlagen](#authentifizierung-fehlgeschlagen)
---
## tsc not found
**Symptom:**
```
sh: 1: tsc: not found
npm error code 127
```
**Ursache:** TypeScript ist nicht installiert (im Container oft nicht verfügbar).
**Lösung:** Auf dem Host bauen!
```bash
# Auf dem Host (nicht im Container)
npm install
npm run build
# Dann in Container kopieren
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
docker restart n8n
```
**Oder mit Skript:**
```bash
./quick-install.sh n8n
```
---
## Read-only Volume
**Symptom:**
```
EROFS: read-only file system
npm error EACCES: permission denied
```
**Ursache:** Volume ist mit `:ro` gemountet.
**Lösung 1:** Volume ohne `:ro` mounten
```yaml
# docker-compose.yml
volumes:
- ./custom-nodes:/home/node/.n8n/custom # Ohne :ro
```
**Lösung 2:** Auf dem Host bauen und kopieren
```bash
npm install && npm run build
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
```
---
## npm audit Vulnerabilities
**Symptom:**
```
found 2 vulnerabilities (1 moderate, 1 critical)
```
**Erklärung:** Diese kommen von `n8n-workflow` und sind für dieses Projekt nicht kritisch.
**Lösung:** Ignorieren oder `.npmrc` verwenden:
```bash
# .npmrc bereits konfiguriert
audit=false
```
Siehe [SECURITY.md](SECURITY.md) für Details.
---
## Node nicht sichtbar
**Lösung:**
1. Container neustarten:
```bash
docker restart n8n
```
2. Dateien prüfen:
```bash
docker exec n8n ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/
```
3. Environment prüfen:
```yaml
environment:
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
```
4. Logs prüfen:
```bash
docker logs n8n | grep -i librebooking
```
---
## Authentifizierung fehlgeschlagen
**Symptom:** "401 Unauthorized" oder "Invalid credentials"
**Lösung:**
1. URL prüfen (ohne `/Web/` am Ende)
2. Benutzer muss Admin sein
3. API muss in LibreBooking aktiviert sein
Test:
```bash
curl -X POST https://librebooking.example.com/Web/Services/Authentication/Authenticate \
-H 'Content-Type: application/json' \
-d '{"username": "admin", "password": "pass"}'
```
---
## Weitere Hilfe
- [README.md](README.md) - Übersicht
- [INSTALLATION.md](INSTALLATION.md) - Installationsanleitung
- [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md) - Docker Details

183
build-on-host.sh Executable file
View File

@ -0,0 +1,183 @@
#!/bin/bash
# ============================================================================
# build-on-host.sh - Baut den Node auf dem Host (außerhalb des Containers)
#
# Verwendung: Wenn das Volume als read-only gemountet werden soll
#
# Dieses Skript:
# 1. Installiert Dependencies auf dem Host
# 2. Baut den Node auf dem Host
# 3. Kopiert nur die fertigen Dateien in den Container
# 4. Das Volume kann dann read-only gemountet werden
# ============================================================================
set -e
# Farben
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo -e "${BLUE}============================================${NC}"
echo -e "${BLUE} LibreBooking Node - Build auf Host${NC}"
echo -e "${BLUE}============================================${NC}"
echo ""
# Hilfe anzeigen
show_help() {
echo "Verwendung: $0 [OPTIONEN]"
echo ""
echo "Dieses Skript baut den Node auf dem Host, sodass er als"
echo "read-only Volume gemountet werden kann."
echo ""
echo "Optionen:"
echo " -o, --output DIR Ausgabeverzeichnis (Standard: ./dist-for-docker)"
echo " -c, --copy-to PATH Kopiert direkt zu einem Pfad (z.B. custom-nodes/)"
echo " -h, --help Diese Hilfe anzeigen"
echo ""
echo "Beispiele:"
echo " $0 # Baut in ./dist-for-docker"
echo " $0 -c ../n8n/custom-nodes/n8n-nodes-librebooking"
echo ""
echo "Danach in docker-compose.yml:"
echo " volumes:"
echo " - ./dist-for-docker:/home/node/.n8n/custom/n8n-nodes-librebooking:ro"
exit 0
}
OUTPUT_DIR="$SCRIPT_DIR/dist-for-docker"
COPY_TO=""
# Parameter parsen
while [[ $# -gt 0 ]]; do
case $1 in
-o|--output)
OUTPUT_DIR="$2"
shift 2
;;
-c|--copy-to)
COPY_TO="$2"
shift 2
;;
-h|--help)
show_help
;;
*)
echo -e "${RED}Unbekannte Option: $1${NC}"
show_help
;;
esac
done
log() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
exit 1
}
# Schritt 1: Prüfe Voraussetzungen
log "Prüfe Voraussetzungen..."
if ! command -v node &>/dev/null; then
error "Node.js ist nicht installiert!\n\n Installieren Sie Node.js 18+: https://nodejs.org/"
fi
NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
if [ "$NODE_VERSION" -lt 18 ]; then
error "Node.js Version $NODE_VERSION ist zu alt. Mindestens v18 erforderlich."
fi
if ! command -v npm &>/dev/null; then
error "npm ist nicht installiert!"
fi
log "Node.js: $(node -v)"
log "npm: $(npm -v)"
# Schritt 2: Dependencies installieren
log "Installiere Dependencies..."
cd "$SCRIPT_DIR"
if npm install 2>&1 | tail -5; then
log "Dependencies installiert ✓"
else
error "npm install fehlgeschlagen!"
fi
# Schritt 3: Bauen
log "Baue den Node..."
if npm run build 2>&1 | tail -10; then
log "Build erfolgreich ✓"
else
error "npm run build fehlgeschlagen!"
fi
# Schritt 4: Prüfe Build
if [ ! -d "$SCRIPT_DIR/dist" ]; then
error "dist/ Verzeichnis wurde nicht erstellt!"
fi
NODE_COUNT=$(find "$SCRIPT_DIR/dist" -name "*.node.js" | wc -l)
if [ "$NODE_COUNT" -eq 0 ]; then
error "Keine .node.js Dateien im dist/ Verzeichnis!"
fi
log "Gefunden: $NODE_COUNT Node-Datei(en)"
# Schritt 5: Erstelle Ausgabeverzeichnis
log "Erstelle Ausgabeverzeichnis: $OUTPUT_DIR"
mkdir -p "$OUTPUT_DIR"
# Kopiere alle notwendigen Dateien
cp -r "$SCRIPT_DIR/dist" "$OUTPUT_DIR/"
cp "$SCRIPT_DIR/package.json" "$OUTPUT_DIR/"
cp -r "$SCRIPT_DIR/node_modules" "$OUTPUT_DIR/" 2>/dev/null || true
# Optional: Nur essentielle Dateien für minimales Image
log "Räume auf..."
rm -rf "$OUTPUT_DIR/node_modules/.cache" 2>/dev/null || true
# Schritt 6: Optional kopieren
if [ -n "$COPY_TO" ]; then
log "Kopiere zu: $COPY_TO"
mkdir -p "$COPY_TO"
cp -r "$OUTPUT_DIR/"* "$COPY_TO/"
log "Kopiert ✓"
fi
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} Build abgeschlossen!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
log "Ausgabe in: $OUTPUT_DIR"
echo ""
echo "Nächste Schritte:"
echo ""
echo "1. Kopieren Sie das Verzeichnis zu Ihrer n8n Installation:"
echo " cp -r $OUTPUT_DIR /pfad/zu/n8n/custom-nodes/n8n-nodes-librebooking"
echo ""
echo "2. Oder verwenden Sie es direkt in docker-compose.yml:"
echo ""
echo " services:"
echo " n8n:"
echo " volumes:"
echo " # Read-only möglich, da bereits gebaut!"
echo " - $OUTPUT_DIR:/home/node/.n8n/custom/n8n-nodes-librebooking:ro"
echo ""
echo "3. Starten Sie n8n neu:"
echo " docker compose restart n8n"
echo ""

281
check-installation.sh Executable file
View File

@ -0,0 +1,281 @@
#!/bin/sh
# ============================================================================
# check-installation.sh - Debug-Skript für LibreBooking Node Installation
# Kann sowohl im Container als auch auf dem Host ausgeführt werden
# ============================================================================
echo "============================================"
echo " LibreBooking Node - Installation Check"
echo "============================================"
echo ""
echo "Ausgeführt: $(date)"
echo "Hostname: $(hostname)"
echo ""
# Status-Zähler
OK_COUNT=0
WARN_COUNT=0
ERROR_COUNT=0
ok() {
echo "[✓] $1"
OK_COUNT=$((OK_COUNT + 1))
}
warn() {
echo "[!] $1"
WARN_COUNT=$((WARN_COUNT + 1))
}
error() {
echo "[✗] $1"
ERROR_COUNT=$((ERROR_COUNT + 1))
}
info() {
echo "[i] $1"
}
echo "============================================"
echo " 1. UMGEBUNG"
echo "============================================"
# Node.js/npm prüfen
if command -v node >/dev/null 2>&1; then
ok "Node.js installiert: $(node --version)"
else
error "Node.js nicht gefunden"
fi
if command -v npm >/dev/null 2>&1; then
ok "npm installiert: $(npm --version)"
else
error "npm nicht gefunden"
fi
# Aktueller Benutzer
info "Aktueller Benutzer: $(whoami) (UID: $(id -u))"
echo ""
echo "============================================"
echo " 2. UMGEBUNGSVARIABLEN"
echo "============================================"
if [ -n "$N8N_CUSTOM_EXTENSIONS" ]; then
ok "N8N_CUSTOM_EXTENSIONS: $N8N_CUSTOM_EXTENSIONS"
else
warn "N8N_CUSTOM_EXTENSIONS nicht gesetzt"
info " Empfehlung: N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom"
fi
if [ "$N8N_COMMUNITY_NODES_ENABLED" = "true" ]; then
ok "N8N_COMMUNITY_NODES_ENABLED: true"
else
warn "N8N_COMMUNITY_NODES_ENABLED ist nicht 'true'"
info " Aktueller Wert: ${N8N_COMMUNITY_NODES_ENABLED:-<nicht gesetzt>}"
fi
# Weitere n8n Variablen
if [ -n "$N8N_LOG_LEVEL" ]; then
info "N8N_LOG_LEVEL: $N8N_LOG_LEVEL"
fi
echo ""
echo "============================================"
echo " 3. VERZEICHNISSE PRÜFEN"
echo "============================================"
# Mögliche Pfade
POSSIBLE_PATHS="
/home/node/.n8n/custom/n8n-nodes-librebooking
/home/node/.n8n/custom
/opt/n8n/custom-nodes
/data/custom-nodes
"
FOUND_PATH=""
for path in $POSSIBLE_PATHS; do
if [ -d "$path" ]; then
if [ -f "$path/package.json" ]; then
ok "Custom Node Verzeichnis: $path"
FOUND_PATH="$path"
break
elif [ -f "$path/n8n-nodes-librebooking/package.json" ]; then
ok "Custom Node Verzeichnis: $path/n8n-nodes-librebooking"
FOUND_PATH="$path/n8n-nodes-librebooking"
break
else
info "Verzeichnis existiert (ohne package.json): $path"
fi
fi
done
if [ -z "$FOUND_PATH" ]; then
error "Kein Custom Node Verzeichnis mit package.json gefunden!"
info " Geprüfte Pfade:"
for path in $POSSIBLE_PATHS; do
info " - $path"
done
fi
echo ""
echo "============================================"
echo " 4. NODE-DATEIEN PRÜFEN"
echo "============================================"
if [ -n "$FOUND_PATH" ]; then
cd "$FOUND_PATH" 2>/dev/null || true
# package.json
if [ -f "package.json" ]; then
ok "package.json vorhanden"
info " Name: $(grep '"name":' package.json | head -1 | cut -d'"' -f4)"
info " Version: $(grep '"version":' package.json | head -1 | cut -d'"' -f4)"
else
error "package.json fehlt"
fi
# node_modules
if [ -d "node_modules" ]; then
MODULE_COUNT=$(ls -1 node_modules 2>/dev/null | wc -l)
ok "node_modules vorhanden ($MODULE_COUNT Pakete)"
else
error "node_modules fehlt - führen Sie 'npm install' aus"
fi
# dist Verzeichnis
if [ -d "dist" ]; then
ok "dist/ Verzeichnis vorhanden"
# .node.js Dateien suchen
echo ""
info "Gefundene Node-Dateien:"
NODE_FILES=$(find dist -name "*.node.js" 2>/dev/null)
if [ -n "$NODE_FILES" ]; then
echo "$NODE_FILES" | while read -r f; do
if [ -f "$f" ]; then
ok " $f ($(ls -lh "$f" | awk '{print $5}'))"
fi
done
else
error " Keine .node.js Dateien gefunden!"
fi
# Credentials
echo ""
info "Gefundene Credential-Dateien:"
CRED_FILES=$(find dist -name "*.credentials.js" 2>/dev/null)
if [ -n "$CRED_FILES" ]; then
echo "$CRED_FILES" | while read -r f; do
if [ -f "$f" ]; then
ok " $f"
fi
done
else
error " Keine .credentials.js Dateien gefunden!"
fi
else
error "dist/ Verzeichnis fehlt - führen Sie 'npm run build' aus"
fi
# Icons
echo ""
info "Icon-Dateien:"
find . -name "*.svg" 2>/dev/null | while read -r f; do
info " $f"
done
fi
echo ""
echo "============================================"
echo " 5. BERECHTIGUNGEN"
echo "============================================"
# Funktion: Prüft ob ein Verzeichnis read-only ist
check_readonly() {
local dir="$1"
local test_file="$dir/.write_test_$$"
# Versuche eine Test-Datei zu erstellen
if touch "$test_file" 2>/dev/null; then
rm -f "$test_file" 2>/dev/null
return 0 # Schreibbar
else
return 1 # Read-only
fi
}
if [ -n "$FOUND_PATH" ]; then
# Prüfe Schreibrechte (inkl. read-only Volume Check)
if check_readonly "$FOUND_PATH"; then
ok "Schreibrechte auf $FOUND_PATH"
else
error "KEINE Schreibrechte auf $FOUND_PATH (Read-only Volume?)"
echo ""
info " PROBLEM: Das Verzeichnis ist möglicherweise als :ro gemountet."
info " LÖSUNG 1: Volume ohne :ro mounten:"
info " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
info " LÖSUNG 2: Auf dem Host bauen mit ./build-on-host.sh"
info " Siehe: TROUBLESHOOTING.md"
echo ""
fi
# Besitzer prüfen
OWNER=$(ls -ld "$FOUND_PATH" | awk '{print $3}')
GROUP=$(ls -ld "$FOUND_PATH" | awk '{print $4}')
info "Besitzer: $OWNER:$GROUP"
# n8n läuft als node (UID 1000)
if [ "$(id -u)" = "1000" ]; then
ok "Läuft als UID 1000 (Standard für n8n)"
else
warn "Läuft nicht als UID 1000 (aktuell: $(id -u))"
fi
fi
echo ""
echo "============================================"
echo " ZUSAMMENFASSUNG"
echo "============================================"
echo ""
echo "Ergebnisse:"
echo " ✓ OK: $OK_COUNT"
echo " ! Warnung: $WARN_COUNT"
echo " ✗ Fehler: $ERROR_COUNT"
echo ""
if [ "$ERROR_COUNT" -gt 0 ]; then
echo "============================================"
echo " EMPFOHLENE AKTIONEN"
echo "============================================"
echo ""
if [ ! -d "node_modules" ] 2>/dev/null; then
echo "1. Dependencies installieren:"
echo " cd $FOUND_PATH && npm install"
echo ""
fi
if [ ! -d "dist" ] 2>/dev/null; then
echo "2. Node bauen:"
echo " cd $FOUND_PATH && npm run build"
echo ""
fi
echo "3. Container neustarten:"
echo " docker restart n8n"
echo ""
exit 1
elif [ "$WARN_COUNT" -gt 0 ]; then
echo "Status: TEILWEISE OK (Warnungen beachten)"
exit 0
else
echo "Status: ALLES OK ✓"
echo ""
echo "Falls der Node trotzdem nicht erscheint:"
echo " 1. Starten Sie n8n neu: docker restart n8n"
echo " 2. Prüfen Sie die Logs: docker logs n8n | grep -i libre"
exit 0
fi

89
create-release.sh Executable file
View File

@ -0,0 +1,89 @@
#!/bin/bash
# ============================================================================
# create-release.sh - Erstellt neue Archive und Git Tag
# ============================================================================
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
RED='\033[0;31m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
# Version aus package.json lesen
VERSION=$(grep '"version"' "$SCRIPT_DIR/package.json" | head -1 | sed 's/.*"version": "\([^"]*\)".*/\1/')
echo -e "${GREEN}=== Release erstellen v$VERSION ===${NC}\n"
cd "$SCRIPT_DIR"
# 1. Prüfe, ob alles committet ist
if [ -n "$(git status --porcelain)" ]; then
echo -e "${YELLOW}Warnung: Es gibt uncommittete Änderungen!${NC}"
git status --short
echo ""
read -p "Trotzdem fortfahren? (j/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Jj]$ ]]; then
exit 1
fi
fi
# 2. Alte Archive löschen
echo "[1/4] Lösche alte Archive..."
./git-cleanup.sh 2>/dev/null || true
# 3. Build prüfen
echo ""
echo "[2/4] Prüfe Build..."
if [ ! -d "dist" ] || [ -z "$(find dist -name '*.node.js' 2>/dev/null)" ]; then
echo "Baue Node..."
npm install --silent
npm run build --silent
fi
echo " ✓ Build OK"
# 4. Archive erstellen
echo ""
echo "[3/4] Erstelle Archive..."
TAR_FILE="$PARENT_DIR/n8n-nodes-librebooking-v${VERSION}.tar.gz"
ZIP_FILE="$PARENT_DIR/n8n-nodes-librebooking-v${VERSION}.zip"
git archive --format=tar.gz --prefix=n8n-nodes-librebooking/ --output="$TAR_FILE" HEAD
echo "$TAR_FILE"
git archive --format=zip --prefix=n8n-nodes-librebooking/ --output="$ZIP_FILE" HEAD
echo "$ZIP_FILE"
# 5. Git Tag (optional)
echo ""
echo "[4/4] Git Tag..."
if git tag | grep -q "v$VERSION"; then
echo -e " ${YELLOW}Tag v$VERSION existiert bereits${NC}"
else
read -p "Git Tag v$VERSION erstellen? (j/N) " -n 1 -r
echo
if [[ $REPLY =~ ^[Jj]$ ]]; then
git tag -a "v$VERSION" -m "Version $VERSION - Vereinfachte Installation"
echo " ✓ Tag v$VERSION erstellt"
fi
fi
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} Release v$VERSION erstellt!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo "Archive:"
echo " $TAR_FILE"
echo " $ZIP_FILE"
echo ""
echo "Zum Pushen:"
echo " git push origin main"
echo " git push origin v$VERSION"
echo ""

View File

@ -0,0 +1,65 @@
import {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
/**
* LibreBooking API Credentials
*
* LibreBooking verwendet Session-basierte Authentifizierung.
* Der Node holt bei jeder Ausführung einen neuen Session-Token.
*/
export class LibreBookingApi implements ICredentialType {
name = 'libreBookingApi';
displayName = 'LibreBooking API';
documentationUrl = 'https://librebooking.org/docs/api';
properties: INodeProperties[] = [
{
displayName: 'LibreBooking URL',
name: 'url',
type: 'string',
default: '',
placeholder: 'https://booking.example.com',
required: true,
description: 'Die Basis-URL Ihrer LibreBooking-Installation (ohne /Web/Services)',
},
{
displayName: 'Benutzername',
name: 'username',
type: 'string',
default: '',
required: true,
description: 'Ihr LibreBooking-Benutzername oder E-Mail-Adresse',
},
{
displayName: 'Passwort',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
required: true,
description: 'Ihr LibreBooking-Passwort',
},
];
// Test-Request um die Credentials zu validieren
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.url}}',
url: '/Web/Services/index.php/Authentication/Authenticate',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
username: '={{$credentials.username}}',
password: '={{$credentials.password}}',
},
},
};
}

42
custom-nodes/README.md Normal file
View File

@ -0,0 +1,42 @@
# LibreBooking n8n Node - Custom Nodes Version
Diese Version ist speziell für die Integration in bestehende n8n Docker-Installationen optimiert.
## Schnellinstallation
### 1. Verzeichnis in n8n Custom Nodes kopieren
```bash
cp -r custom-nodes /pfad/zu/n8n/.n8n/custom/n8n-nodes-librebooking
```
### 2. Dependencies installieren und bauen
```bash
cd /pfad/zu/n8n/.n8n/custom/n8n-nodes-librebooking
npm install
npm run build
```
### 3. n8n neustarten
```bash
docker-compose restart n8n
```
## Enthaltene Dateien
- `credentials/` - API Credentials Definition
- `nodes/` - LibreBooking und LibreBookingTrigger Nodes
- `package.json` - Vereinfachte Package-Konfiguration
- `tsconfig.json` - TypeScript Konfiguration
## Weitere Informationen
Siehe die ausführliche Dokumentation:
- `DOCKER-INTEGRATION.md` - Detaillierte Docker-Anleitung
- `README.md` - Vollständige Dokumentation
## Lizenz
MIT License

View File

@ -0,0 +1,65 @@
import {
IAuthenticateGeneric,
ICredentialTestRequest,
ICredentialType,
INodeProperties,
} from 'n8n-workflow';
/**
* LibreBooking API Credentials
*
* LibreBooking verwendet Session-basierte Authentifizierung.
* Der Node holt bei jeder Ausführung einen neuen Session-Token.
*/
export class LibreBookingApi implements ICredentialType {
name = 'libreBookingApi';
displayName = 'LibreBooking API';
documentationUrl = 'https://librebooking.org/docs/api';
properties: INodeProperties[] = [
{
displayName: 'LibreBooking URL',
name: 'url',
type: 'string',
default: '',
placeholder: 'https://booking.example.com',
required: true,
description: 'Die Basis-URL Ihrer LibreBooking-Installation (ohne /Web/Services)',
},
{
displayName: 'Benutzername',
name: 'username',
type: 'string',
default: '',
required: true,
description: 'Ihr LibreBooking-Benutzername oder E-Mail-Adresse',
},
{
displayName: 'Passwort',
name: 'password',
type: 'string',
typeOptions: {
password: true,
},
default: '',
required: true,
description: 'Ihr LibreBooking-Passwort',
},
];
// Test-Request um die Credentials zu validieren
test: ICredentialTestRequest = {
request: {
baseURL: '={{$credentials.url}}',
url: '/Web/Services/index.php/Authentication/Authenticate',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
username: '={{$credentials.username}}',
password: '={{$credentials.password}}',
},
},
};
}

4
custom-nodes/index.ts Normal file
View File

@ -0,0 +1,4 @@
// LibreBooking n8n Node - Entry Point
export { LibreBooking } from './nodes/LibreBooking/LibreBooking.node';
export { LibreBookingTrigger } from './nodes/LibreBookingTrigger/LibreBookingTrigger.node';
export { LibreBookingApi } from './credentials/LibreBookingApi.credentials';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
<!-- Background -->
<rect width="60" height="60" rx="8" fill="#2C3E50"/>
<!-- Calendar base -->
<rect x="10" y="15" width="40" height="35" rx="3" fill="#ECF0F1" stroke="#3498DB" stroke-width="2"/>
<!-- Calendar header -->
<rect x="10" y="15" width="40" height="10" rx="3" fill="#3498DB"/>
<rect x="10" y="22" width="40" height="3" fill="#3498DB"/>
<!-- Calendar rings -->
<rect x="18" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<rect x="38" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<!-- Grid lines -->
<line x1="10" y1="33" x2="50" y2="33" stroke="#BDC3C7" stroke-width="1"/>
<line x1="10" y1="41" x2="50" y2="41" stroke="#BDC3C7" stroke-width="1"/>
<line x1="23" y1="25" x2="23" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<line x1="37" y1="25" x2="37" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<!-- Booking indicator -->
<rect x="25" y="35" width="10" height="4" rx="1" fill="#27AE60"/>
<!-- Check mark -->
<path d="M39 27 L42 30 L48 22" stroke="#FFFFFF" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,352 @@
import {
INodeType,
INodeTypeDescription,
IPollFunctions,
INodeExecutionData,
NodeApiError,
NodeOperationError,
} from 'n8n-workflow';
interface LibreBookingSession {
sessionToken: string;
userId: number;
sessionExpires: string;
}
interface ReservationData {
referenceNumber: string;
startDate: string;
endDate: string;
title: string;
resourceId: number;
userId: number;
[key: string]: any;
}
/**
* Authentifizierung bei LibreBooking
*/
async function authenticateTrigger(
pollFunctions: IPollFunctions,
baseUrl: string,
username: string,
password: string,
): Promise<LibreBookingSession> {
try {
const response = await pollFunctions.helpers.httpRequest({
method: 'POST',
url: `${baseUrl}/Web/Services/index.php/Authentication/Authenticate`,
headers: { 'Content-Type': 'application/json' },
body: { username, password },
json: true,
});
if (!response.isAuthenticated) {
throw new NodeOperationError(
pollFunctions.getNode(),
'Authentifizierung fehlgeschlagen',
);
}
return {
sessionToken: response.sessionToken,
userId: response.userId,
sessionExpires: response.sessionExpires,
};
} catch (error: any) {
throw new NodeApiError(pollFunctions.getNode(), error, {
message: 'Authentifizierung fehlgeschlagen',
});
}
}
/**
* Abmeldung
*/
async function signOutTrigger(
pollFunctions: IPollFunctions,
baseUrl: string,
session: LibreBookingSession,
): Promise<void> {
try {
await pollFunctions.helpers.httpRequest({
method: 'POST',
url: `${baseUrl}/Web/Services/index.php/Authentication/SignOut`,
headers: { 'Content-Type': 'application/json' },
body: {
userId: session.userId,
sessionToken: session.sessionToken,
},
json: true,
});
} catch (error) {
// Ignoriere SignOut-Fehler
}
}
/**
* Reservierungen abrufen
*/
async function getReservations(
pollFunctions: IPollFunctions,
baseUrl: string,
session: LibreBookingSession,
startDateTime: string,
endDateTime: string,
filters: any,
): Promise<ReservationData[]> {
const qs: any = {
startDateTime,
endDateTime,
};
if (filters.resourceId) qs.resourceId = filters.resourceId;
if (filters.scheduleId) qs.scheduleId = filters.scheduleId;
if (filters.userId) qs.userId = filters.userId;
const response = await pollFunctions.helpers.httpRequest({
method: 'GET',
url: `${baseUrl}/Web/Services/index.php/Reservations/`,
headers: {
'Content-Type': 'application/json',
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
},
qs,
json: true,
});
return response.reservations || [];
}
/**
* Detaillierte Reservierungsdaten abrufen
*/
async function getReservationDetails(
pollFunctions: IPollFunctions,
baseUrl: string,
session: LibreBookingSession,
referenceNumber: string,
): Promise<any> {
const response = await pollFunctions.helpers.httpRequest({
method: 'GET',
url: `${baseUrl}/Web/Services/index.php/Reservations/${referenceNumber}`,
headers: {
'Content-Type': 'application/json',
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
},
json: true,
});
return response;
}
/**
* Zeitfenster berechnen
*/
function getTimeWindow(timeWindow: string): { start: string; end: string } {
const now = new Date();
const start = now.toISOString();
let endDate = new Date(now);
switch (timeWindow) {
case '7days':
endDate.setDate(endDate.getDate() + 7);
break;
case '14days':
endDate.setDate(endDate.getDate() + 14);
break;
case '30days':
endDate.setDate(endDate.getDate() + 30);
break;
case '90days':
endDate.setDate(endDate.getDate() + 90);
break;
default:
endDate.setDate(endDate.getDate() + 14);
}
return {
start,
end: endDate.toISOString(),
};
}
/**
* Eindeutigen Schlüssel für Reservierung generieren
*/
function getReservationKey(reservation: ReservationData): string {
return `${reservation.referenceNumber}_${reservation.startDate}_${reservation.endDate}_${reservation.title || ''}`;
}
/**
* LibreBooking Trigger Node
*/
export class LibreBookingTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'LibreBooking Trigger',
name: 'libreBookingTrigger',
icon: 'file:librebooking.svg',
group: ['trigger'],
version: 1,
description: 'Wird bei neuen oder geänderten Reservierungen in LibreBooking ausgelöst',
subtitle: '={{$parameter["event"]}}',
defaults: {
name: 'LibreBooking Trigger',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'libreBookingApi',
required: true,
},
],
polling: true,
properties: [
{
displayName: 'Event',
name: 'event',
type: 'options',
options: [
{ name: 'Neue Reservierung', value: 'newReservation', description: 'Wird bei neuen Reservierungen ausgelöst' },
{ name: 'Geänderte Reservierung', value: 'updatedReservation', description: 'Wird bei geänderten Reservierungen ausgelöst' },
{ name: 'Alle Reservierungen', value: 'allReservations', description: 'Wird bei neuen und geänderten Reservierungen ausgelöst' },
],
default: 'newReservation',
},
{
displayName: 'Filter',
name: 'filters',
type: 'collection',
placeholder: 'Filter hinzufügen',
default: {},
options: [
{ displayName: 'Ressourcen-ID', name: 'resourceId', type: 'number', default: '' },
{ displayName: 'Zeitplan-ID', name: 'scheduleId', type: 'number', default: '' },
{ displayName: 'Benutzer-ID', name: 'userId', type: 'number', default: '' },
],
},
{
displayName: 'Zeitfenster',
name: 'timeWindow',
type: 'options',
options: [
{ name: 'Nächste 7 Tage', value: '7days' },
{ name: 'Nächste 14 Tage', value: '14days' },
{ name: 'Nächste 30 Tage', value: '30days' },
{ name: 'Nächste 90 Tage', value: '90days' },
],
default: '14days',
},
{
displayName: 'Optionen',
name: 'options',
type: 'collection',
placeholder: 'Option hinzufügen',
default: {},
options: [
{ displayName: 'Detaillierte Daten Abrufen', name: 'fetchDetails', type: 'boolean', default: false },
],
},
],
};
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
const credentials = await this.getCredentials('libreBookingApi');
const baseUrl = (credentials.url as string).replace(/\/$/, '');
const username = credentials.username as string;
const password = credentials.password as string;
const event = this.getNodeParameter('event') as string;
const filters = this.getNodeParameter('filters', {}) as any;
const timeWindow = this.getNodeParameter('timeWindow', '14days') as string;
const options = this.getNodeParameter('options', {}) as any;
const workflowStaticData = this.getWorkflowStaticData('node');
const previousReservations = (workflowStaticData.reservations as Record<string, string>) || {};
let session: LibreBookingSession;
try {
session = await authenticateTrigger(this, baseUrl, username, password);
} catch (error) {
throw error;
}
try {
const { start, end } = getTimeWindow(timeWindow);
const reservations = await getReservations(
this,
baseUrl,
session,
start,
end,
filters,
);
const returnData: INodeExecutionData[] = [];
const currentReservations: Record<string, string> = {};
for (const reservation of reservations) {
const refNumber = reservation.referenceNumber;
const reservationKey = getReservationKey(reservation);
currentReservations[refNumber] = reservationKey;
const isNew = !previousReservations[refNumber];
const isUpdated = previousReservations[refNumber] && previousReservations[refNumber] !== reservationKey;
let shouldTrigger = false;
let eventType = '';
if (event === 'newReservation' && isNew) {
shouldTrigger = true;
eventType = 'new';
} else if (event === 'updatedReservation' && isUpdated) {
shouldTrigger = true;
eventType = 'updated';
} else if (event === 'allReservations' && (isNew || isUpdated)) {
shouldTrigger = true;
eventType = isNew ? 'new' : 'updated';
}
if (shouldTrigger) {
let reservationData = reservation;
if (options.fetchDetails) {
try {
reservationData = await getReservationDetails(
this,
baseUrl,
session,
refNumber,
);
} catch (error) {
reservationData = reservation;
}
}
returnData.push({
json: {
...reservationData,
_eventType: eventType,
_triggeredAt: new Date().toISOString(),
},
});
}
}
workflowStaticData.reservations = currentReservations;
if (returnData.length === 0) {
return null;
}
return [returnData];
} finally {
await signOutTrigger(this, baseUrl, session);
}
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
<!-- Background -->
<rect width="60" height="60" rx="8" fill="#2C3E50"/>
<!-- Calendar base -->
<rect x="10" y="15" width="40" height="35" rx="3" fill="#ECF0F1" stroke="#3498DB" stroke-width="2"/>
<!-- Calendar header -->
<rect x="10" y="15" width="40" height="10" rx="3" fill="#3498DB"/>
<rect x="10" y="22" width="40" height="3" fill="#3498DB"/>
<!-- Calendar rings -->
<rect x="18" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<rect x="38" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<!-- Grid lines -->
<line x1="10" y1="33" x2="50" y2="33" stroke="#BDC3C7" stroke-width="1"/>
<line x1="10" y1="41" x2="50" y2="41" stroke="#BDC3C7" stroke-width="1"/>
<line x1="23" y1="25" x2="23" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<line x1="37" y1="25" x2="37" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<!-- Booking indicator -->
<rect x="25" y="35" width="10" height="4" rx="1" fill="#27AE60"/>
<!-- Check mark -->
<path d="M39 27 L42 30 L48 22" stroke="#FFFFFF" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

35
custom-nodes/package.json Normal file
View File

@ -0,0 +1,35 @@
{
"name": "n8n-nodes-librebooking",
"version": "1.0.0",
"description": "LibreBooking n8n Community Node - Custom Nodes Version für Docker Integration",
"keywords": [
"n8n-community-node-package",
"librebooking",
"booking",
"reservation"
],
"main": "dist/index.js",
"scripts": {
"build": "tsc && npm run copy-icons",
"copy-icons": "mkdir -p dist/nodes/LibreBooking dist/nodes/LibreBookingTrigger && cp nodes/LibreBooking/*.svg dist/nodes/LibreBooking/ && cp nodes/LibreBookingTrigger/*.svg dist/nodes/LibreBookingTrigger/",
"clean": "rm -rf dist",
"rebuild": "npm run clean && npm run build"
},
"n8n": {
"n8nNodesApiVersion": 1,
"credentials": [
"dist/credentials/LibreBookingApi.credentials.js"
],
"nodes": [
"dist/nodes/LibreBooking/LibreBooking.node.js",
"dist/nodes/LibreBookingTrigger/LibreBookingTrigger.node.js"
]
},
"devDependencies": {
"typescript": "^5.3.0"
},
"peerDependencies": {
"n8n-workflow": ">=1.0.0"
},
"license": "MIT"
}

View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "ES2019",
"module": "commonjs",
"lib": ["ES2019", "ES2020.Promise"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": ".",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": false,
"noUnusedParameters": false
},
"include": [
"nodes/**/*.ts",
"credentials/**/*.ts"
],
"exclude": [
"node_modules",
"dist",
"test"
]
}

131
docker-compose.example.yml Normal file
View File

@ -0,0 +1,131 @@
# Vollständige Docker Compose Beispiel-Konfiguration
# n8n mit LibreBooking Node - Ready to Use
#
# Verwendung:
# 1. cp docker-compose.example.yml docker-compose.yml
# 2. cp .env.docker .env
# 3. .env Datei anpassen
# 4. docker-compose up -d
version: '3.8'
services:
n8n:
# Verwende das vorgefertigte Image mit LibreBooking Node
build:
context: .
dockerfile: Dockerfile.custom-nodes
args:
N8N_VERSION: latest
# Oder nutze das offizielle Image mit Volume-Mount:
# image: n8nio/n8n:latest
container_name: n8n-librebooking
restart: unless-stopped
ports:
- "${N8N_PORT:-5678}:5678"
environment:
# Basis-Konfiguration
- N8N_HOST=${N8N_HOST:-localhost}
- N8N_PORT=5678
- N8N_PROTOCOL=${N8N_PROTOCOL:-http}
- WEBHOOK_URL=${WEBHOOK_URL:-http://localhost:5678/}
# Authentifizierung (aktivieren für Produktion!)
- N8N_BASIC_AUTH_ACTIVE=${N8N_BASIC_AUTH_ACTIVE:-false}
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER:-admin}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD:-changeme}
# Custom Nodes
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true
# Zeitzone
- GENERIC_TIMEZONE=${TZ:-Europe/Berlin}
- TZ=${TZ:-Europe/Berlin}
# Logging
- N8N_LOG_LEVEL=${N8N_LOG_LEVEL:-info}
# Execution Settings
- EXECUTIONS_DATA_SAVE_ON_ERROR=all
- EXECUTIONS_DATA_SAVE_ON_SUCCESS=all
- EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true
volumes:
# Persistente n8n Daten
- n8n_data:/home/node/.n8n
# Custom Nodes (wenn nicht im Image gebaut)
# - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
healthcheck:
test: ["CMD", "wget", "-qO-", "http://localhost:5678/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
networks:
- n8n_network
# Optional: PostgreSQL Datenbank für n8n
# Für Produktion empfohlen statt SQLite
postgres:
image: postgres:15-alpine
container_name: n8n-postgres
restart: unless-stopped
profiles:
- with-postgres
environment:
- POSTGRES_USER=${POSTGRES_USER:-n8n}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD:-n8n_password}
- POSTGRES_DB=${POSTGRES_DB:-n8n}
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U ${POSTGRES_USER:-n8n}"]
interval: 10s
timeout: 5s
retries: 5
networks:
- n8n_network
# Optional: Redis für Queue Mode
redis:
image: redis:7-alpine
container_name: n8n-redis
restart: unless-stopped
profiles:
- with-redis
volumes:
- redis_data:/data
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 5s
retries: 5
networks:
- n8n_network
volumes:
n8n_data:
driver: local
postgres_data:
driver: local
redis_data:
driver: local
networks:
n8n_network:
driver: bridge

View File

@ -0,0 +1,21 @@
# Docker Compose Override für LibreBooking n8n Node
#
# WICHTIG: custom-nodes Volume NICHT als read-only (:ro) mounten!
# Der Node benötigt Schreibrechte für npm install/build.
#
# Für read-only Volumes siehe: docker-compose.readonly.yml
# und führen Sie vorher build-on-host.sh aus.
version: '3.8'
services:
n8n:
volumes:
# LibreBooking Custom Node
# OHNE :ro - Der Node muss beim Start gebaut werden können!
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking
environment:
# Custom Nodes aktivieren
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true

View File

@ -0,0 +1,25 @@
# Docker Compose Override für READ-ONLY Volume Mount
#
# VORAUSSETZUNG: Node wurde auf dem Host gebaut!
# Führen Sie zuerst aus: ./build-on-host.sh
#
# Verwendung:
# docker compose -f docker-compose.yml -f docker-compose.readonly.yml up -d
#
# Oder setzen Sie in Ihrer Umgebung:
# export COMPOSE_FILE=docker-compose.yml:docker-compose.readonly.yml
version: '3.8'
services:
n8n:
volumes:
# LibreBooking Custom Node - READ-ONLY
# Das dist-for-docker Verzeichnis wurde mit build-on-host.sh erstellt
# und enthält bereits alle kompilierten Dateien.
- ./dist-for-docker:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
environment:
# Custom Nodes aktivieren
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true

65
docker-compose.yml Normal file
View File

@ -0,0 +1,65 @@
# Docker Compose für n8n mit LibreBooking Node
#
# Verwendung:
# docker-compose up -d # Im Hintergrund starten
# docker-compose logs -f # Logs anzeigen
# docker-compose down # Stoppen und entfernen
# docker-compose build --no-cache # Neu bauen
version: '3.8'
services:
n8n:
build:
context: .
dockerfile: Dockerfile
container_name: n8n-librebooking
restart: unless-stopped
ports:
- "5678:5678"
environment:
# Basis-Konfiguration
- N8N_BASIC_AUTH_ACTIVE=true
- N8N_BASIC_AUTH_USER=${N8N_BASIC_AUTH_USER:-admin}
- N8N_BASIC_AUTH_PASSWORD=${N8N_BASIC_AUTH_PASSWORD:-changeme}
# Webhook-URL (für Produktionsumgebung anpassen)
- WEBHOOK_URL=${WEBHOOK_URL:-http://localhost:5678/}
# Timezone
- GENERIC_TIMEZONE=${TZ:-Europe/Berlin}
- TZ=${TZ:-Europe/Berlin}
# Custom Extensions
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
# Optional: Logging
- N8N_LOG_LEVEL=${N8N_LOG_LEVEL:-info}
# Optional: Executions
- EXECUTIONS_DATA_SAVE_ON_ERROR=all
- EXECUTIONS_DATA_SAVE_ON_SUCCESS=all
- EXECUTIONS_DATA_SAVE_MANUAL_EXECUTIONS=true
volumes:
# Persistente Daten
- n8n_data:/home/node/.n8n
# Workflow-Dateien (optional)
- ./workflows:/home/node/workflows:ro
networks:
- n8n-network
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:5678/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
volumes:
n8n_data:
driver: local
name: n8n-librebooking-data
networks:
n8n-network:
driver: bridge
name: n8n-librebooking-network

268
fix-node-installation.sh Executable file
View File

@ -0,0 +1,268 @@
#!/bin/bash
# ============================================================================
# fix-node-installation.sh - All-in-One Lösung für Node-Installation im Container
# Führt alle Schritte automatisch aus (auf dem HOST ausführen!)
# ============================================================================
set -e
# Farben
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# Standard-Werte
CONTAINER_NAME="n8n"
CONTAINER_PATH="/home/node/.n8n/custom/n8n-nodes-librebooking"
SKIP_RESTART=false
VERBOSE=false
echo -e "${BLUE}============================================${NC}"
echo -e "${BLUE} LibreBooking Node - Auto-Fix Installation${NC}"
echo -e "${BLUE}============================================${NC}"
echo ""
# Hilfe anzeigen
show_help() {
echo "Verwendung: $0 [OPTIONEN]"
echo ""
echo "Dieses Skript installiert den LibreBooking Node automatisch im Docker Container."
echo ""
echo "Optionen:"
echo " -c, --container NAME Container-Name (Standard: n8n)"
echo " -p, --path PATH Pfad im Container (Standard: /home/node/.n8n/custom/n8n-nodes-librebooking)"
echo " -n, --no-restart Container nicht neustarten"
echo " -v, --verbose Ausführliche Ausgabe"
echo " -h, --help Diese Hilfe anzeigen"
echo ""
echo "Beispiele:"
echo " $0 # Standard-Installation"
echo " $0 -c mein-n8n # Anderer Container-Name"
echo " $0 -p /opt/n8n/custom-nodes # Anderer Pfad"
echo " $0 -n # Ohne Neustart"
exit 0
}
# Parameter parsen
while [[ $# -gt 0 ]]; do
case $1 in
-c|--container)
CONTAINER_NAME="$2"
shift 2
;;
-p|--path)
CONTAINER_PATH="$2"
shift 2
;;
-n|--no-restart)
SKIP_RESTART=true
shift
;;
-v|--verbose)
VERBOSE=true
shift
;;
-h|--help)
show_help
;;
*)
echo -e "${RED}Unbekannte Option: $1${NC}"
show_help
;;
esac
done
log() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
exit 1
}
# Funktion: Prüft ob ein Verzeichnis im Container read-only ist
check_readonly_container() {
local container="$1"
local dir="$2"
local test_file="$dir/.write_test_$$"
# Versuche eine Test-Datei im Container zu erstellen
if docker exec "$container" touch "$test_file" 2>/dev/null; then
docker exec "$container" rm -f "$test_file" 2>/dev/null
return 0 # Schreibbar
else
return 1 # Read-only
fi
}
print_readonly_solution() {
echo ""
echo -e "${RED}============================================${NC}"
echo -e "${RED} PROBLEM: Read-only Volume erkannt!${NC}"
echo -e "${RED}============================================${NC}"
echo ""
echo "Das custom-nodes Verzeichnis ist als read-only gemountet."
echo "npm install und npm run build benötigen Schreibrechte."
echo ""
echo "LÖSUNGEN:"
echo ""
echo "1. Volume OHNE :ro mounten (empfohlen):"
echo " In docker-compose.yml oder docker-compose.override.yml:"
echo ""
echo " volumes:"
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
echo " # ENTFERNEN: :ro am Ende!"
echo ""
echo "2. Auf dem Host bauen (für read-only Volumes):"
echo " ./build-on-host.sh"
echo " # Dann docker-compose.readonly.yml verwenden"
echo ""
echo "3. docker-compose.override.yml anpassen:"
echo " cp docker-compose.override.yml docker-compose.override.yml.bak"
echo " # Entfernen Sie ':ro' aus der Volume-Definition"
echo ""
echo "Siehe: TROUBLESHOOTING.md und DOCKER-INTEGRATION.md"
}
# Schritt 1: Docker prüfen
log "Prüfe Docker..."
if ! command -v docker &>/dev/null; then
error "Docker ist nicht installiert!"
fi
# Schritt 2: Container prüfen
log "Prüfe Container '$CONTAINER_NAME'..."
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
# Prüfe ob Container existiert aber nicht läuft
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
warn "Container '$CONTAINER_NAME' existiert aber läuft nicht."
log "Starte Container..."
docker start "$CONTAINER_NAME"
sleep 3
else
error "Container '$CONTAINER_NAME' nicht gefunden! Prüfen Sie den Namen mit: docker ps -a"
fi
fi
log "Container '$CONTAINER_NAME' läuft ✓"
# Schritt 3: Prüfe ob Pfad im Container existiert
log "Prüfe Pfad im Container: $CONTAINER_PATH"
if ! docker exec "$CONTAINER_NAME" test -d "$CONTAINER_PATH"; then
# Versuche alternative Pfade
ALTERNATIVE_PATHS=(
"/home/node/.n8n/custom/n8n-nodes-librebooking"
"/home/node/.n8n/custom"
"/opt/n8n/custom-nodes"
"/data/custom-nodes"
)
FOUND_PATH=""
for alt_path in "${ALTERNATIVE_PATHS[@]}"; do
if docker exec "$CONTAINER_NAME" test -f "$alt_path/package.json" 2>/dev/null; then
FOUND_PATH="$alt_path"
break
fi
done
if [ -n "$FOUND_PATH" ]; then
warn "Angegebener Pfad nicht gefunden, verwende: $FOUND_PATH"
CONTAINER_PATH="$FOUND_PATH"
else
error "Kein Custom-Node-Verzeichnis mit package.json gefunden!\n\n Bitte stellen Sie sicher, dass die Dateien korrekt kopiert wurden.\n Beispiel: docker cp custom-nodes/. $CONTAINER_NAME:/home/node/.n8n/custom/n8n-nodes-librebooking/"
fi
fi
log "Pfad gefunden: $CONTAINER_PATH"
# Schritt 3.5: Prüfe ob Verzeichnis schreibbar ist (read-only Volume?)
log "Prüfe Schreibrechte im Container..."
if ! check_readonly_container "$CONTAINER_NAME" "$CONTAINER_PATH"; then
print_readonly_solution
exit 1
fi
log "Schreibrechte vorhanden ✓"
# Schritt 4: Skript in Container kopieren
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
if [ -f "$SCRIPT_DIR/install-in-container.sh" ]; then
log "Kopiere install-in-container.sh in Container..."
docker cp "$SCRIPT_DIR/install-in-container.sh" "$CONTAINER_NAME:/tmp/"
else
warn "install-in-container.sh nicht gefunden, erstelle inline..."
fi
# Schritt 5: npm install und build im Container
log "Führe npm install aus..."
if $VERBOSE; then
docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm install
else
if ! docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm install 2>&1 | tail -5; then
error "npm install fehlgeschlagen!"
fi
fi
log "Dependencies installiert ✓"
log "Führe npm run build aus..."
if $VERBOSE; then
docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm run build
else
if ! docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm run build 2>&1 | tail -10; then
error "npm run build fehlgeschlagen!"
fi
fi
log "Build erfolgreich ✓"
# Schritt 6: Prüfe Ergebnis
log "Prüfe Build-Ergebnis..."
NODE_COUNT=$(docker exec "$CONTAINER_NAME" find "$CONTAINER_PATH/dist" -name "*.node.js" 2>/dev/null | wc -l)
if [ "$NODE_COUNT" -gt 0 ]; then
log "$NODE_COUNT Node-Datei(en) gefunden ✓"
else
error "Keine Node-Dateien nach Build gefunden!"
fi
# Schritt 7: Container neustarten
if $SKIP_RESTART; then
warn "Container-Neustart übersprungen (-n Option)"
echo ""
echo -e "${YELLOW}Bitte starten Sie den Container manuell neu:${NC}"
echo " docker restart $CONTAINER_NAME"
else
log "Starte Container neu..."
docker restart "$CONTAINER_NAME"
log "Container neugestartet ✓"
# Warte kurz auf Start
sleep 5
fi
# Schritt 8: Abschluss-Check (optional)
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} Installation abgeschlossen!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
log "Der LibreBooking Node sollte jetzt in n8n verfügbar sein."
echo ""
echo "Nächste Schritte:"
echo " 1. Öffnen Sie n8n im Browser"
echo " 2. Erstellen Sie einen neuen Workflow"
echo " 3. Suchen Sie nach 'LibreBooking'"
echo ""
echo "Falls der Node nicht erscheint:"
echo " - Prüfen Sie die Logs: docker logs $CONTAINER_NAME 2>&1 | grep -i libre"
echo " - Führen Sie das Check-Skript aus: docker exec $CONTAINER_NAME sh /tmp/check-installation.sh"
echo ""
# Optional: Check-Skript kopieren und ausführen
if [ -f "$SCRIPT_DIR/check-installation.sh" ]; then
docker cp "$SCRIPT_DIR/check-installation.sh" "$CONTAINER_NAME:/tmp/"
echo "Tipp: Führen Sie für einen detaillierten Status aus:"
echo " docker exec $CONTAINER_NAME sh /tmp/check-installation.sh"
fi

32
git-cleanup.sh Executable file
View File

@ -0,0 +1,32 @@
#!/bin/bash
# ============================================================================
# git-cleanup.sh - Löscht alte Archive und temporäre Dateien
# ============================================================================
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
echo -e "${GREEN}=== Git Cleanup ===${NC}\n"
# Alte Archive im übergeordneten Verzeichnis löschen
echo "Lösche alte Archive in $PARENT_DIR..."
find "$PARENT_DIR" -maxdepth 1 -name "n8n-nodes-librebooking*.tar.gz" -delete 2>/dev/null && echo " ✓ .tar.gz gelöscht" || echo " - Keine .tar.gz gefunden"
find "$PARENT_DIR" -maxdepth 1 -name "n8n-nodes-librebooking*.zip" -delete 2>/dev/null && echo " ✓ .zip gelöscht" || echo " - Keine .zip gefunden"
# Temporäre Dateien im Projekt löschen
echo ""
echo "Lösche temporäre Dateien..."
rm -rf "$SCRIPT_DIR/dist-for-docker" 2>/dev/null && echo " ✓ dist-for-docker/ gelöscht" || true
rm -rf "$SCRIPT_DIR/.tsbuildinfo" 2>/dev/null && echo " ✓ .tsbuildinfo gelöscht" || true
find "$SCRIPT_DIR" -name "*.log" -delete 2>/dev/null && echo " ✓ .log Dateien gelöscht" || true
find "$SCRIPT_DIR" -name ".DS_Store" -delete 2>/dev/null || true
echo ""
echo -e "${GREEN}✓ Cleanup abgeschlossen${NC}"
echo ""

36
git-commit.sh Executable file
View File

@ -0,0 +1,36 @@
#!/bin/bash
# ============================================================================
# git-commit.sh - Committet alle Änderungen
# ============================================================================
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
MESSAGE="${1:-fix: Vereinfachte Installation - auf dem Host bauen}"
echo -e "${GREEN}=== Git Commit ===${NC}\n"
# Status anzeigen
echo "Geänderte Dateien:"
git status --short
echo ""
# Alles hinzufügen
git add .
# Commit
echo "Commit mit Nachricht: $MESSAGE"
git commit -m "$MESSAGE"
echo ""
echo -e "${GREEN}✓ Commit erfolgreich${NC}"
echo ""
echo "Zum Pushen:"
echo " git push origin main"
echo ""

6
index.ts Normal file
View File

@ -0,0 +1,6 @@
// n8n-nodes-librebooking
// Entry point for the LibreBooking n8n node package
export * from './nodes/LibreBooking/LibreBooking.node';
export * from './nodes/LibreBookingTrigger/LibreBookingTrigger.node';
export * from './credentials/LibreBookingApi.credentials';

238
install-docker-manual.sh Executable file
View File

@ -0,0 +1,238 @@
#!/bin/bash
# =============================================================================
# LibreBooking n8n Node - Manuelle Docker Installation (ohne docker-compose)
# =============================================================================
# Diese Alternative verwendet nur "docker" Befehle für maximale Kompatibilität.
# Nutzen Sie dieses Skript wenn docker-compose Probleme macht.
#
# Verwendung: ./install-docker-manual.sh [OPTIONS]
#
# Optionen:
# -p, --port PORT n8n Port (Standard: 5678)
# -n, --name NAME Container Name (Standard: n8n-librebooking)
# -f, --force Bestehenden Container ersetzen
# -h, --help Diese Hilfe anzeigen
#
# =============================================================================
set -e
# Farben für Ausgabe
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Standardwerte
N8N_PORT=5678
CONTAINER_NAME="n8n-librebooking"
FORCE=false
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Hilfsfunktionen
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[✓]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[!]${NC} $1"
}
print_error() {
echo -e "${RED}[FEHLER]${NC} $1"
}
show_help() {
echo "LibreBooking n8n Node - Manuelle Docker Installation"
echo ""
echo "Verwendung: $0 [OPTIONS]"
echo ""
echo "Optionen:"
echo " -p, --port PORT n8n Port (Standard: 5678)"
echo " -n, --name NAME Container Name (Standard: n8n-librebooking)"
echo " -f, --force Bestehenden Container ersetzen"
echo " -h, --help Diese Hilfe anzeigen"
echo ""
echo "Beispiele:"
echo " $0 # Standard-Installation"
echo " $0 -p 8080 # Anderer Port"
echo " $0 -n mein-n8n -p 9000 # Eigener Name und Port"
exit 0
}
# Argumente parsen
while [[ $# -gt 0 ]]; do
case $1 in
-p|--port)
N8N_PORT="$2"
shift 2
;;
-n|--name)
CONTAINER_NAME="$2"
shift 2
;;
-f|--force)
FORCE=true
shift
;;
-h|--help)
show_help
;;
*)
print_error "Unbekannte Option: $1"
show_help
;;
esac
done
echo ""
echo "============================================="
echo " LibreBooking n8n Node - Manuelle Installation"
echo " (ohne docker-compose)"
echo "============================================="
echo ""
# =============================================================================
# Voraussetzungen prüfen
# =============================================================================
print_info "Prüfe Voraussetzungen..."
# Docker prüfen
if ! command -v docker &> /dev/null; then
print_error "Docker ist nicht installiert!"
echo " Bitte installieren Sie Docker: https://docs.docker.com/get-docker/"
exit 1
fi
print_success "Docker gefunden: $(docker --version)"
# Prüfen ob Container bereits existiert
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
if [ "$FORCE" = true ]; then
print_warning "Bestehender Container '$CONTAINER_NAME' wird entfernt..."
docker stop "$CONTAINER_NAME" 2>/dev/null || true
docker rm "$CONTAINER_NAME" 2>/dev/null || true
else
print_error "Container '$CONTAINER_NAME' existiert bereits!"
echo " Nutzen Sie -f um ihn zu ersetzen, oder -n für einen anderen Namen."
exit 1
fi
fi
# =============================================================================
# Volume und Verzeichnisse vorbereiten
# =============================================================================
print_info "Bereite Volumes vor..."
# Docker Volume für n8n Daten erstellen
VOLUME_NAME="${CONTAINER_NAME}_data"
if ! docker volume ls --format '{{.Name}}' | grep -q "^${VOLUME_NAME}$"; then
docker volume create "$VOLUME_NAME"
print_success "Volume '$VOLUME_NAME' erstellt"
else
print_info "Volume '$VOLUME_NAME' existiert bereits"
fi
# Custom Nodes Verzeichnis vorbereiten
CUSTOM_NODES_DIR="$SCRIPT_DIR/custom-nodes"
if [ ! -d "$CUSTOM_NODES_DIR" ]; then
print_error "custom-nodes Verzeichnis nicht gefunden: $CUSTOM_NODES_DIR"
exit 1
fi
# =============================================================================
# Node bauen (wenn nicht bereits gebaut)
# =============================================================================
if [ ! -d "$CUSTOM_NODES_DIR/dist" ]; then
print_info "Baue LibreBooking Node..."
if command -v npm &> /dev/null; then
cd "$CUSTOM_NODES_DIR"
npm install 2>/dev/null || print_warning "npm install hatte Warnungen"
npm run build 2>/dev/null || {
print_warning "Build fehlgeschlagen, versuche alternativen Ansatz..."
npx tsc 2>/dev/null || true
mkdir -p dist/nodes/LibreBooking dist/nodes/LibreBookingTrigger
cp nodes/LibreBooking/*.svg dist/nodes/LibreBooking/ 2>/dev/null || true
cp nodes/LibreBookingTrigger/*.svg dist/nodes/LibreBookingTrigger/ 2>/dev/null || true
}
cd "$SCRIPT_DIR"
print_success "Node erfolgreich gebaut"
else
print_warning "npm nicht gefunden - Node wird im Container gebaut"
fi
fi
# =============================================================================
# n8n Container starten
# =============================================================================
print_info "Starte n8n Container..."
# Berechtigungen setzen
if [ "$(id -u)" = "0" ]; then
chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || true
else
sudo chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || print_warning "Konnte Berechtigungen nicht setzen"
fi
# Container starten
docker run -d \
--name "$CONTAINER_NAME" \
--restart unless-stopped \
-p "${N8N_PORT}:5678" \
-v "${VOLUME_NAME}:/home/node/.n8n" \
-v "${CUSTOM_NODES_DIR}:/home/node/.n8n/custom/n8n-nodes-librebooking:ro" \
-e N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom \
-e N8N_COMMUNITY_NODES_ENABLED=true \
-e TZ=Europe/Berlin \
-e GENERIC_TIMEZONE=Europe/Berlin \
n8nio/n8n:latest
if [ $? -eq 0 ]; then
print_success "Container gestartet!"
else
print_error "Container konnte nicht gestartet werden!"
exit 1
fi
# Warten auf Start
print_info "Warte auf n8n Start..."
sleep 5
# Status prüfen
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
print_success "n8n Container läuft!"
else
print_error "Container läuft nicht - prüfen Sie: docker logs $CONTAINER_NAME"
exit 1
fi
# =============================================================================
# Abschluss
# =============================================================================
echo ""
echo "============================================="
print_success "Installation abgeschlossen!"
echo "============================================="
echo ""
echo "n8n ist erreichbar unter: http://localhost:${N8N_PORT}"
echo ""
echo "Nützliche Befehle:"
echo " docker logs $CONTAINER_NAME # Logs anzeigen"
echo " docker stop $CONTAINER_NAME # Container stoppen"
echo " docker start $CONTAINER_NAME # Container starten"
echo " docker restart $CONTAINER_NAME # Container neustarten"
echo " docker rm -f $CONTAINER_NAME # Container löschen"
echo ""
echo "Bei Problemen siehe: TROUBLESHOOTING.md"
echo ""

454
install-docker.sh Executable file
View File

@ -0,0 +1,454 @@
#!/bin/bash
# =============================================================================
# LibreBooking n8n Node - Docker Integration Script
# =============================================================================
# Automatische Integration des LibreBooking Nodes in bestehende n8n Docker-Installationen
#
# Verwendung: ./install-docker.sh [OPTIONS]
#
# Optionen:
# -p, --path PATH Pfad zur n8n Docker-Installation (Standard: aktuelles Verzeichnis)
# -b, --build Node im Container bauen statt vorgebaut kopieren
# -f, --force Bestehende Installation überschreiben
# -h, --help Diese Hilfe anzeigen
#
# =============================================================================
set -e
# Farben für Ausgabe
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Standardwerte
N8N_PATH="."
FORCE=false
BUILD_IN_CONTAINER=false
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Hilfsfunktionen
print_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
print_success() {
echo -e "${GREEN}[✓]${NC} $1"
}
print_warning() {
echo -e "${YELLOW}[!]${NC} $1"
}
print_error() {
echo -e "${RED}[FEHLER]${NC} $1"
}
show_help() {
echo "LibreBooking n8n Node - Docker Integration Script"
echo ""
echo "Verwendung: $0 [OPTIONS]"
echo ""
echo "Optionen:"
echo " -p, --path PATH Pfad zur n8n Docker-Installation"
echo " -b, --build Node im Container bauen"
echo " -f, --force Bestehende Installation überschreiben"
echo " -h, --help Diese Hilfe anzeigen"
echo ""
echo "Beispiele:"
echo " $0 # Installation im aktuellen Verzeichnis"
echo " $0 -p /opt/n8n # Installation in /opt/n8n"
echo " $0 -f -p /home/user/n8n # Installation mit Überschreiben"
exit 0
}
# Argumente parsen
while [[ $# -gt 0 ]]; do
case $1 in
-p|--path)
N8N_PATH="$2"
shift 2
;;
-b|--build)
BUILD_IN_CONTAINER=true
shift
;;
-f|--force)
FORCE=true
shift
;;
-h|--help)
show_help
;;
*)
print_error "Unbekannte Option: $1"
show_help
;;
esac
done
echo ""
echo "========================================="
echo " LibreBooking n8n Node - Docker Setup"
echo "========================================="
echo ""
# =============================================================================
# Voraussetzungen prüfen
# =============================================================================
print_info "Prüfe Voraussetzungen..."
# Docker prüfen
if ! command -v docker &> /dev/null; then
print_error "Docker ist nicht installiert!"
echo " Bitte installieren Sie Docker: https://docs.docker.com/get-docker/"
exit 1
fi
print_success "Docker gefunden: $(docker --version)"
# Docker Compose prüfen - v2 bevorzugen
detect_compose_command() {
# Zuerst docker compose (v2/Plugin) prüfen - bevorzugt
if docker compose version &> /dev/null 2>&1; then
COMPOSE_CMD="docker compose"
COMPOSE_VERSION="v2"
return 0
fi
# Dann docker-compose (v1) prüfen
if command -v docker-compose &> /dev/null; then
# Prüfen ob es tatsächlich funktioniert (distutils Problem bei Python 3.12)
if docker-compose --version &> /dev/null 2>&1; then
COMPOSE_CMD="docker-compose"
COMPOSE_VERSION="v1"
return 0
else
# docker-compose existiert aber funktioniert nicht
print_warning "docker-compose (v1) ist installiert, funktioniert aber nicht!"
print_warning "Dies liegt wahrscheinlich am fehlenden 'distutils' Modul (Python 3.12+)"
echo ""
echo " Mögliche Lösungen:"
echo " 1. Docker Compose v2 installieren (empfohlen):"
echo " sudo apt-get update && sudo apt-get install docker-compose-plugin"
echo ""
echo " 2. distutils für Python installieren (Workaround):"
echo " sudo apt-get install python3-distutils"
echo " # Oder für neuere Systeme:"
echo " pip3 install setuptools"
echo ""
echo " Siehe TROUBLESHOOTING.md für weitere Details."
echo ""
return 1
fi
fi
return 1
}
# Compose Command ermitteln
if detect_compose_command; then
if [ "$COMPOSE_VERSION" = "v2" ]; then
print_success "Docker Compose v2 (Plugin) gefunden: $(docker compose version --short 2>/dev/null || docker compose version)"
else
print_success "docker-compose v1 gefunden: $(docker-compose --version)"
print_warning "Empfehlung: Upgrade zu Docker Compose v2 für bessere Kompatibilität"
fi
else
print_error "Docker Compose ist nicht installiert oder funktioniert nicht!"
echo ""
echo " Installation von Docker Compose v2 (empfohlen):"
echo " sudo apt-get update && sudo apt-get install docker-compose-plugin"
echo ""
echo " Oder siehe: https://docs.docker.com/compose/install/"
echo ""
echo " Alternativ: Verwenden Sie install-docker-manual.sh für Installation ohne docker-compose"
exit 1
fi
# Hilfsfunktion für Compose-Befehle
run_compose() {
$COMPOSE_CMD "$@"
}
# =============================================================================
# Funktion: Prüft ob ein Verzeichnis read-only ist
# =============================================================================
check_readonly() {
local dir="$1"
local test_file="$dir/.write_test_$$"
# Versuche eine Test-Datei zu erstellen
if touch "$test_file" 2>/dev/null; then
rm -f "$test_file" 2>/dev/null
return 0 # Schreibbar
else
return 1 # Read-only
fi
}
print_readonly_warning() {
local dir="$1"
print_error "Das Verzeichnis '$dir' ist read-only!"
echo ""
echo " Das custom-nodes Verzeichnis benötigt Schreibrechte für:"
echo " - npm install (Dependencies)"
echo " - npm run build (Kompilierung)"
echo ""
echo " LÖSUNGEN:"
echo ""
echo " 1. Volume OHNE :ro mounten (empfohlen):"
echo " volumes:"
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
echo " # NICHT: - ./custom-nodes:/...:ro"
echo ""
echo " 2. Auf dem Host bauen (für read-only Volumes):"
echo " ./build-on-host.sh"
echo " # Dann docker-compose.readonly.yml verwenden"
echo ""
echo " 3. Berechtigungen prüfen:"
echo " ls -la $dir"
echo " sudo chown -R 1000:1000 $dir"
echo ""
echo " Siehe: TROUBLESHOOTING.md und SECURITY.md"
}
# Zielpfad prüfen
if [ ! -d "$N8N_PATH" ]; then
print_error "Verzeichnis existiert nicht: $N8N_PATH"
exit 1
fi
N8N_PATH=$(cd "$N8N_PATH" && pwd)
print_info "Zielverzeichnis: $N8N_PATH"
# docker-compose.yml prüfen
if [ ! -f "$N8N_PATH/docker-compose.yml" ] && [ ! -f "$N8N_PATH/docker-compose.yaml" ]; then
print_warning "Keine docker-compose.yml gefunden in $N8N_PATH"
echo ""
read -p "Soll eine Beispiel-Konfiguration erstellt werden? (j/n): " CREATE_EXAMPLE
if [[ "$CREATE_EXAMPLE" =~ ^[jJyY]$ ]]; then
print_info "Kopiere Beispiel-Konfiguration..."
cp "$SCRIPT_DIR/docker-compose.example.yml" "$N8N_PATH/docker-compose.yml"
cp "$SCRIPT_DIR/.env.docker" "$N8N_PATH/.env"
print_success "Beispiel-Konfiguration erstellt"
print_warning "Bitte $N8N_PATH/.env anpassen!"
else
print_error "Abbruch: Keine docker-compose.yml vorhanden"
exit 1
fi
fi
# =============================================================================
# n8n Status prüfen
# =============================================================================
print_info "Prüfe n8n Container Status..."
cd "$N8N_PATH"
N8N_RUNNING=false
if $COMPOSE_CMD ps 2>/dev/null | grep -q "n8n.*Up"; then
N8N_RUNNING=true
print_success "n8n Container läuft"
else
print_warning "n8n Container läuft nicht (wird später gestartet)"
fi
# =============================================================================
# Custom Nodes Verzeichnis vorbereiten
# =============================================================================
print_info "Bereite Custom Nodes Verzeichnis vor..."
CUSTOM_NODES_DIR="$N8N_PATH/custom-nodes"
# Prüfen ob bereits installiert
if [ -d "$CUSTOM_NODES_DIR" ]; then
if [ "$FORCE" = true ]; then
print_warning "Bestehendes custom-nodes Verzeichnis wird überschrieben"
rm -rf "$CUSTOM_NODES_DIR"
else
print_warning "custom-nodes Verzeichnis existiert bereits"
read -p "Überschreiben? (j/n): " OVERWRITE
if [[ "$OVERWRITE" =~ ^[jJyY]$ ]]; then
rm -rf "$CUSTOM_NODES_DIR"
else
print_info "Behalte bestehendes Verzeichnis"
fi
fi
fi
# Custom Nodes kopieren
if [ ! -d "$CUSTOM_NODES_DIR" ]; then
cp -r "$SCRIPT_DIR/custom-nodes" "$CUSTOM_NODES_DIR"
print_success "Custom Nodes kopiert nach: $CUSTOM_NODES_DIR"
fi
# Prüfe ob Verzeichnis schreibbar ist
if ! check_readonly "$CUSTOM_NODES_DIR"; then
print_readonly_warning "$CUSTOM_NODES_DIR"
echo ""
read -p "Trotzdem fortfahren? (j/n): " CONTINUE_RO
if [[ ! "$CONTINUE_RO" =~ ^[jJyY]$ ]]; then
print_error "Abbruch wegen read-only Verzeichnis"
exit 1
fi
print_warning "Fortsetzen trotz read-only - npm install wird fehlschlagen!"
fi
# =============================================================================
# Node bauen (wenn nicht bereits gebaut)
# =============================================================================
if [ ! -d "$CUSTOM_NODES_DIR/dist" ] || [ "$BUILD_IN_CONTAINER" = true ]; then
print_info "Baue LibreBooking Node..."
cd "$CUSTOM_NODES_DIR"
# Prüfen ob node/npm vorhanden
if command -v npm &> /dev/null; then
npm install 2>/dev/null || print_warning "npm install hatte Warnungen"
npm run build 2>/dev/null || {
print_warning "Build fehlgeschlagen, versuche alternativen Ansatz..."
# Manueller Build
npx tsc 2>/dev/null || true
mkdir -p dist/nodes/LibreBooking dist/nodes/LibreBookingTrigger
cp nodes/LibreBooking/*.svg dist/nodes/LibreBooking/ 2>/dev/null || true
cp nodes/LibreBookingTrigger/*.svg dist/nodes/LibreBookingTrigger/ 2>/dev/null || true
}
print_success "Node erfolgreich gebaut"
else
print_warning "npm nicht gefunden - Node wird beim Container-Start gebaut"
print_info "Erstelle Build-Skript für Container..."
cat > "$CUSTOM_NODES_DIR/build.sh" << 'BUILDEOF'
#!/bin/sh
cd /home/node/.n8n/custom/n8n-nodes-librebooking
npm install
npm run build
BUILDEOF
chmod +x "$CUSTOM_NODES_DIR/build.sh"
fi
cd "$N8N_PATH"
fi
# =============================================================================
# Docker Compose Override erstellen/aktualisieren
# =============================================================================
print_info "Erstelle docker-compose.override.yml..."
OVERRIDE_FILE="$N8N_PATH/docker-compose.override.yml"
if [ -f "$OVERRIDE_FILE" ]; then
print_warning "docker-compose.override.yml existiert bereits"
# Prüfen ob LibreBooking bereits konfiguriert
if grep -q "n8n-nodes-librebooking" "$OVERRIDE_FILE"; then
print_success "LibreBooking bereits in override.yml konfiguriert"
else
print_info "Füge LibreBooking Konfiguration hinzu..."
# Backup erstellen
cp "$OVERRIDE_FILE" "${OVERRIDE_FILE}.backup"
# Hinzufügen (vereinfacht - für komplexe Fälle manuell anpassen)
cat >> "$OVERRIDE_FILE" << 'OVERRIDEEOF'
# LibreBooking Node (automatisch hinzugefügt)
# Falls Konflikte auftreten, bitte manuell anpassen
# Siehe DOCKER-INTEGRATION.md für Details
OVERRIDEEOF
print_warning "Bitte $OVERRIDE_FILE manuell prüfen!"
fi
else
# Neue Override-Datei erstellen
cat > "$OVERRIDE_FILE" << 'OVERRIDEEOF'
# Docker Compose Override für LibreBooking n8n Node
# Automatisch generiert von install-docker.sh
version: '3.8'
services:
n8n:
volumes:
# LibreBooking Custom Node
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
environment:
# Custom Nodes aktivieren
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true
OVERRIDEEOF
print_success "docker-compose.override.yml erstellt"
fi
# =============================================================================
# Berechtigungen setzen
# =============================================================================
print_info "Setze Berechtigungen..."
# n8n läuft oft als node User mit UID 1000
if [ "$(id -u)" = "0" ]; then
chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || print_warning "Konnte Berechtigungen nicht setzen"
else
# Versuche mit sudo falls verfügbar
if command -v sudo &> /dev/null; then
sudo chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || print_warning "Konnte Berechtigungen nicht setzen (sudo fehlgeschlagen)"
fi
fi
print_success "Berechtigungen konfiguriert"
# =============================================================================
# Container neustarten
# =============================================================================
echo ""
read -p "Soll der n8n Container jetzt neu gestartet werden? (j/n): " RESTART
if [[ "$RESTART" =~ ^[jJyY]$ ]]; then
print_info "Starte n8n Container neu..."
if [ "$N8N_RUNNING" = true ]; then
$COMPOSE_CMD restart n8n 2>/dev/null || $COMPOSE_CMD restart
else
$COMPOSE_CMD up -d n8n 2>/dev/null || $COMPOSE_CMD up -d
fi
# Warten bis Container bereit
print_info "Warte auf Container-Start..."
sleep 5
# Status prüfen
if $COMPOSE_CMD ps | grep -q "n8n.*Up"; then
print_success "n8n Container läuft!"
else
print_warning "Container-Status unklar - bitte manuell prüfen"
fi
else
print_info "Container nicht neu gestartet"
echo " Führen Sie später aus: cd $N8N_PATH && $COMPOSE_CMD restart"
fi
# =============================================================================
# Abschluss
# =============================================================================
echo ""
echo "========================================="
print_success "Installation abgeschlossen!"
echo "========================================="
echo ""
echo "Nächste Schritte:"
echo "1. Öffnen Sie n8n in Ihrem Browser"
echo "2. Gehen Sie zu: Settings > Community Nodes"
echo "3. Der 'LibreBooking' Node sollte sichtbar sein"
echo "4. Erstellen Sie neue Credentials für LibreBooking"
echo ""
echo "Bei Problemen siehe: DOCKER-INTEGRATION.md"
echo ""

191
install-in-container.sh Executable file
View File

@ -0,0 +1,191 @@
#!/bin/sh
# ============================================================================
# install-in-container.sh - Installiert den LibreBooking Node IM Docker Container
# Dieses Skript wird INNERHALB des Containers ausgeführt!
# ============================================================================
set -e
echo "============================================"
echo " LibreBooking Node - Container Installation"
echo "============================================"
echo ""
# Funktion: Prüft ob ein Verzeichnis read-only ist
check_readonly() {
local dir="$1"
local test_file="$dir/.write_test_$$"
# Versuche eine Test-Datei zu erstellen
if touch "$test_file" 2>/dev/null; then
rm -f "$test_file" 2>/dev/null
return 0 # Schreibbar (exit code 0 = true)
else
return 1 # Read-only (exit code 1 = false)
fi
}
print_readonly_error() {
local dir="$1"
echo ""
echo "============================================"
echo " FEHLER: Read-only Volume!"
echo "============================================"
echo ""
echo "Das Verzeichnis '$dir' ist read-only gemountet."
echo "npm install und npm run build benötigen Schreibrechte."
echo ""
echo "LÖSUNGEN:"
echo ""
echo "1. Volume OHNE :ro mounten:"
echo " In docker-compose.yml oder docker-compose.override.yml:"
echo ""
echo " volumes:"
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
echo " # NICHT:"
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro"
echo ""
echo "2. Auf dem Host bauen:"
echo " Führen Sie auf dem HOST aus: ./build-on-host.sh"
echo " Dann verwenden Sie: docker-compose.readonly.yml"
echo ""
echo "Siehe: TROUBLESHOOTING.md"
exit 1
}
# Mögliche Pfade für custom-nodes
POSSIBLE_PATHS="
/home/node/.n8n/custom/n8n-nodes-librebooking
/home/node/.n8n/custom
/opt/n8n/custom-nodes
/data/custom-nodes
"
CUSTOM_NODE_PATH=""
# Finde den custom-nodes Pfad
echo "[1/5] Suche custom-nodes Verzeichnis..."
for path in $POSSIBLE_PATHS; do
if [ -d "$path" ]; then
# Prüfe ob package.json vorhanden ist
if [ -f "$path/package.json" ]; then
CUSTOM_NODE_PATH="$path"
echo " ✓ Gefunden: $path"
break
elif [ -f "$path/n8n-nodes-librebooking/package.json" ]; then
CUSTOM_NODE_PATH="$path/n8n-nodes-librebooking"
echo " ✓ Gefunden: $CUSTOM_NODE_PATH"
break
fi
fi
done
if [ -z "$CUSTOM_NODE_PATH" ]; then
echo " ✗ Kein custom-nodes Verzeichnis mit package.json gefunden!"
echo ""
echo " Geprüfte Pfade:"
for path in $POSSIBLE_PATHS; do
if [ -d "$path" ]; then
echo " - $path (existiert, aber keine package.json)"
else
echo " - $path (existiert nicht)"
fi
done
echo ""
echo " Bitte stellen Sie sicher, dass die Dateien korrekt kopiert wurden."
exit 1
fi
# Wechsle ins Verzeichnis
echo "[2/5] Wechsle ins Verzeichnis: $CUSTOM_NODE_PATH"
cd "$CUSTOM_NODE_PATH"
echo " ✓ Aktuelles Verzeichnis: $(pwd)"
# Prüfe ob npm verfügbar ist
echo "[3/5] Prüfe npm..."
if ! command -v npm >/dev/null 2>&1; then
echo " ✗ npm ist nicht installiert!"
echo ""
echo " Im n8n Docker-Image sollte npm vorhanden sein."
echo " Falls nicht, verwenden Sie ein Image mit Node.js."
exit 1
fi
echo " ✓ npm Version: $(npm --version)"
echo " ✓ node Version: $(node --version)"
# Prüfe Schreibrechte (read-only Volume?)
echo "[3.5/5] Prüfe Schreibrechte..."
if ! check_readonly "$CUSTOM_NODE_PATH"; then
print_readonly_error "$CUSTOM_NODE_PATH"
fi
echo " ✓ Schreibrechte vorhanden"
# Dependencies installieren
echo "[4/5] Installiere Dependencies..."
echo " Führe 'npm install' aus..."
if npm install 2>&1; then
echo " ✓ Dependencies installiert"
else
echo " ✗ npm install fehlgeschlagen!"
echo ""
echo " Mögliche Lösungen:"
echo " - Prüfen Sie die Berechtigungen"
echo " - Prüfen Sie die Internetverbindung"
echo " - Führen Sie 'npm cache clean --force' aus"
exit 1
fi
# Build ausführen
echo "[5/5] Baue den Node..."
echo " Führe 'npm run build' aus..."
if npm run build 2>&1; then
echo " ✓ Build erfolgreich"
else
echo " ✗ Build fehlgeschlagen!"
echo ""
echo " Prüfen Sie die TypeScript-Fehler in der Ausgabe oben."
exit 1
fi
# Prüfe Ergebnis
echo ""
echo "============================================"
echo " Prüfe Installation..."
echo "============================================"
if [ -d "$CUSTOM_NODE_PATH/dist" ]; then
echo "✓ dist/ Verzeichnis existiert"
# Prüfe auf .node.js Dateien
NODE_FILES=$(find "$CUSTOM_NODE_PATH/dist" -name "*.node.js" 2>/dev/null | wc -l)
if [ "$NODE_FILES" -gt 0 ]; then
echo "$NODE_FILES Node-Datei(en) gefunden:"
find "$CUSTOM_NODE_PATH/dist" -name "*.node.js" -exec echo " - {}" \;
else
echo "✗ Keine .node.js Dateien im dist/ Verzeichnis gefunden!"
fi
# Prüfe credentials
CRED_FILES=$(find "$CUSTOM_NODE_PATH/dist" -name "*.credentials.js" 2>/dev/null | wc -l)
if [ "$CRED_FILES" -gt 0 ]; then
echo "$CRED_FILES Credential-Datei(en) gefunden"
fi
else
echo "✗ dist/ Verzeichnis wurde nicht erstellt!"
exit 1
fi
echo ""
echo "============================================"
echo " Installation abgeschlossen!"
echo "============================================"
echo ""
echo "Nächste Schritte:"
echo " 1. Verlassen Sie den Container: exit"
echo " 2. Starten Sie n8n neu: docker restart n8n"
echo " 3. Öffnen Sie n8n im Browser und suchen Sie nach 'LibreBooking'"
echo ""
echo "Falls der Node nicht erscheint:"
echo " - Prüfen Sie die Umgebungsvariable N8N_CUSTOM_EXTENSIONS"
echo " - Führen Sie ./check-installation.sh aus"
echo ""

250
install.ps1 Normal file
View File

@ -0,0 +1,250 @@
#
# LibreBooking n8n Node - Installations-Skript für Windows (PowerShell)
#
# Verwendung:
# .\install.ps1
#
# Optionen:
# -NoLink Überspringt npm link (nur Build)
# -Global Installiert global statt npm link
# -Help Zeigt diese Hilfe an
#
param(
[switch]$NoLink,
[switch]$Global,
[switch]$Help
)
# Konfiguration
$MIN_NODE_VERSION = 18
$ErrorActionPreference = "Stop"
# Funktionen
function Write-ColorOutput {
param(
[string]$Message,
[string]$Color = "White"
)
Write-Host $Message -ForegroundColor $Color
}
function Write-Header {
Write-ColorOutput "" "Blue"
Write-ColorOutput "=============================================" "Blue"
Write-ColorOutput " LibreBooking n8n Node Installer" "Blue"
Write-ColorOutput "=============================================" "Blue"
Write-ColorOutput "" "Blue"
}
function Write-Success {
param([string]$Message)
Write-ColorOutput "$Message" "Green"
}
function Write-Warning-Msg {
param([string]$Message)
Write-ColorOutput "$Message" "Yellow"
}
function Write-Error-Msg {
param([string]$Message)
Write-ColorOutput "$Message" "Red"
}
function Write-Info {
param([string]$Message)
Write-ColorOutput " $Message" "Cyan"
}
function Show-Help {
Write-Host "Verwendung: .\install.ps1 [OPTIONEN]"
Write-Host ""
Write-Host "Optionen:"
Write-Host " -NoLink Überspringt npm link (nur Build)"
Write-Host " -Global Installiert global mit npm install -g"
Write-Host " -Help Zeigt diese Hilfe an"
Write-Host ""
Write-Host "Beispiele:"
Write-Host " .\install.ps1 # Standard-Installation mit npm link"
Write-Host " .\install.ps1 -NoLink # Nur Dependencies installieren und Build"
Write-Host " .\install.ps1 -Global # Globale Installation"
exit 0
}
function Test-Command {
param([string]$Command)
try {
$null = Get-Command $Command -ErrorAction Stop
return $true
}
catch {
return $false
}
}
function Get-NodeVersion {
$version = (node -v) -replace 'v', ''
return [int]($version.Split('.')[0])
}
# Hilfe anzeigen
if ($Help) {
Show-Help
}
# Start
Write-Header
# 1. Node.js prüfen
Write-Host ""
Write-Info "Prüfe Voraussetzungen..."
Write-Host ""
if (-not (Test-Command "node")) {
Write-Error-Msg "Node.js ist nicht installiert!"
Write-Host " Bitte installiere Node.js v$MIN_NODE_VERSION oder höher:"
Write-Host " https://nodejs.org/"
exit 1
}
$nodeVersion = Get-NodeVersion
if ($nodeVersion -lt $MIN_NODE_VERSION) {
Write-Error-Msg "Node.js Version $nodeVersion ist zu alt!"
Write-Host " Mindestens Version $MIN_NODE_VERSION benötigt."
exit 1
}
$nodeFullVersion = (node -v) -replace 'v', ''
Write-Success "Node.js v$nodeFullVersion gefunden"
# 2. npm prüfen
if (-not (Test-Command "npm")) {
Write-Error-Msg "npm ist nicht installiert!"
exit 1
}
$npmVersion = npm -v
Write-Success "npm v$npmVersion gefunden"
# 3. n8n prüfen (optional)
if (Test-Command "n8n") {
try {
$n8nVersion = n8n --version 2>$null
Write-Success "n8n $n8nVersion gefunden"
}
catch {
Write-Success "n8n installiert"
}
}
else {
Write-Warning-Msg "n8n ist nicht global installiert."
Write-Host " Für npm link muss n8n global installiert sein:"
Write-Host " npm install -g n8n"
Write-Host ""
if (-not $NoLink -and -not $Global) {
$response = Read-Host "Möchtest du trotzdem fortfahren? (j/N)"
if ($response -notmatch '^[Jj]$') {
exit 1
}
}
}
# 4. Dependencies installieren
Write-Host ""
Write-Info "Installiere Dependencies..."
try {
npm install
Write-Success "Dependencies installiert"
}
catch {
Write-Error-Msg "Fehler bei npm install: $_"
exit 1
}
# 5. TypeScript kompilieren
Write-Host ""
Write-Info "Kompiliere TypeScript..."
try {
npm run build
Write-Success "Build erfolgreich"
}
catch {
Write-Error-Msg "Fehler beim Build: $_"
exit 1
}
# 6. npm link oder global install
if ($Global) {
Write-Host ""
Write-Info "Installiere global..."
try {
npm install -g .
Write-Success "Global installiert"
}
catch {
Write-Error-Msg "Fehler bei globaler Installation: $_"
exit 1
}
}
elseif (-not $NoLink) {
Write-Host ""
Write-Info "Verlinke mit npm link..."
try {
npm link
Write-Success "npm link erfolgreich"
# Versuche mit n8n zu verlinken
if (Test-Command "n8n") {
$n8nPath = Join-Path (npm root -g) "n8n"
if (Test-Path $n8nPath) {
Write-Info "Verlinke mit n8n..."
Push-Location $n8nPath
try {
npm link n8n-nodes-librebooking 2>$null
Write-Success "Mit n8n verlinkt"
}
catch {
Write-Warning-Msg "Konnte nicht automatisch mit n8n verlinken"
}
finally {
Pop-Location
}
}
}
}
catch {
Write-Error-Msg "Fehler bei npm link: $_"
exit 1
}
}
# Abschluss
Write-Host ""
Write-ColorOutput "=============================================" "Green"
Write-ColorOutput " Installation erfolgreich abgeschlossen!" "Green"
Write-ColorOutput "=============================================" "Green"
Write-Host ""
Write-Host "Nächste Schritte:"
Write-Host ""
if ($NoLink) {
Write-Host " 1. Führe 'npm link' aus, um den Node zu verlinken"
Write-Host " 2. Starte n8n neu: n8n start"
}
else {
Write-Host " 1. Starte n8n neu: n8n start"
Write-Host " (oder mit Docker: docker-compose restart)"
}
Write-Host ""
Write-Host " 2. Öffne n8n im Browser: http://localhost:5678"
Write-Host " 3. Der LibreBooking Node sollte verfügbar sein"
Write-Host ""
Write-Host "Bei Problemen siehe INSTALLATION.md oder README.md"
Write-Host ""

209
install.sh Executable file
View File

@ -0,0 +1,209 @@
#!/bin/bash
#
# LibreBooking n8n Node - Installations-Skript für Linux/Mac
#
# Verwendung:
# chmod +x install.sh
# ./install.sh
#
# Optionen:
# --no-link Überspringt npm link (nur Build)
# --global Installiert global statt npm link
# --help Zeigt diese Hilfe an
#
set -e
# Farben für Ausgabe
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Konfiguration
MIN_NODE_VERSION=18
REQUIRED_NPM_VERSION=8
# Optionen
SKIP_LINK=false
GLOBAL_INSTALL=false
# Funktionen
print_header() {
echo -e "${BLUE}"
echo "============================================="
echo " LibreBooking n8n Node Installer"
echo "============================================="
echo -e "${NC}"
}
print_success() {
echo -e "${GREEN}$1${NC}"
}
print_warning() {
echo -e "${YELLOW}$1${NC}"
}
print_error() {
echo -e "${RED}$1${NC}"
}
print_info() {
echo -e "${BLUE} $1${NC}"
}
show_help() {
echo "Verwendung: ./install.sh [OPTIONEN]"
echo ""
echo "Optionen:"
echo " --no-link Überspringt npm link (nur Build)"
echo " --global Installiert global mit npm install -g"
echo " --help Zeigt diese Hilfe an"
echo ""
echo "Beispiele:"
echo " ./install.sh # Standard-Installation mit npm link"
echo " ./install.sh --no-link # Nur Dependencies installieren und Build"
echo " ./install.sh --global # Globale Installation"
exit 0
}
check_command() {
if command -v $1 &> /dev/null; then
return 0
else
return 1
fi
}
get_node_version() {
node -v | sed 's/v//' | cut -d. -f1
}
get_npm_version() {
npm -v | cut -d. -f1
}
# Parameter verarbeiten
for arg in "$@"; do
case $arg in
--no-link)
SKIP_LINK=true
;;
--global)
GLOBAL_INSTALL=true
;;
--help|-h)
show_help
;;
*)
print_error "Unbekannte Option: $arg"
show_help
;;
esac
done
# Start
print_header
# 1. Node.js prüfen
echo ""
print_info "Prüfe Voraussetzungen..."
echo ""
if ! check_command node; then
print_error "Node.js ist nicht installiert!"
echo " Bitte installiere Node.js v${MIN_NODE_VERSION} oder höher:"
echo " https://nodejs.org/"
exit 1
fi
NODE_VERSION=$(get_node_version)
if [ "$NODE_VERSION" -lt "$MIN_NODE_VERSION" ]; then
print_error "Node.js Version $NODE_VERSION ist zu alt!"
echo " Mindestens Version ${MIN_NODE_VERSION} benötigt."
exit 1
fi
print_success "Node.js v$(node -v | sed 's/v//') gefunden"
# 2. npm prüfen
if ! check_command npm; then
print_error "npm ist nicht installiert!"
exit 1
fi
print_success "npm v$(npm -v) gefunden"
# 3. n8n prüfen (optional, aber empfohlen)
if check_command n8n; then
print_success "n8n $(n8n --version 2>/dev/null || echo 'installiert') gefunden"
else
print_warning "n8n ist nicht global installiert."
echo " Für npm link muss n8n global installiert sein:"
echo " npm install -g n8n"
echo ""
if [ "$SKIP_LINK" = false ] && [ "$GLOBAL_INSTALL" = false ]; then
read -p "Möchtest du trotzdem fortfahren? (j/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Jj]$ ]]; then
exit 1
fi
fi
fi
# 4. Dependencies installieren
echo ""
print_info "Installiere Dependencies..."
npm install
print_success "Dependencies installiert"
# 5. TypeScript kompilieren
echo ""
print_info "Kompiliere TypeScript..."
npm run build
print_success "Build erfolgreich"
# 6. npm link oder global install
if [ "$GLOBAL_INSTALL" = true ]; then
echo ""
print_info "Installiere global..."
npm install -g .
print_success "Global installiert"
elif [ "$SKIP_LINK" = false ]; then
echo ""
print_info "Verlinke mit npm link..."
npm link
print_success "npm link erfolgreich"
# Prüfen ob n8n vorhanden und linken
if check_command n8n; then
N8N_PATH=$(npm root -g)/n8n
if [ -d "$N8N_PATH" ]; then
print_info "Verlinke mit n8n..."
cd "$N8N_PATH" 2>/dev/null && npm link n8n-nodes-librebooking 2>/dev/null && cd - > /dev/null
print_success "Mit n8n verlinkt"
fi
fi
fi
# Abschluss
echo ""
echo -e "${GREEN}=============================================${NC}"
echo -e "${GREEN} Installation erfolgreich abgeschlossen!${NC}"
echo -e "${GREEN}=============================================${NC}"
echo ""
echo "Nächste Schritte:"
echo ""
if [ "$SKIP_LINK" = true ]; then
echo " 1. Führe 'npm link' aus, um den Node zu verlinken"
echo " 2. Starte n8n neu: n8n start"
else
echo " 1. Starte n8n neu: n8n start"
echo " (oder mit Docker: docker-compose restart)"
fi
echo ""
echo " 2. Öffne n8n im Browser: http://localhost:5678"
echo " 3. Der LibreBooking Node sollte verfügbar sein"
echo ""
echo "Bei Problemen siehe INSTALLATION.md oder README.md"
echo ""

Binary file not shown.

Binary file not shown.

122
nginx.conf Normal file
View File

@ -0,0 +1,122 @@
# Nginx Reverse Proxy Konfiguration für n8n mit LibreBooking
# Beispielkonfiguration für HTTPS Zugang
#
# Verwendung:
# 1. Diese Datei nach /etc/nginx/sites-available/n8n kopieren
# 2. ln -s /etc/nginx/sites-available/n8n /etc/nginx/sites-enabled/
# 3. SSL-Zertifikate einrichten (z.B. mit Certbot)
# 4. nginx -t && systemctl reload nginx
# Upstream für n8n
upstream n8n_backend {
server 127.0.0.1:5678;
keepalive 32;
}
# HTTP -> HTTPS Redirect
server {
listen 80;
listen [::]:80;
server_name n8n.example.com;
# ACME Challenge für Let's Encrypt
location /.well-known/acme-challenge/ {
root /var/www/certbot;
}
location / {
return 301 https://$host$request_uri;
}
}
# HTTPS Server
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name n8n.example.com;
# SSL-Zertifikate (Let's Encrypt Beispiel)
ssl_certificate /etc/letsencrypt/live/n8n.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/n8n.example.com/privkey.pem;
# SSL-Einstellungen (moderne Konfiguration)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers off;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;
# Logging
access_log /var/log/nginx/n8n_access.log;
error_log /var/log/nginx/n8n_error.log;
# Proxy-Einstellungen
location / {
proxy_pass http://n8n_backend;
proxy_http_version 1.1;
# WebSocket Support (wichtig für n8n Editor)
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Standard Proxy Headers
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Port $server_port;
# Timeouts (erhöht für lange Workflow-Ausführungen)
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# Buffer-Einstellungen
proxy_buffering off;
proxy_buffer_size 4k;
# Client-Upload Limit (anpassen nach Bedarf)
client_max_body_size 50M;
}
# Webhook-spezifische Einstellungen
location /webhook/ {
proxy_pass http://n8n_backend;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# Erhöhte Timeouts für Webhooks
proxy_connect_timeout 60s;
proxy_send_timeout 600s;
proxy_read_timeout 600s;
proxy_buffering off;
client_max_body_size 100M;
}
# Health Check Endpoint
location /healthz {
proxy_pass http://n8n_backend/healthz;
proxy_http_version 1.1;
proxy_set_header Host $host;
access_log off;
}
}
# Optional: Monitoring/Metrics Server Block
# server {
# listen 127.0.0.1:9090;
# server_name localhost;
#
# location /nginx_status {
# stub_status on;
# allow 127.0.0.1;
# deny all;
# }
# }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
<!-- Background -->
<rect width="60" height="60" rx="8" fill="#2C3E50"/>
<!-- Calendar base -->
<rect x="10" y="15" width="40" height="35" rx="3" fill="#ECF0F1" stroke="#3498DB" stroke-width="2"/>
<!-- Calendar header -->
<rect x="10" y="15" width="40" height="10" rx="3" fill="#3498DB"/>
<rect x="10" y="22" width="40" height="3" fill="#3498DB"/>
<!-- Calendar rings -->
<rect x="18" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<rect x="38" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<!-- Grid lines -->
<line x1="10" y1="33" x2="50" y2="33" stroke="#BDC3C7" stroke-width="1"/>
<line x1="10" y1="41" x2="50" y2="41" stroke="#BDC3C7" stroke-width="1"/>
<line x1="23" y1="25" x2="23" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<line x1="37" y1="25" x2="37" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<!-- Booking indicator -->
<rect x="25" y="35" width="10" height="4" rx="1" fill="#27AE60"/>
<!-- Check mark -->
<path d="M39 27 L42 30 L48 22" stroke="#FFFFFF" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,352 @@
import {
INodeType,
INodeTypeDescription,
IPollFunctions,
INodeExecutionData,
NodeApiError,
NodeOperationError,
} from 'n8n-workflow';
interface LibreBookingSession {
sessionToken: string;
userId: number;
sessionExpires: string;
}
interface ReservationData {
referenceNumber: string;
startDate: string;
endDate: string;
title: string;
resourceId: number;
userId: number;
[key: string]: any;
}
/**
* Authentifizierung bei LibreBooking
*/
async function authenticateTrigger(
pollFunctions: IPollFunctions,
baseUrl: string,
username: string,
password: string,
): Promise<LibreBookingSession> {
try {
const response = await pollFunctions.helpers.httpRequest({
method: 'POST',
url: `${baseUrl}/Web/Services/index.php/Authentication/Authenticate`,
headers: { 'Content-Type': 'application/json' },
body: { username, password },
json: true,
});
if (!response.isAuthenticated) {
throw new NodeOperationError(
pollFunctions.getNode(),
'Authentifizierung fehlgeschlagen',
);
}
return {
sessionToken: response.sessionToken,
userId: response.userId,
sessionExpires: response.sessionExpires,
};
} catch (error: any) {
throw new NodeApiError(pollFunctions.getNode(), error, {
message: 'Authentifizierung fehlgeschlagen',
});
}
}
/**
* Abmeldung
*/
async function signOutTrigger(
pollFunctions: IPollFunctions,
baseUrl: string,
session: LibreBookingSession,
): Promise<void> {
try {
await pollFunctions.helpers.httpRequest({
method: 'POST',
url: `${baseUrl}/Web/Services/index.php/Authentication/SignOut`,
headers: { 'Content-Type': 'application/json' },
body: {
userId: session.userId,
sessionToken: session.sessionToken,
},
json: true,
});
} catch (error) {
// Ignoriere SignOut-Fehler
}
}
/**
* Reservierungen abrufen
*/
async function getReservations(
pollFunctions: IPollFunctions,
baseUrl: string,
session: LibreBookingSession,
startDateTime: string,
endDateTime: string,
filters: any,
): Promise<ReservationData[]> {
const qs: any = {
startDateTime,
endDateTime,
};
if (filters.resourceId) qs.resourceId = filters.resourceId;
if (filters.scheduleId) qs.scheduleId = filters.scheduleId;
if (filters.userId) qs.userId = filters.userId;
const response = await pollFunctions.helpers.httpRequest({
method: 'GET',
url: `${baseUrl}/Web/Services/index.php/Reservations/`,
headers: {
'Content-Type': 'application/json',
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
},
qs,
json: true,
});
return response.reservations || [];
}
/**
* Detaillierte Reservierungsdaten abrufen
*/
async function getReservationDetails(
pollFunctions: IPollFunctions,
baseUrl: string,
session: LibreBookingSession,
referenceNumber: string,
): Promise<any> {
const response = await pollFunctions.helpers.httpRequest({
method: 'GET',
url: `${baseUrl}/Web/Services/index.php/Reservations/${referenceNumber}`,
headers: {
'Content-Type': 'application/json',
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
},
json: true,
});
return response;
}
/**
* Zeitfenster berechnen
*/
function getTimeWindow(timeWindow: string): { start: string; end: string } {
const now = new Date();
const start = now.toISOString();
let endDate = new Date(now);
switch (timeWindow) {
case '7days':
endDate.setDate(endDate.getDate() + 7);
break;
case '14days':
endDate.setDate(endDate.getDate() + 14);
break;
case '30days':
endDate.setDate(endDate.getDate() + 30);
break;
case '90days':
endDate.setDate(endDate.getDate() + 90);
break;
default:
endDate.setDate(endDate.getDate() + 14);
}
return {
start,
end: endDate.toISOString(),
};
}
/**
* Eindeutigen Schlüssel für Reservierung generieren
*/
function getReservationKey(reservation: ReservationData): string {
return `${reservation.referenceNumber}_${reservation.startDate}_${reservation.endDate}_${reservation.title || ''}`;
}
/**
* LibreBooking Trigger Node
*/
export class LibreBookingTrigger implements INodeType {
description: INodeTypeDescription = {
displayName: 'LibreBooking Trigger',
name: 'libreBookingTrigger',
icon: 'file:librebooking.svg',
group: ['trigger'],
version: 1,
description: 'Wird bei neuen oder geänderten Reservierungen in LibreBooking ausgelöst',
subtitle: '={{$parameter["event"]}}',
defaults: {
name: 'LibreBooking Trigger',
},
inputs: [],
outputs: ['main'],
credentials: [
{
name: 'libreBookingApi',
required: true,
},
],
polling: true,
properties: [
{
displayName: 'Event',
name: 'event',
type: 'options',
options: [
{ name: 'Neue Reservierung', value: 'newReservation', description: 'Wird bei neuen Reservierungen ausgelöst' },
{ name: 'Geänderte Reservierung', value: 'updatedReservation', description: 'Wird bei geänderten Reservierungen ausgelöst' },
{ name: 'Alle Reservierungen', value: 'allReservations', description: 'Wird bei neuen und geänderten Reservierungen ausgelöst' },
],
default: 'newReservation',
},
{
displayName: 'Filter',
name: 'filters',
type: 'collection',
placeholder: 'Filter hinzufügen',
default: {},
options: [
{ displayName: 'Ressourcen-ID', name: 'resourceId', type: 'number', default: '' },
{ displayName: 'Zeitplan-ID', name: 'scheduleId', type: 'number', default: '' },
{ displayName: 'Benutzer-ID', name: 'userId', type: 'number', default: '' },
],
},
{
displayName: 'Zeitfenster',
name: 'timeWindow',
type: 'options',
options: [
{ name: 'Nächste 7 Tage', value: '7days' },
{ name: 'Nächste 14 Tage', value: '14days' },
{ name: 'Nächste 30 Tage', value: '30days' },
{ name: 'Nächste 90 Tage', value: '90days' },
],
default: '14days',
},
{
displayName: 'Optionen',
name: 'options',
type: 'collection',
placeholder: 'Option hinzufügen',
default: {},
options: [
{ displayName: 'Detaillierte Daten Abrufen', name: 'fetchDetails', type: 'boolean', default: false },
],
},
],
};
async poll(this: IPollFunctions): Promise<INodeExecutionData[][] | null> {
const credentials = await this.getCredentials('libreBookingApi');
const baseUrl = (credentials.url as string).replace(/\/$/, '');
const username = credentials.username as string;
const password = credentials.password as string;
const event = this.getNodeParameter('event') as string;
const filters = this.getNodeParameter('filters', {}) as any;
const timeWindow = this.getNodeParameter('timeWindow', '14days') as string;
const options = this.getNodeParameter('options', {}) as any;
const workflowStaticData = this.getWorkflowStaticData('node');
const previousReservations = (workflowStaticData.reservations as Record<string, string>) || {};
let session: LibreBookingSession;
try {
session = await authenticateTrigger(this, baseUrl, username, password);
} catch (error) {
throw error;
}
try {
const { start, end } = getTimeWindow(timeWindow);
const reservations = await getReservations(
this,
baseUrl,
session,
start,
end,
filters,
);
const returnData: INodeExecutionData[] = [];
const currentReservations: Record<string, string> = {};
for (const reservation of reservations) {
const refNumber = reservation.referenceNumber;
const reservationKey = getReservationKey(reservation);
currentReservations[refNumber] = reservationKey;
const isNew = !previousReservations[refNumber];
const isUpdated = previousReservations[refNumber] && previousReservations[refNumber] !== reservationKey;
let shouldTrigger = false;
let eventType = '';
if (event === 'newReservation' && isNew) {
shouldTrigger = true;
eventType = 'new';
} else if (event === 'updatedReservation' && isUpdated) {
shouldTrigger = true;
eventType = 'updated';
} else if (event === 'allReservations' && (isNew || isUpdated)) {
shouldTrigger = true;
eventType = isNew ? 'new' : 'updated';
}
if (shouldTrigger) {
let reservationData = reservation;
if (options.fetchDetails) {
try {
reservationData = await getReservationDetails(
this,
baseUrl,
session,
refNumber,
);
} catch (error) {
reservationData = reservation;
}
}
returnData.push({
json: {
...reservationData,
_eventType: eventType,
_triggeredAt: new Date().toISOString(),
},
});
}
}
workflowStaticData.reservations = currentReservations;
if (returnData.length === 0) {
return null;
}
return [returnData];
} finally {
await signOutTrigger(this, baseUrl, session);
}
}
}

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 60 60" width="60" height="60">
<!-- Background -->
<rect width="60" height="60" rx="8" fill="#2C3E50"/>
<!-- Calendar base -->
<rect x="10" y="15" width="40" height="35" rx="3" fill="#ECF0F1" stroke="#3498DB" stroke-width="2"/>
<!-- Calendar header -->
<rect x="10" y="15" width="40" height="10" rx="3" fill="#3498DB"/>
<rect x="10" y="22" width="40" height="3" fill="#3498DB"/>
<!-- Calendar rings -->
<rect x="18" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<rect x="38" y="12" width="4" height="8" rx="1" fill="#2C3E50"/>
<!-- Grid lines -->
<line x1="10" y1="33" x2="50" y2="33" stroke="#BDC3C7" stroke-width="1"/>
<line x1="10" y1="41" x2="50" y2="41" stroke="#BDC3C7" stroke-width="1"/>
<line x1="23" y1="25" x2="23" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<line x1="37" y1="25" x2="37" y2="50" stroke="#BDC3C7" stroke-width="1"/>
<!-- Booking indicator -->
<rect x="25" y="35" width="10" height="4" rx="1" fill="#27AE60"/>
<!-- Check mark -->
<path d="M39 27 L42 30 L48 22" stroke="#FFFFFF" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

3067
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

90
package.json Normal file
View File

@ -0,0 +1,90 @@
{
"name": "n8n-nodes-librebooking",
"version": "1.1.0",
"description": "n8n Node für LibreBooking - Ressourcen- und Reservierungsverwaltung",
"keywords": [
"n8n-community-node-package",
"n8n",
"n8n-node",
"workflow",
"automation",
"librebooking",
"booking",
"reservation",
"resource-management",
"room-booking",
"raumbuchung",
"terminbuchung",
"open-source"
],
"license": "MIT",
"homepage": "https://github.com/your-org/n8n-nodes-librebooking#readme",
"author": {
"name": "LibreBooking n8n Integration",
"email": "support@example.com"
},
"repository": {
"type": "git",
"url": "git+https://github.com/your-org/n8n-nodes-librebooking.git"
},
"bugs": {
"url": "https://github.com/your-org/n8n-nodes-librebooking/issues"
},
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "tsc && npm run copy:icons",
"copy:icons": "cp nodes/LibreBooking/librebooking.svg dist/nodes/LibreBooking/ && cp nodes/LibreBookingTrigger/librebooking.svg dist/nodes/LibreBookingTrigger/",
"dev": "tsc --watch",
"clean": "rm -rf dist node_modules",
"rebuild": "npm run clean && npm install && npm run build",
"format": "prettier nodes credentials --write",
"lint": "eslint nodes credentials --ext .ts",
"lint:fix": "eslint nodes credentials --ext .ts --fix",
"prepack": "npm run build",
"prepublishOnly": "npm run lint && npm run build",
"postinstall": "echo 'Installation abgeschlossen. Führe npm run build aus.'",
"test": "ts-node test/test-api.ts",
"link:n8n": "npm link && echo 'Node verlinkt. Starte n8n neu mit: n8n start'",
"unlink": "npm unlink -g n8n-nodes-librebooking",
"install-in-docker": "npm install && npm run build && echo '\\n✓ Installation abgeschlossen. Bitte n8n Container neustarten: docker restart n8n'",
"docker:copy": "docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/ && docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/ && docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/",
"docker:restart": "docker restart n8n",
"docker:deploy": "npm run build && npm run docker:copy && npm run docker:restart"
},
"files": [
"dist",
"README.md",
"LICENSE"
],
"n8n": {
"n8nNodesApiVersion": 1,
"credentials": [
"dist/credentials/LibreBookingApi.credentials.js"
],
"nodes": [
"dist/nodes/LibreBooking/LibreBooking.node.js",
"dist/nodes/LibreBookingTrigger/LibreBookingTrigger.node.js"
]
},
"devDependencies": {
"@types/node": "^20.10.0",
"@typescript-eslint/eslint-plugin": "^6.13.0",
"@typescript-eslint/parser": "^6.13.0",
"eslint": "^8.54.0",
"n8n-workflow": "^1.20.0",
"prettier": "^3.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.3.2"
},
"peerDependencies": {
"n8n-workflow": "*"
},
"engines": {
"node": ">=18.17.0"
},
"overrides": {
"form-data": "^4.0.1",
"lodash": "^4.17.21"
}
}

71
quick-install.sh Executable file
View File

@ -0,0 +1,71 @@
#!/bin/bash
# ============================================================================
# quick-install.sh - Schnellste Installation des LibreBooking n8n Nodes
#
# EMPFOHLENE METHODE: Auf dem Host bauen, in Container kopieren
# ============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
CONTAINER_NAME="${1:-n8n}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo -e "${GREEN}=== LibreBooking Quick Install ===${NC}\n"
# Prüfe Voraussetzungen
if ! command -v node &>/dev/null; then
echo -e "${RED}Fehler: Node.js nicht installiert!${NC}"
echo "Installieren: https://nodejs.org/ (v18+)"
exit 1
fi
if ! command -v docker &>/dev/null; then
echo -e "${RED}Fehler: Docker nicht installiert!${NC}"
exit 1
fi
if ! docker ps | grep -q "$CONTAINER_NAME"; then
echo -e "${YELLOW}Warnung: Container '$CONTAINER_NAME' nicht gefunden oder läuft nicht.${NC}"
echo "Verfügbare Container:"
docker ps --format " {{.Names}}"
exit 1
fi
cd "$SCRIPT_DIR"
# 1. Dependencies installieren
echo "[1/4] Installiere Dependencies..."
npm install --silent
# 2. Bauen
echo "[2/4] Baue Node..."
npm run build --silent
# 3. In Container kopieren
echo "[3/4] Kopiere in Container '$CONTAINER_NAME'..."
# Erstelle Zielverzeichnis falls nötig
docker exec "$CONTAINER_NAME" mkdir -p /home/node/.n8n/custom/n8n-nodes-librebooking 2>/dev/null || true
# Kopiere Dateien
docker cp dist "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp package.json "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp node_modules "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
# 4. Container neustarten
echo "[4/4] Starte Container neu..."
docker restart "$CONTAINER_NAME"
echo ""
echo -e "${GREEN}✓ Installation abgeschlossen!${NC}"
echo ""
echo "Nächste Schritte:"
echo " 1. Öffne n8n: http://localhost:5678"
echo " 2. Gehe zu: Einstellungen → Credentials → Add Credential"
echo " 3. Suche: 'LibreBooking API'"
echo ""

325
test/test-api.ts Normal file
View File

@ -0,0 +1,325 @@
/**
* LibreBooking API Test-Skript
*
* Testet die Authentifizierung und grundlegende API-Operationen
* mit den bereitgestellten Test-Credentials.
*
* Ausführen mit: npx ts-node test/test-api.ts
*/
const https = require('https');
const http = require('http');
// Test-Credentials
const TEST_CONFIG = {
url: 'https://librebooking.zell-cloud.de',
username: 'sebastian.zell@zell-aufmass.de',
password: 'wanUQ4uVqU6lfP',
};
interface LibreBookingSession {
sessionToken: string;
userId: number;
sessionExpires: string;
}
/**
* HTTP/HTTPS Request Helper
*/
async function makeRequest(
url: string,
method: string,
headers: Record<string, string>,
body?: any
): Promise<any> {
return new Promise((resolve, reject) => {
const urlObj = new URL(url);
const isHttps = urlObj.protocol === 'https:';
const lib = isHttps ? https : http;
const options = {
hostname: urlObj.hostname,
port: urlObj.port || (isHttps ? 443 : 80),
path: urlObj.pathname + urlObj.search,
method,
headers: {
'Content-Type': 'application/json',
...headers,
},
};
const req = lib.request(options, (res: any) => {
let data = '';
res.on('data', (chunk: string) => (data += chunk));
res.on('end', () => {
try {
const jsonData = JSON.parse(data);
resolve({ statusCode: res.statusCode, data: jsonData });
} catch (e) {
resolve({ statusCode: res.statusCode, data });
}
});
});
req.on('error', reject);
if (body) {
req.write(JSON.stringify(body));
}
req.end();
});
}
/**
* Authentifizierung testen
*/
async function testAuthentication(): Promise<LibreBookingSession | null> {
console.log('\n========================================');
console.log('TEST 1: Authentifizierung');
console.log('========================================');
try {
const response = await makeRequest(
`${TEST_CONFIG.url}/Web/Services/index.php/Authentication/Authenticate`,
'POST',
{},
{
username: TEST_CONFIG.username,
password: TEST_CONFIG.password,
}
);
if (response.statusCode === 200 && response.data.isAuthenticated) {
console.log('✅ Authentifizierung erfolgreich!');
console.log(` Session Token: ${response.data.sessionToken.substring(0, 20)}...`);
console.log(` User ID: ${response.data.userId}`);
console.log(` Session läuft ab: ${response.data.sessionExpires}`);
return {
sessionToken: response.data.sessionToken,
userId: response.data.userId,
sessionExpires: response.data.sessionExpires,
};
} else {
console.log('❌ Authentifizierung fehlgeschlagen!');
console.log(' Response:', JSON.stringify(response.data, null, 2));
return null;
}
} catch (error: any) {
console.log('❌ Fehler bei der Authentifizierung:', error.message);
return null;
}
}
/**
* Alle Reservierungen abrufen
*/
async function testGetReservations(session: LibreBookingSession): Promise<void> {
console.log('\n========================================');
console.log('TEST 2: Reservierungen abrufen');
console.log('========================================');
try {
const response = await makeRequest(
`${TEST_CONFIG.url}/Web/Services/index.php/Reservations/`,
'GET',
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
if (response.statusCode === 200) {
const reservations = response.data.reservations || [];
console.log(`${reservations.length} Reservierung(en) gefunden`);
if (reservations.length > 0) {
console.log('\n Erste 3 Reservierungen:');
reservations.slice(0, 3).forEach((res: any, idx: number) => {
console.log(` ${idx + 1}. ${res.title || 'Ohne Titel'}`);
console.log(` Referenz: ${res.referenceNumber}`);
console.log(` Ressource: ${res.resourceName}`);
console.log(` Zeit: ${res.startDate} - ${res.endDate}`);
console.log('');
});
}
} else {
console.log(`❌ Fehler beim Abrufen: Status ${response.statusCode}`);
console.log(' Response:', JSON.stringify(response.data, null, 2));
}
} catch (error: any) {
console.log('❌ Fehler:', error.message);
}
}
/**
* Alle Ressourcen abrufen
*/
async function testGetResources(session: LibreBookingSession): Promise<void> {
console.log('\n========================================');
console.log('TEST 3: Ressourcen abrufen');
console.log('========================================');
try {
const response = await makeRequest(
`${TEST_CONFIG.url}/Web/Services/index.php/Resources/`,
'GET',
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
if (response.statusCode === 200) {
const resources = response.data.resources || [];
console.log(`${resources.length} Ressource(n) gefunden`);
resources.forEach((res: any, idx: number) => {
console.log(` ${idx + 1}. ${res.name} (ID: ${res.resourceId})`);
if (res.location) console.log(` Standort: ${res.location}`);
console.log(` Status: ${res.statusId === 1 ? 'Verfügbar' : res.statusId === 0 ? 'Versteckt' : 'Nicht verfügbar'}`);
});
} else {
console.log(`❌ Fehler beim Abrufen: Status ${response.statusCode}`);
}
} catch (error: any) {
console.log('❌ Fehler:', error.message);
}
}
/**
* Alle Zeitpläne abrufen
*/
async function testGetSchedules(session: LibreBookingSession): Promise<void> {
console.log('\n========================================');
console.log('TEST 4: Zeitpläne abrufen');
console.log('========================================');
try {
const response = await makeRequest(
`${TEST_CONFIG.url}/Web/Services/index.php/Schedules/`,
'GET',
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
if (response.statusCode === 200) {
const schedules = response.data.schedules || [];
console.log(`${schedules.length} Zeitplan/Zeitpläne gefunden`);
schedules.forEach((schedule: any, idx: number) => {
console.log(` ${idx + 1}. ${schedule.name} (ID: ${schedule.id})`);
console.log(` Zeitzone: ${schedule.timezone}`);
console.log(` Standard: ${schedule.isDefault ? 'Ja' : 'Nein'}`);
});
} else {
console.log(`❌ Fehler beim Abrufen: Status ${response.statusCode}`);
}
} catch (error: any) {
console.log('❌ Fehler:', error.message);
}
}
/**
* Alle Benutzer abrufen
*/
async function testGetUsers(session: LibreBookingSession): Promise<void> {
console.log('\n========================================');
console.log('TEST 5: Benutzer abrufen');
console.log('========================================');
try {
const response = await makeRequest(
`${TEST_CONFIG.url}/Web/Services/index.php/Users/`,
'GET',
{
'X-Booked-SessionToken': session.sessionToken,
'X-Booked-UserId': session.userId.toString(),
}
);
if (response.statusCode === 200) {
const users = response.data.users || [];
console.log(`${users.length} Benutzer gefunden`);
users.slice(0, 5).forEach((user: any, idx: number) => {
console.log(` ${idx + 1}. ${user.firstName} ${user.lastName}`);
console.log(` E-Mail: ${user.emailAddress}`);
console.log(` ID: ${user.id}`);
});
if (users.length > 5) {
console.log(` ... und ${users.length - 5} weitere`);
}
} else {
console.log(`❌ Fehler beim Abrufen: Status ${response.statusCode}`);
}
} catch (error: any) {
console.log('❌ Fehler:', error.message);
}
}
/**
* Abmelden
*/
async function testSignOut(session: LibreBookingSession): Promise<void> {
console.log('\n========================================');
console.log('TEST 6: Abmelden');
console.log('========================================');
try {
const response = await makeRequest(
`${TEST_CONFIG.url}/Web/Services/index.php/Authentication/SignOut`,
'POST',
{},
{
userId: session.userId,
sessionToken: session.sessionToken,
}
);
if (response.statusCode === 200 || response.statusCode === 204) {
console.log('✅ Erfolgreich abgemeldet');
} else {
console.log(`⚠️ Abmeldung mit Status ${response.statusCode} abgeschlossen`);
}
} catch (error: any) {
console.log('⚠️ Abmeldung fehlgeschlagen (kann ignoriert werden):', error.message);
}
}
/**
* Hauptfunktion
*/
async function runTests(): Promise<void> {
console.log('\n📝 LibreBooking API Test');
console.log('======================================');
console.log(`URL: ${TEST_CONFIG.url}`);
console.log(`User: ${TEST_CONFIG.username}`);
console.log('======================================');
// Test 1: Authentifizierung
const session = await testAuthentication();
if (!session) {
console.log('\n❌ Tests abgebrochen - Authentifizierung fehlgeschlagen');
process.exit(1);
}
// Test 2-5: API-Endpunkte
await testGetReservations(session);
await testGetResources(session);
await testGetSchedules(session);
await testGetUsers(session);
// Test 6: Abmelden
await testSignOut(session);
console.log('\n========================================');
console.log('✅ Alle Tests abgeschlossen!');
console.log('========================================\n');
}
// Tests ausführen
runTests().catch(console.error);

31
tsconfig.json Normal file
View File

@ -0,0 +1,31 @@
{
"compilerOptions": {
"target": "ES2019",
"module": "commonjs",
"lib": ["ES2019", "ES2020.Promise"],
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": ".",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"noImplicitAny": true,
"noImplicitReturns": true,
"noUnusedLocals": false,
"noUnusedParameters": false
},
"include": [
"nodes/**/*.ts",
"credentials/**/*.ts"
],
"exclude": [
"node_modules",
"dist",
"test"
]
}

112
update-dependencies.sh Executable file
View File

@ -0,0 +1,112 @@
#!/bin/bash
# ============================================================================
# update-dependencies.sh - Aktualisiert Dependencies und behebt Vulnerabilities
# ============================================================================
set -e
# Farben
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo -e "${BLUE}============================================${NC}"
echo -e "${BLUE} LibreBooking Node - Dependency Update${NC}"
echo -e "${BLUE}============================================${NC}"
echo ""
log() {
echo -e "${GREEN}[INFO]${NC} $1"
}
warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
error() {
echo -e "${RED}[ERROR]${NC} $1"
}
cd "$SCRIPT_DIR"
# Schritt 1: Aktuelle Vulnerabilities anzeigen
log "Prüfe aktuelle Vulnerabilities..."
echo ""
echo "=== VOR dem Update ==="
npm audit 2>/dev/null || true
echo ""
# Schritt 2: npm audit fix
log "Führe npm audit fix aus..."
if npm audit fix 2>&1; then
log "npm audit fix erfolgreich ✓"
else
warn "npm audit fix hatte Probleme (evtl. normale Warnungen)"
fi
# Schritt 3: npm update
log "Führe npm update aus..."
if npm update 2>&1; then
log "npm update erfolgreich ✓"
else
warn "npm update hatte Probleme"
fi
# Schritt 4: Outdated Packages prüfen
log "Prüfe veraltete Packages..."
echo ""
npm outdated 2>/dev/null || log "Alle Packages sind aktuell ✓"
echo ""
# Schritt 5: Build testen
log "Teste Build..."
if npm run build 2>&1 | tail -5; then
log "Build erfolgreich ✓"
else
error "Build fehlgeschlagen!"
echo ""
echo "Versuchen Sie:"
echo " 1. rm -rf node_modules package-lock.json"
echo " 2. npm install"
echo " 3. npm run build"
exit 1
fi
# Schritt 6: Finale Vulnerability-Prüfung
log "Finale Vulnerability-Prüfung..."
echo ""
echo "=== NACH dem Update ==="
npm audit 2>/dev/null || true
echo ""
# Schritt 7: Report
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} Update abgeschlossen!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
# Verbleibende Vulnerabilities zählen
AUDIT_OUTPUT=$(npm audit 2>/dev/null || true)
if echo "$AUDIT_OUTPUT" | grep -q "found 0 vulnerabilities"; then
log "Keine Vulnerabilities mehr! ✓"
else
VULN_COUNT=$(echo "$AUDIT_OUTPUT" | grep -oP '\d+ vulnerabilities' | head -1 || echo "Einige")
warn "Verbleibende Vulnerabilities: $VULN_COUNT"
echo ""
echo "Diese kommen wahrscheinlich von n8n-workflow Dependencies."
echo "Siehe SECURITY.md für weitere Informationen."
fi
echo ""
echo "Nächste Schritte:"
echo " 1. Testen Sie die Änderungen lokal"
echo " 2. Committen Sie die Änderungen:"
echo " git add package.json package-lock.json"
echo " git commit -m 'chore: update dependencies'"
echo " 3. Bauen Sie das Docker-Image neu:"
echo " docker compose build --no-cache"
echo ""

61
update-node.sh Executable file
View File

@ -0,0 +1,61 @@
#!/bin/bash
# ============================================================================
# update-node.sh - Aktualisiert den LibreBooking n8n Node
#
# Verwendung nach git pull oder Änderungen am Code
# ============================================================================
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
CONTAINER_NAME="${1:-n8n}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo -e "${GREEN}=== LibreBooking Node Update ===${NC}\n"
cd "$SCRIPT_DIR"
# Git Pull (optional)
if [ -d ".git" ]; then
echo "[1/5] Hole neueste Änderungen..."
git pull 2>/dev/null || echo -e "${YELLOW}Git pull übersprungen${NC}"
else
echo "[1/5] Kein Git Repository - übersprungen"
fi
# Dependencies aktualisieren
echo "[2/5] Aktualisiere Dependencies..."
npm install --silent
# Bauen
echo "[3/5] Baue Node..."
npm run build --silent
# Prüfe Container
if ! docker ps | grep -q "$CONTAINER_NAME"; then
echo -e "${YELLOW}Container '$CONTAINER_NAME' nicht gefunden.${NC}"
echo "Verfügbare Container:"
docker ps --format " {{.Names}}"
echo ""
echo "Manuell kopieren:"
echo " docker cp dist <container>:/home/node/.n8n/custom/n8n-nodes-librebooking/"
exit 0
fi
# In Container kopieren
echo "[4/5] Kopiere in Container '$CONTAINER_NAME'..."
docker cp dist "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp package.json "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
docker cp node_modules "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
# Container neustarten
echo "[5/5] Starte Container neu..."
docker restart "$CONTAINER_NAME"
echo ""
echo -e "${GREEN}✓ Update abgeschlossen!${NC}"
echo ""

View File

@ -0,0 +1,265 @@
{
"name": "LibreBooking Beispiel-Workflows",
"description": "Sammlung von Beispiel-Workflows für den LibreBooking n8n Node",
"workflows": [
{
"name": "1. Alle Reservierungen abrufen",
"description": "Ruft alle Reservierungen der nächsten 14 Tage ab",
"nodes": [
{
"parameters": {},
"name": "Start",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [100, 300]
},
{
"parameters": {
"resource": "reservation",
"operation": "getAll",
"filters": {}
},
"name": "Alle Reservierungen",
"type": "n8n-nodes-librebooking.libreBooking",
"typeVersion": 1,
"position": [300, 300],
"credentials": {
"libreBookingApi": "LibreBooking Account"
}
}
],
"connections": {
"Start": {
"main": [[{"node": "Alle Reservierungen", "type": "main", "index": 0}]]
}
}
},
{
"name": "2. Neue Reservierung erstellen",
"description": "Erstellt eine neue Reservierung für morgen 10:00-11:00 Uhr",
"nodes": [
{
"parameters": {},
"name": "Start",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [100, 300]
},
{
"parameters": {
"resource": "reservation",
"operation": "create",
"resourceId": 1,
"startDateTime": "={{ $now.plus({days: 1}).set({hour: 10, minute: 0, second: 0}).toISO() }}",
"endDateTime": "={{ $now.plus({days: 1}).set({hour: 11, minute: 0, second: 0}).toISO() }}",
"title": "Automatisch erstellte Reservierung",
"additionalFields": {
"description": "Diese Reservierung wurde automatisch über n8n erstellt"
}
},
"name": "Reservierung erstellen",
"type": "n8n-nodes-librebooking.libreBooking",
"typeVersion": 1,
"position": [300, 300],
"credentials": {
"libreBookingApi": "LibreBooking Account"
}
}
],
"connections": {
"Start": {
"main": [[{"node": "Reservierung erstellen", "type": "main", "index": 0}]]
}
}
},
{
"name": "3. Ressourcen-Verfügbarkeit prüfen",
"description": "Prüft die Verfügbarkeit aller Ressourcen",
"nodes": [
{
"parameters": {},
"name": "Start",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [100, 300]
},
{
"parameters": {
"resource": "resource",
"operation": "getAvailability"
},
"name": "Verfügbarkeit prüfen",
"type": "n8n-nodes-librebooking.libreBooking",
"typeVersion": 1,
"position": [300, 300],
"credentials": {
"libreBookingApi": "LibreBooking Account"
}
},
{
"parameters": {
"conditions": {
"boolean": [
{
"value1": "={{ $json.available }}",
"value2": true
}
]
}
},
"name": "Nur Verfügbare",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [500, 300]
}
],
"connections": {
"Start": {
"main": [[{"node": "Verfügbarkeit prüfen", "type": "main", "index": 0}]]
},
"Verfügbarkeit prüfen": {
"main": [[{"node": "Nur Verfügbare", "type": "main", "index": 0}]]
}
}
},
{
"name": "4. Benutzer-Übersicht",
"description": "Ruft alle Benutzer ab und formatiert sie",
"nodes": [
{
"parameters": {},
"name": "Start",
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [100, 300]
},
{
"parameters": {
"resource": "user",
"operation": "getAll",
"userFilters": {}
},
"name": "Alle Benutzer",
"type": "n8n-nodes-librebooking.libreBooking",
"typeVersion": 1,
"position": [300, 300],
"credentials": {
"libreBookingApi": "LibreBooking Account"
}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "return {\n json: {\n name: `${$json.firstName} ${$json.lastName}`,\n email: $json.emailAddress,\n organization: $json.organization || 'Keine',\n lastLogin: $json.lastLogin\n }\n};"
},
"name": "Formatieren",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [500, 300]
}
],
"connections": {
"Start": {
"main": [[{"node": "Alle Benutzer", "type": "main", "index": 0}]]
},
"Alle Benutzer": {
"main": [[{"node": "Formatieren", "type": "main", "index": 0}]]
}
}
},
{
"name": "5. Trigger: Neue Reservierungen überwachen",
"description": "Trigger-Workflow der bei neuen Reservierungen auslöst",
"nodes": [
{
"parameters": {
"event": "newReservation",
"filters": {},
"timeWindow": "14days",
"options": {
"fetchDetails": true
}
},
"name": "LibreBooking Trigger",
"type": "n8n-nodes-librebooking.libreBookingTrigger",
"typeVersion": 1,
"position": [100, 300],
"credentials": {
"libreBookingApi": "LibreBooking Account"
}
},
{
"parameters": {
"mode": "runOnceForEachItem",
"jsCode": "const reservation = $json;\nreturn {\n json: {\n message: `Neue Reservierung: ${reservation.title || 'Ohne Titel'}`,\n resource: reservation.resourceName,\n start: reservation.startDate,\n end: reservation.endDate,\n user: `${reservation.firstName} ${reservation.lastName}`\n }\n};"
},
"name": "Nachricht formatieren",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [300, 300]
}
],
"connections": {
"LibreBooking Trigger": {
"main": [[{"node": "Nachricht formatieren", "type": "main", "index": 0}]]
}
}
},
{
"name": "6. Täglicher Reservierungsbericht",
"description": "Sendet täglich eine Übersicht der heutigen Reservierungen",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 8 * * *"
}
]
}
},
"name": "Täglich 8:00",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1,
"position": [100, 300]
},
{
"parameters": {
"resource": "reservation",
"operation": "getAll",
"filters": {
"startDateTime": "={{ $now.startOf('day').toISO() }}",
"endDateTime": "={{ $now.endOf('day').toISO() }}"
}
},
"name": "Heutige Reservierungen",
"type": "n8n-nodes-librebooking.libreBooking",
"typeVersion": 1,
"position": [300, 300],
"credentials": {
"libreBookingApi": "LibreBooking Account"
}
},
{
"parameters": {
"aggregate": "aggregateAllItemData"
},
"name": "Zusammenfassen",
"type": "n8n-nodes-base.aggregate",
"typeVersion": 1,
"position": [500, 300]
}
],
"connections": {
"Täglich 8:00": {
"main": [[{"node": "Heutige Reservierungen", "type": "main", "index": 0}]]
},
"Heutige Reservierungen": {
"main": [[{"node": "Zusammenfassen", "type": "main", "index": 0}]]
}
}
}
]
}