Initial commit

This commit is contained in:
Sebastian Zell 2026-01-25 14:43:42 +01:00
commit 1af67a06d1
43 changed files with 11130 additions and 0 deletions

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/

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*

109
CHANGELOG.md Normal file
View File

@ -0,0 +1,109 @@
# Changelog
Alle wichtigen Änderungen an diesem Projekt werden in dieser Datei dokumentiert.
Das Format basiert auf [Keep a Changelog](https://keepachangelog.com/de/1.0.0/),
und dieses Projekt folgt [Semantic Versioning](https://semver.org/lang/de/).
## [Unreleased]
### Geplant
- Webhook-basierter Trigger (falls von LibreBooking unterstützt)
- Batch-Operationen für mehrere Reservierungen
- Erweiterte Filteroptionen
## [1.0.0] - 2026-01-25
### Hinzugefügt
#### LibreBooking Node
- **Reservierung (Reservation)**
- Alle Reservierungen abrufen (GetAll)
- Reservierung nach Referenznummer abrufen (Get)
- Neue Reservierung erstellen (Create)
- Reservierung aktualisieren (Update)
- Reservierung löschen (Delete)
- Reservierung genehmigen (Approve)
- Check-In durchführen (CheckIn)
- Check-Out durchführen (CheckOut)
- **Ressource (Resource)**
- Alle Ressourcen abrufen (GetAll)
- Ressource nach ID abrufen (Get)
- Verfügbarkeit prüfen (GetAvailability)
- Status abrufen (GetStatus)
- Neue Ressource erstellen (Create)
- Ressource aktualisieren (Update)
- Ressource löschen (Delete)
- **Zeitplan (Schedule)**
- Alle Zeitpläne abrufen (GetAll)
- Zeitplan nach ID abrufen (Get)
- Slots abrufen (GetSlots)
- **Benutzer (User)**
- Alle Benutzer abrufen (GetAll)
- Benutzer nach ID abrufen (Get)
- Neuen Benutzer erstellen (Create)
- Benutzer aktualisieren (Update)
- Benutzer löschen (Delete)
- **Konto (Account)**
- Eigenes Konto abrufen (Get)
- Konto aktualisieren (Update)
- Passwort ändern (ChangePassword)
- **Gruppe (Group)**
- Alle Gruppen abrufen (GetAll)
- Gruppe nach ID abrufen (Get)
- Neue Gruppe erstellen (Create)
- Gruppe aktualisieren (Update)
- Gruppe löschen (Delete)
- **Zubehör (Accessory)**
- Alles Zubehör abrufen (GetAll)
- Zubehör nach ID abrufen (Get)
- Neues Zubehör erstellen (Create)
- Zubehör aktualisieren (Update)
- Zubehör löschen (Delete)
- **Attribut (Attribute)**
- Attributkategorien abrufen (GetCategories)
- Attribute nach Kategorie abrufen (GetByCategory)
#### LibreBooking Trigger Node
- Polling-basierter Trigger für Reservierungs-Events
- Event-Typen:
- Neue Reservierung
- Geänderte Reservierung
- Alle Reservierungen
- Filter nach Ressource, Zeitplan und Benutzer
- Konfigurierbares Zeitfenster (7-90 Tage)
- Deduplizierung von Events
#### Credentials
- LibreBooking API Credentials mit Session-basierter Authentifizierung
- Automatische Token-Verwaltung
- Verbindungstest integriert
#### Dokumentation
- Vollständige README.md auf Deutsch
- Detaillierte INSTALLATION.md
- Beispiel-Workflows
- API-Dokumentation mit allen Operationen
#### Entwickler-Tools
- Docker-Support mit Dockerfile und docker-compose
- Installations-Skripte für Linux/Mac und Windows
- Test-Suite für API-Verbindung
- ESLint und Prettier Konfiguration
### Sicherheit
- Keine Speicherung von Passwörtern im Klartext
- Session-basierte Authentifizierung
- Automatisches Sign-Out nach Operationen
---
[Unreleased]: https://github.com/DEIN-REPO/n8n-nodes-librebooking/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/DEIN-REPO/n8n-nodes-librebooking/releases/tag/v1.0.0

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! 🙏

592
DOCKER-INTEGRATION.md Normal file
View File

@ -0,0 +1,592 @@
# Docker-Integration für LibreBooking n8n Node
Diese Anleitung beschreibt die Integration des LibreBooking Nodes in eine **bestehende n8n Docker-Installation**.
## Inhaltsverzeichnis
- [Voraussetzungen](#voraussetzungen)
- [Methode 1: Automatische Integration mit Skript](#methode-1-automatische-integration-mit-skript)
- [Methode 2: Manuelle Integration](#methode-2-manuelle-integration)
- [Methode 3: Integration in bestehende docker-compose.yml](#methode-3-integration-in-bestehende-docker-composeyml)
- [Methode 4: Dockerfile erweitern](#methode-4-dockerfile-erweitern)
- [Verifizierung der Installation](#verifizierung-der-installation)
- [Troubleshooting](#troubleshooting)
- [Updates und Wartung](#updates-und-wartung)
---
## Voraussetzungen
### System-Anforderungen
- **Docker** Version 20.10 oder höher
- **Docker Compose** v2.0+ (Plugin) oder docker-compose v1.29+
- **Laufende n8n Docker-Installation**
- **Zugriff auf das Dateisystem** des Docker-Hosts
### Prüfen der Voraussetzungen
```bash
# Docker Version prüfen
docker --version
# Docker version 24.0.x, build xxxxx
# Docker Compose Version prüfen
docker compose version
# Docker Compose version v2.x.x
# Oder für ältere Versionen:
docker-compose --version
# docker-compose version 1.29.x, build xxxxx
# n8n Container Status prüfen
docker ps | grep n8n
```
---
## Methode 1: Automatische Integration mit Skript
Die einfachste Methode für die Integration in eine bestehende Installation.
### Schritt 1: Skript ausführbar machen
```bash
chmod +x install-docker.sh
```
### Schritt 2: Skript ausführen
```bash
# Im aktuellen Verzeichnis (wenn dort n8n läuft)
./install-docker.sh
# Oder mit Pfad zur n8n Installation
./install-docker.sh -p /pfad/zu/n8n
# Mit Überschreiben bestehender Dateien
./install-docker.sh -f -p /pfad/zu/n8n
```
### Skript-Optionen
| Option | Beschreibung |
|--------|--------------|
| `-p, --path PATH` | Pfad zur n8n Docker-Installation |
| `-b, --build` | Node im Container bauen |
| `-f, --force` | Bestehende Installation überschreiben |
| `-h, --help` | Hilfe anzeigen |
### Was das Skript tut
1. Prüft Docker und Docker Compose Installation
2. Prüft ob n8n Container läuft
3. Kopiert `custom-nodes/` Verzeichnis
4. Erstellt/aktualisiert `docker-compose.override.yml`
5. Setzt korrekte Berechtigungen (UID 1000)
6. Startet Container bei Bedarf neu
---
## Methode 2: Manuelle Integration
Für mehr Kontrolle oder spezielle Setups.
### Schritt 1: Custom Nodes Verzeichnis kopieren
```bash
# Zum n8n Verzeichnis wechseln
cd /pfad/zu/ihrer/n8n/installation
# Custom Nodes kopieren
cp -r /pfad/zu/librebooking_n8n_node/custom-nodes ./custom-nodes
```
### Schritt 2: Dependencies installieren und bauen
```bash
cd custom-nodes
# Node.js Dependencies installieren
npm install
# TypeScript kompilieren
npm run build
```
### Schritt 3: docker-compose.override.yml erstellen
Erstellen Sie eine `docker-compose.override.yml` Datei:
```yaml
version: '3.8'
services:
n8n:
volumes:
# LibreBooking Custom Node einbinden
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
environment:
# Custom Nodes Pfad
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
# Community Nodes aktivieren
- N8N_COMMUNITY_NODES_ENABLED=true
```
### Schritt 4: Berechtigungen setzen
```bash
# n8n läuft als "node" User mit UID 1000
sudo chown -R 1000:1000 custom-nodes/
```
### Schritt 5: Container neustarten
```bash
# Mit docker-compose
docker-compose restart n8n
# Oder mit Docker Compose Plugin
docker compose restart n8n
# Bei Problemen: Container komplett neu erstellen
docker-compose down && docker-compose up -d
```
---
## Methode 3: Integration in bestehende docker-compose.yml
Wenn Sie keine Override-Datei verwenden möchten.
### Bestehende docker-compose.yml erweitern
Fügen Sie folgende Einträge zu Ihrem n8n Service hinzu:
```yaml
version: '3.8'
services:
n8n:
image: n8nio/n8n:latest
# ... Ihre bestehende Konfiguration ...
volumes:
# Bestehende Volumes beibehalten
- n8n_data:/home/node/.n8n
# LibreBooking Node hinzufügen
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
environment:
# Bestehende Umgebungsvariablen beibehalten
- N8N_HOST=0.0.0.0
- N8N_PORT=5678
# Custom Nodes Konfiguration hinzufügen
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
- N8N_COMMUNITY_NODES_ENABLED=true
```
### Vollständiges Beispiel
Siehe `docker-compose.example.yml` für eine vollständige Konfiguration mit:
- PostgreSQL Datenbank (optional)
- Redis Queue (optional)
- Health Checks
- Alle Umgebungsvariablen
---
## Methode 4: Dockerfile erweitern
Für produktive Deployments oder wenn Sie ein eigenes Image benötigen.
### Einfaches Dockerfile
```dockerfile
# Dockerfile.custom-nodes
ARG N8N_VERSION=latest
FROM n8nio/n8n:${N8N_VERSION}
USER root
# Custom Node Verzeichnis erstellen
RUN mkdir -p /home/node/.n8n/custom/n8n-nodes-librebooking && \
chown -R node:node /home/node/.n8n/custom
WORKDIR /home/node/.n8n/custom/n8n-nodes-librebooking
# Dateien kopieren
COPY --chown=node:node custom-nodes/ ./
# Dependencies installieren und bauen
RUN npm install && npm run build
USER node
WORKDIR /home/node
ENV N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
ENV N8N_COMMUNITY_NODES_ENABLED=true
EXPOSE 5678
CMD ["n8n", "start"]
```
### Image bauen und verwenden
```bash
# Image bauen
docker build -f Dockerfile.custom-nodes -t n8n-librebooking .
# Container starten
docker run -d \
--name n8n \
-p 5678:5678 \
-v n8n_data:/home/node/.n8n \
n8n-librebooking
```
### Mit docker-compose
```yaml
version: '3.8'
services:
n8n:
build:
context: .
dockerfile: Dockerfile.custom-nodes
args:
N8N_VERSION: latest
# ... weitere Konfiguration
```
### Multi-Stage Build (Optimiert)
```dockerfile
# Build Stage
FROM node:18-alpine AS builder
WORKDIR /build
COPY custom-nodes/ ./
RUN npm install && npm run build
# Production Stage
ARG N8N_VERSION=latest
FROM n8nio/n8n:${N8N_VERSION}
USER root
RUN mkdir -p /home/node/.n8n/custom/n8n-nodes-librebooking && \
chown -R node:node /home/node/.n8n/custom
# Nur gebaute Dateien kopieren
COPY --from=builder --chown=node:node /build/dist /home/node/.n8n/custom/n8n-nodes-librebooking/dist
COPY --from=builder --chown=node:node /build/package.json /home/node/.n8n/custom/n8n-nodes-librebooking/
USER node
ENV N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
ENV N8N_COMMUNITY_NODES_ENABLED=true
CMD ["n8n", "start"]
```
---
## Verifizierung der Installation
### 1. Container-Logs prüfen
```bash
# Logs anzeigen
docker logs n8n 2>&1 | grep -i "librebooking\|custom\|node"
# Live Logs verfolgen
docker logs -f n8n
```
### 2. In n8n prüfen
1. Öffnen Sie n8n im Browser (z.B. `http://localhost:5678`)
2. Erstellen Sie einen neuen Workflow
3. Fügen Sie einen neuen Node hinzu
4. Suchen Sie nach "LibreBooking" - zwei Nodes sollten erscheinen:
- **LibreBooking** - Hauptnode für alle Operationen
- **LibreBooking Trigger** - Trigger für neue Reservierungen
### 3. Node-Verzeichnis im Container prüfen
```bash
# In Container einloggen
docker exec -it n8n /bin/sh
# Custom Nodes Verzeichnis prüfen
ls -la /home/node/.n8n/custom/
# LibreBooking Node prüfen
ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/dist/
```
### 4. Umgebungsvariablen prüfen
```bash
docker exec n8n env | grep N8N_CUSTOM
# N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
docker exec n8n env | grep COMMUNITY
# N8N_COMMUNITY_NODES_ENABLED=true
```
---
## Troubleshooting
### Problem: Node wird nicht erkannt
**Symptom:** LibreBooking Node erscheint nicht in der Node-Suche
**Lösungen:**
1. **Pfad prüfen:**
```bash
docker exec n8n ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/
```
2. **Umgebungsvariablen prüfen:**
```bash
docker exec n8n env | grep -E "N8N_CUSTOM|COMMUNITY"
```
3. **Container komplett neu starten:**
```bash
docker-compose down
docker-compose up -d
```
4. **Logs auf Fehler prüfen:**
```bash
docker logs n8n 2>&1 | grep -i error
```
### Problem: Permissions-Fehler
**Symptom:** `EACCES: permission denied` oder ähnliche Fehler
**Lösungen:**
1. **Berechtigungen setzen:**
```bash
sudo chown -R 1000:1000 custom-nodes/
sudo chmod -R 755 custom-nodes/
```
2. **Volume mit korrektem User mounten:**
```yaml
services:
n8n:
user: "1000:1000"
```
3. **SELinux Context (falls aktiviert):**
```bash
sudo chcon -R -t svirt_sandbox_file_t custom-nodes/
```
### Problem: Container startet nicht
**Symptom:** Container crashed oder startet nicht
**Lösungen:**
1. **Logs prüfen:**
```bash
docker logs n8n
```
2. **Ohne Override starten (zum Testen):**
```bash
mv docker-compose.override.yml docker-compose.override.yml.bak
docker-compose up -d
```
3. **Volume-Konflikte prüfen:**
```bash
docker volume ls
docker volume inspect n8n_data
```
### Problem: Node wird geladen aber Fehler bei Ausführung
**Symptom:** Node ist sichtbar aber Ausführung schlägt fehl
**Lösungen:**
1. **Build prüfen:**
```bash
docker exec n8n ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/dist/
```
2. **Neu bauen:**
```bash
cd custom-nodes
npm run rebuild
docker-compose restart n8n
```
3. **Dependencies prüfen:**
```bash
docker exec -it n8n /bin/sh
cd /home/node/.n8n/custom/n8n-nodes-librebooking
npm ls
```
### Problem: TypeScript Build schlägt fehl
**Symptom:** `tsc` Fehler beim Bauen
**Lösungen:**
1. **Node.js Version prüfen:**
```bash
node --version # Sollte 18+ sein
npm --version
```
2. **Clean Build:**
```bash
cd custom-nodes
rm -rf node_modules dist package-lock.json
npm install
npm run build
```
### Problem: Webhook URL nicht erreichbar
**Symptom:** LibreBooking kann Webhooks nicht senden
**Lösungen:**
1. **WEBHOOK_URL Umgebungsvariable setzen:**
```yaml
environment:
- WEBHOOK_URL=https://ihre-domain.com/
```
2. **Netzwerk-Konfiguration prüfen:**
```bash
docker network inspect $(docker network ls -q)
```
---
## Updates und Wartung
### Node aktualisieren
```bash
# Neue Version herunterladen
cd /pfad/zu/neuem/librebooking_n8n_node
# Custom Nodes ersetzen
rm -rf /pfad/zu/n8n/custom-nodes
cp -r custom-nodes /pfad/zu/n8n/custom-nodes
# Neu bauen
cd /pfad/zu/n8n/custom-nodes
npm install
npm run build
# Container neustarten
cd /pfad/zu/n8n
docker-compose restart n8n
```
### n8n Version aktualisieren
```bash
# Image aktualisieren
docker pull n8nio/n8n:latest
# Container neu erstellen
docker-compose down
docker-compose up -d
```
### Backup erstellen
```bash
# Docker Volumes sichern
docker run --rm \
-v n8n_data:/source:ro \
-v $(pwd)/backup:/backup \
alpine tar cvzf /backup/n8n_data_backup.tar.gz -C /source .
# Custom Nodes sichern
tar cvzf custom-nodes-backup.tar.gz custom-nodes/
```
### Logs rotieren
```yaml
services:
n8n:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
```
---
## Erweiterte Konfiguration
### Kubernetes Deployment
Für Kubernetes-Umgebungen kann ein ConfigMap oder PersistentVolume verwendet werden:
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: librebooking-node
data:
# Node-Dateien als ConfigMap
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: n8n
spec:
template:
spec:
containers:
- name: n8n
volumeMounts:
- name: custom-nodes
mountPath: /home/node/.n8n/custom/n8n-nodes-librebooking
volumes:
- name: custom-nodes
configMap:
name: librebooking-node
```
### Mit Reverse Proxy (Nginx)
Siehe `nginx.conf` für eine vollständige Nginx-Konfiguration mit:
- SSL/TLS Termination
- WebSocket Support
- Optimierte Timeouts für Webhooks
---
## Hilfe und Support
- **GitHub Issues:** [Repository Issues](https://github.com/ihr-repo/librebooking-n8n-node/issues)
- **n8n Community:** [community.n8n.io](https://community.n8n.io)
- **LibreBooking Dokumentation:** [LibreBooking Wiki](https://github.com/effgarces/BookedScheduler/wiki)
---
*Letzte Aktualisierung: Januar 2026*

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"]

553
INSTALLATION.md Normal file
View File

@ -0,0 +1,553 @@
# Installationsanleitung - LibreBooking n8n Node
Diese Anleitung beschreibt alle verfügbaren Methoden zur Installation des LibreBooking n8n Nodes.
## Inhaltsverzeichnis
- [Voraussetzungen](#voraussetzungen)
- [Installation aus Git-Archiv](#installation-aus-git-archiv)
- [Methode 1: Automatische Installation mit Skript](#methode-1-automatische-installation-mit-skript)
- [Methode 2: Manuelle Installation mit npm](#methode-2-manuelle-installation-mit-npm)
- [Methode 3: Installation aus npm Registry](#methode-3-installation-aus-npm-registry)
- [Methode 4: Docker Installation](#methode-4-docker-installation)
- [Methode 5: n8n Community Nodes](#methode-5-n8n-community-nodes)
- [Verifizierung der Installation](#verifizierung-der-installation)
- [Troubleshooting](#troubleshooting)
- [Deinstallation](#deinstallation)
---
## Voraussetzungen
### Systemanforderungen
| Komponente | Mindestversion | Empfohlen |
|------------|---------------|-----------|
| Node.js | 18.x | 20.x LTS |
| npm | 8.x | 10.x |
| n8n | 1.0.0 | Neueste |
### Node.js installieren
**Linux (Ubuntu/Debian):**
```bash
# Mit NodeSource Repository (empfohlen)
curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
sudo apt-get install -y nodejs
# Version prüfen
node --version
npm --version
```
**macOS:**
```bash
# Mit Homebrew
brew install node@20
# Version prüfen
node --version
npm --version
```
**Windows:**
1. Lade Node.js von https://nodejs.org/ herunter
2. Wähle die LTS-Version (20.x)
3. Führe den Installer aus
4. Öffne PowerShell und prüfe: `node --version`
### n8n installieren
```bash
# Global installieren
npm install -g n8n
# Installation prüfen
n8n --version
```
---
## Installation aus Git-Archiv
### Archiv herunterladen
1. **GitHub Release herunterladen:**
```bash
# .tar.gz für Linux/Mac
wget https://github.com/DEIN-REPO/n8n-nodes-librebooking/releases/latest/download/n8n-nodes-librebooking.tar.gz
# .zip für Windows
# Über Browser herunterladen
```
2. **Oder direkt von Git:**
```bash
git clone https://github.com/DEIN-REPO/n8n-nodes-librebooking.git
cd n8n-nodes-librebooking
```
### Archiv entpacken
**Linux/macOS:**
```bash
# .tar.gz entpacken
tar -xzf n8n-nodes-librebooking.tar.gz
cd n8n-nodes-librebooking
```
**Windows (PowerShell):**
```powershell
# .zip entpacken
Expand-Archive -Path n8n-nodes-librebooking.zip -DestinationPath .
cd n8n-nodes-librebooking
```
---
## Methode 1: Automatische Installation mit Skript
Die einfachste Methode für die meisten Benutzer.
### Linux/macOS
```bash
# In das Verzeichnis wechseln
cd n8n-nodes-librebooking
# Skript ausführbar machen (falls nötig)
chmod +x install.sh
# Installation starten
./install.sh
```
**Optionen:**
```bash
./install.sh # Standard-Installation mit npm link
./install.sh --no-link # Nur Build, ohne npm link
./install.sh --global # Globale Installation
./install.sh --help # Hilfe anzeigen
```
### Windows (PowerShell)
```powershell
# In das Verzeichnis wechseln
cd n8n-nodes-librebooking
# Skript ausführen (evtl. Ausführungsrichtlinie anpassen)
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
.\install.ps1
```
**Optionen:**
```powershell
.\install.ps1 # Standard-Installation mit npm link
.\install.ps1 -NoLink # Nur Build, ohne npm link
.\install.ps1 -Global # Globale Installation
.\install.ps1 -Help # Hilfe anzeigen
```
---
## Methode 2: Manuelle Installation mit npm
Für Benutzer, die mehr Kontrolle über den Installationsprozess möchten.
### Schritt 1: Dependencies installieren
```bash
cd n8n-nodes-librebooking
npm install
```
### Schritt 2: TypeScript kompilieren
```bash
npm run build
```
### Schritt 3: Node verlinken
```bash
# Node global verfügbar machen
npm link
# Mit n8n verlinken (optional, falls n8n global installiert ist)
cd $(npm root -g)/n8n
npm link n8n-nodes-librebooking
```
### Schritt 4: n8n neu starten
```bash
# n8n stoppen (falls läuft)
# Ctrl+C oder:
pkill -f n8n
# n8n starten
n8n start
```
---
## Methode 3: Installation aus npm Registry
> **Hinweis:** Diese Methode ist für eine zukünftige Veröffentlichung auf npm vorgesehen.
```bash
# Global installieren
npm install -g n8n-nodes-librebooking
# Oder lokal in einem Projekt
npm install n8n-nodes-librebooking
```
Nach der Veröffentlichung auf npm wird diese Methode die einfachste sein.
---
## Methode 4: Docker Installation
### Voraussetzungen für Docker
- Docker 20.x oder höher
- Docker Compose v2.x (empfohlen)
**Docker installieren:**
- Linux: https://docs.docker.com/engine/install/
- macOS: https://docs.docker.com/desktop/mac/install/
- Windows: https://docs.docker.com/desktop/windows/install/
### Mit docker-compose (empfohlen)
```bash
cd n8n-nodes-librebooking
# Umgebungsvariablen konfigurieren (optional)
cp .env.example .env
# .env Datei bearbeiten
# Container bauen und starten
docker-compose up -d
# Logs anzeigen
docker-compose logs -f
# Status prüfen
docker-compose ps
```
**Umgebungsvariablen (`.env`):**
```env
# n8n Authentifizierung
N8N_BASIC_AUTH_USER=admin
N8N_BASIC_AUTH_PASSWORD=sicheres-passwort-hier
# Webhook-URL (für Produktion)
WEBHOOK_URL=https://n8n.deine-domain.de/
# Zeitzone
TZ=Europe/Berlin
# Log-Level (debug, info, warn, error)
N8N_LOG_LEVEL=info
```
**Nützliche docker-compose Befehle:**
```bash
# Stoppen
docker-compose down
# Neu bauen (nach Änderungen)
docker-compose build --no-cache
# Neustart
docker-compose restart
# Logs eines bestimmten Services
docker-compose logs -f n8n
# In Container Shell
docker-compose exec n8n sh
```
### Mit Docker direkt
```bash
cd n8n-nodes-librebooking
# Image bauen
docker build -t n8n-librebooking .
# Container starten
docker run -d \
--name n8n-librebooking \
-p 5678:5678 \
-e N8N_BASIC_AUTH_ACTIVE=true \
-e N8N_BASIC_AUTH_USER=admin \
-e N8N_BASIC_AUTH_PASSWORD=changeme \
-e GENERIC_TIMEZONE=Europe/Berlin \
-v n8n_data:/home/node/.n8n \
n8n-librebooking
```
**Container verwalten:**
```bash
# Logs anzeigen
docker logs -f n8n-librebooking
# Stoppen
docker stop n8n-librebooking
# Starten
docker start n8n-librebooking
# Entfernen
docker rm -f n8n-librebooking
# Image entfernen
docker rmi n8n-librebooking
```
### Volumes und Konfiguration
**Wichtige Volumes:**
| Volume/Pfad | Beschreibung |
|-------------|--------------|
| `/home/node/.n8n` | n8n Datenverzeichnis (Workflows, Credentials) |
| `/home/node/.n8n/custom` | Custom Nodes |
| `/home/node/workflows` | Beispiel-Workflows (read-only) |
**Daten sichern:**
```bash
# Mit docker-compose
docker-compose exec n8n tar -czf /tmp/backup.tar.gz /home/node/.n8n
docker cp n8n-librebooking:/tmp/backup.tar.gz ./backup.tar.gz
# Ohne docker-compose
docker cp n8n-librebooking:/home/node/.n8n ./n8n-backup
```
**Daten wiederherstellen:**
```bash
docker cp ./n8n-backup/. n8n-librebooking:/home/node/.n8n/
docker-compose restart
```
---
## Methode 5: n8n Community Nodes
> **Hinweis:** Diese Methode wird verfügbar sein, sobald der Node im n8n Community Node Repository veröffentlicht ist.
1. Öffne n8n im Browser
2. Gehe zu **Settings** → **Community Nodes**
3. Klicke auf **Install a community node**
4. Gib ein: `n8n-nodes-librebooking`
5. Klicke auf **Install**
6. Starte n8n neu
---
## Verifizierung der Installation
### 1. n8n starten
```bash
# Lokal
n8n start
# Mit Docker
docker-compose up -d
```
### 2. Browser öffnen
Öffne http://localhost:5678 im Browser.
### 3. Node suchen
1. Erstelle einen neuen Workflow
2. Klicke auf das **+** Symbol
3. Suche nach "LibreBooking"
4. Du solltest zwei Nodes sehen:
- **LibreBooking** (für API-Operationen)
- **LibreBooking Trigger** (für Events)
### 4. Credentials einrichten
1. Klicke auf einen LibreBooking Node
2. Unter "Credentials" klicke auf **Create New**
3. Wähle **LibreBooking API**
4. Fülle aus:
- **URL:** Deine LibreBooking URL (z.B. `https://booking.example.com/Web/Services`)
- **Username:** Dein Admin-Benutzername
- **Password:** Dein Passwort
5. Klicke auf **Save**
6. Teste die Verbindung
---
## Troubleshooting
### Häufige Probleme
#### Node wird nicht angezeigt
**Problem:** Der LibreBooking Node erscheint nicht in n8n.
**Lösungen:**
```bash
# 1. Prüfe, ob der Build erfolgreich war
ls -la dist/
# 2. Prüfe npm link Status
npm ls -g --depth=0 | grep librebooking
# 3. n8n Custom Extensions Pfad prüfen
echo $N8N_CUSTOM_EXTENSIONS
# 4. n8n komplett neu starten
pkill -f n8n
n8n start
```
#### Build-Fehler
**Problem:** `npm run build` schlägt fehl.
**Lösungen:**
```bash
# Node.js Version prüfen
node --version # Sollte >= 18 sein
# node_modules löschen und neu installieren
rm -rf node_modules
npm install
# TypeScript-Fehler anzeigen
npx tsc --noEmit
```
#### npm link Probleme
**Problem:** `npm link` funktioniert nicht.
**Lösungen:**
```bash
# Als Admin/Root ausführen (Linux/Mac)
sudo npm link
# Windows: PowerShell als Administrator starten
# Alternativer Pfad für Custom Nodes
export N8N_CUSTOM_EXTENSIONS=$(pwd)
n8n start
```
#### Docker-Probleme
**Problem:** Container startet nicht.
**Lösungen:**
```bash
# Logs prüfen
docker-compose logs n8n
# Container-Status prüfen
docker-compose ps
# Neu bauen
docker-compose build --no-cache
docker-compose up -d
```
#### Credential-Fehler
**Problem:** "Authentication failed" bei Verbindung.
**Lösungen:**
1. Prüfe LibreBooking URL (inkl. `/Web/Services`)
2. Prüfe Benutzername und Passwort
3. Prüfe ob API in LibreBooking aktiviert ist:
- Admin → Konfiguration → API aktivieren
4. Teste API manuell:
```bash
curl -X POST "https://dein-server/Web/Services/Authentication/Authenticate" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"password"}'
```
### Logs und Debugging
**n8n Logs aktivieren:**
```bash
# Umgebungsvariable setzen
export N8N_LOG_LEVEL=debug
n8n start
# Mit Docker
docker-compose exec n8n sh -c "N8N_LOG_LEVEL=debug n8n start"
```
**Node-spezifische Logs:**
In n8n Workflow-Ausführungen werden Details angezeigt unter "Execution Data".
---
## Deinstallation
### npm link entfernen
```bash
# Im Projektverzeichnis
npm unlink
# Global entfernen
npm unlink -g n8n-nodes-librebooking
```
### Global installiertes Paket entfernen
```bash
npm uninstall -g n8n-nodes-librebooking
```
### Docker entfernen
```bash
# Container und Volumes entfernen
docker-compose down -v
# Images entfernen
docker rmi n8n-librebooking
docker rmi n8nio/n8n
```
### Projektverzeichnis löschen
```bash
# Verzeichnis löschen
rm -rf n8n-nodes-librebooking
# Archiv löschen
rm n8n-nodes-librebooking.tar.gz
rm n8n-nodes-librebooking.zip
```
---
## Support
Bei Fragen oder Problemen:
1. **GitHub Issues:** [Hier Issues erstellen](https://github.com/DEIN-REPO/n8n-nodes-librebooking/issues)
2. **Dokumentation:** Siehe [README.md](README.md)
3. **LibreBooking API:** https://www.bookedscheduler.com/help/api/
---
*Letzte Aktualisierung: Januar 2026*

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.

618
README.md Normal file
View File

@ -0,0 +1,618 @@
# n8n-nodes-librebooking
Ein vollständiger n8n Node für die Integration mit [LibreBooking](https://librebooking.org/) - einer Open-Source Ressourcen- und Raumbuchungslösung.
<!-- Badges -->
[![npm version](https://img.shields.io/npm/v/n8n-nodes-librebooking.svg?style=flat-square)](https://www.npmjs.com/package/n8n-nodes-librebooking)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=flat-square)](https://opensource.org/licenses/MIT)
[![n8n Community Node](https://img.shields.io/badge/n8n-Community%20Node-orange?style=flat-square)](https://n8n.io)
[![LibreBooking](https://img.shields.io/badge/LibreBooking-Integration-blue?style=flat-square)](https://librebooking.org)
[![Node.js Version](https://img.shields.io/badge/node-%3E%3D18-brightgreen?style=flat-square)](https://nodejs.org)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue?style=flat-square)](https://www.typescriptlang.org)
---
## ⚡ Schnellstart
### Option 1: Mit Installations-Skript (empfohlen)
```bash
# Archiv entpacken
tar -xzf n8n-nodes-librebooking.tar.gz
cd n8n-nodes-librebooking
# Installieren (Linux/Mac)
./install.sh
# n8n starten
n8n start
```
### Option 2: Mit Docker
```bash
# Archiv entpacken und starten
tar -xzf n8n-nodes-librebooking.tar.gz
cd n8n-nodes-librebooking
docker-compose up -d
# Browser öffnen: http://localhost:5678
```
### Option 3: Manuell mit npm
```bash
cd n8n-nodes-librebooking
npm install && npm run build && npm link
n8n start
```
📖 **Detaillierte Anleitung:** Siehe [INSTALLATION.md](INSTALLATION.md)
---
## 📋 Inhaltsverzeichnis
- [Schnellstart](#-schnellstart)
- [Funktionen](#-funktionen)
- [Installation](#-installation)
- [Konfiguration](#-konfiguration)
- [Operationen](#-operationen)
- [Beispiele](#-beispiele)
- [Trigger Node](#-trigger-node)
- [Troubleshooting](#-troubleshooting)
- [Entwicklung](#-entwicklung)
- [Lizenz](#-lizenz)
---
## 🚀 Funktionen
### Regular Node (LibreBooking)
- **Reservierungen**: Erstellen, Abrufen, Aktualisieren, Löschen, Genehmigen, Check-In/Check-Out
- **Ressourcen**: Verwalten von Räumen, Equipment und anderen buchbaren Ressourcen
- **Zeitpläne**: Abrufen von Zeitplänen und verfügbaren Slots
- **Benutzer**: Vollständige Benutzerverwaltung (Admin-Rechte erforderlich)
- **Konten**: Eigenes Konto verwalten
- **Gruppen**: Benutzergruppen mit Rollen und Berechtigungen verwalten
- **Zubehör**: Zubehörteile abrufen
- **Attribute**: Benutzerdefinierte Felder verwalten
### Trigger Node (LibreBooking Trigger)
- Polling-basierter Trigger für Reservierungs-Events
- Erkennung neuer Reservierungen
- Erkennung geänderter Reservierungen
- Konfigurierbare Filter (Ressource, Zeitplan, Benutzer)
- Deduplizierung von Events
---
## 📦 Installation
### Über npm (empfohlen)
```bash
npm install n8n-nodes-librebooking
```
### Manuelle Installation
1. Laden Sie das Paket herunter oder klonen Sie das Repository:
```bash
git clone https://github.com/your-org/n8n-nodes-librebooking.git
```
2. Wechseln Sie ins Verzeichnis und installieren Sie die Abhängigkeiten:
```bash
cd n8n-nodes-librebooking
npm install
```
3. Kompilieren Sie das Projekt:
```bash
npm run build
```
4. Verlinken Sie das Paket für lokale Entwicklung:
```bash
npm link
```
5. Verlinken Sie es in Ihrem n8n Custom-Nodes-Verzeichnis:
```bash
cd ~/.n8n/custom
npm link n8n-nodes-librebooking
```
6. Starten Sie n8n neu:
```bash
n8n start
```
### Docker-Installation
Fügen Sie in Ihrem Docker-Compose oder Dockerfile hinzu:
```dockerfile
RUN npm install -g n8n-nodes-librebooking
```
Oder über Umgebungsvariable:
```yaml
environment:
- N8N_CUSTOM_EXTENSIONS=n8n-nodes-librebooking
```
### Integration in bestehende Docker-Installation
Für bestehende n8n Docker-Installationen gibt es mehrere Integrationsmethoden:
#### Schnellste Methode: Automatisches Skript
```bash
# Ins n8n Verzeichnis wechseln
cd /pfad/zu/ihrer/n8n/installation
# Skript ausführen
./install-docker.sh
```
#### Manuelle Methode
```bash
# 1. Custom Nodes kopieren
cp -r custom-nodes /pfad/zu/n8n/
cd /pfad/zu/n8n/custom-nodes && npm install && npm run build
# 2. 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
# 3. Container neustarten
docker-compose restart n8n
```
#### Eigenes Docker-Image bauen
```bash
docker build -f Dockerfile.custom-nodes -t n8n-librebooking .
docker run -d -p 5678:5678 n8n-librebooking
```
📖 **Ausführliche Docker-Anleitung:** Siehe [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md)
🚀 **Docker-Schnellstart:** Siehe [SCHNELLSTART-DOCKER.md](SCHNELLSTART-DOCKER.md)
---
## ⚙️ Konfiguration
### Credentials einrichten
1. Öffnen Sie n8n und gehen Sie zu **Settings** → **Credentials**
2. Klicken Sie auf **Add Credential** und wählen Sie **LibreBooking API**
3. Füllen Sie die folgenden Felder aus:
| Feld | Beschreibung | Beispiel |
|------|-------------|----------|
| **LibreBooking URL** | Die Basis-URL Ihrer LibreBooking-Installation | `https://booking.example.com` |
| **Benutzername** | Ihr LibreBooking-Login (E-Mail oder Benutzername) | `admin@example.com` |
| **Passwort** | Ihr LibreBooking-Passwort | `•••••••••` |
> ⚠️ **Wichtig**: Die URL sollte **ohne** `/Web/Services` angegeben werden!
### API aktivieren
Stellen Sie sicher, dass die API in Ihrer LibreBooking-Installation aktiviert ist:
1. Öffnen Sie die `config.php` Ihrer LibreBooking-Installation
2. Suchen Sie nach `$conf['settings']['api']['enabled']`
3. Setzen Sie den Wert auf `true`
```php
$conf['settings']['api']['enabled'] = 'true';
```
### Credential-Test
Nach dem Speichern der Credentials können Sie diese testen:
- Klicken Sie auf **Test** um die Verbindung zu überprüfen
- Bei erfolgreicher Verbindung wird eine Bestätigung angezeigt
---
## 📖 Operationen
### Reservierungen
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Alle Abrufen** | Liste aller Reservierungen mit optionalen Filtern | ❌ |
| **Abrufen** | Einzelne Reservierung per Referenznummer | ❌ |
| **Erstellen** | Neue Reservierung anlegen | ❌ |
| **Aktualisieren** | Bestehende Reservierung ändern | ❌ |
| **Löschen** | Reservierung entfernen | ❌ |
| **Genehmigen** | Ausstehende Reservierung genehmigen | ❌* |
| **Check-In** | In Reservierung einchecken | ❌ |
| **Check-Out** | Aus Reservierung auschecken | ❌ |
*Erfordert Genehmigungsrechte
#### Filter für "Alle Abrufen"
- `userId` - Nach Benutzer filtern
- `resourceId` - Nach Ressource filtern
- `scheduleId` - Nach Zeitplan filtern
- `startDateTime` - Startzeit (ISO 8601)
- `endDateTime` - Endzeit (ISO 8601)
#### Zusätzliche Felder für "Erstellen/Aktualisieren"
- `description` - Beschreibung
- `participants` - Teilnehmer (Benutzer-IDs)
- `invitees` - Eingeladene (Benutzer-IDs)
- `resources` - Zusätzliche Ressourcen
- `allowParticipation` - Teilnahme erlauben
### Ressourcen
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Alle Abrufen** | Liste aller Ressourcen | ❌ |
| **Abrufen** | Einzelne Ressource per ID | ❌ |
| **Verfügbarkeit Prüfen** | Verfügbarkeit einer/aller Ressourcen | ❌ |
| **Gruppen Abrufen** | Ressourcen-Gruppenstruktur | ❌ |
| **Typen Abrufen** | Verfügbare Ressourcen-Typen | ❌ |
| **Status Abrufen** | Verfügbare Status-Werte | ❌ |
| **Erstellen** | Neue Ressource anlegen | ✅ |
| **Aktualisieren** | Ressource ändern | ✅ |
| **Löschen** | Ressource entfernen | ✅ |
#### Ressourcen-Optionen
- `location` - Standort
- `contact` - Kontaktinformation
- `description` - Beschreibung
- `maxParticipants` - Maximale Teilnehmerzahl
- `requiresApproval` - Genehmigung erforderlich
- `allowMultiday` - Mehrtägige Buchungen
- `requiresCheckIn` - Check-In erforderlich
- `autoReleaseMinutes` - Auto-Release nach X Minuten
- `color` - Anzeigefarbe (Hex)
- `statusId` - Status (0=Versteckt, 1=Verfügbar, 2=Nicht verfügbar)
### Zeitpläne
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Alle Abrufen** | Liste aller Zeitpläne | ❌ |
| **Abrufen** | Einzelner Zeitplan mit Perioden | ❌ |
| **Slots Abrufen** | Verfügbare Slots eines Zeitplans | ❌ |
### Benutzer (Admin)
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Alle Abrufen** | Liste aller Benutzer | ❌ |
| **Abrufen** | Einzelner Benutzer per ID | ❌ |
| **Erstellen** | Neuen Benutzer anlegen | ✅ |
| **Aktualisieren** | Benutzer ändern | ✅ |
| **Passwort Ändern** | Benutzer-Passwort setzen | ✅ |
| **Löschen** | Benutzer entfernen | ✅ |
### Konten
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Abrufen** | Eigene Kontoinformationen | ❌ |
| **Erstellen** | Neues Konto (Registrierung) | ❌ |
| **Aktualisieren** | Eigenes Konto ändern | ❌ |
| **Passwort Ändern** | Eigenes Passwort ändern | ❌ |
### Gruppen
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Alle Abrufen** | Liste aller Gruppen | ❌ |
| **Abrufen** | Einzelne Gruppe | ❌ |
| **Erstellen** | Neue Gruppe anlegen | ✅ |
| **Aktualisieren** | Gruppe ändern | ✅ |
| **Löschen** | Gruppe entfernen | ✅ |
| **Rollen Ändern** | Gruppenrollen setzen | ✅ |
| **Berechtigungen Ändern** | Ressourcen-Berechtigungen | ✅ |
| **Benutzer Ändern** | Gruppenmitglieder | ✅ |
#### Rollen-IDs
- `1` - Gruppenadministrator
- `2` - Anwendungsadministrator
- `3` - Ressourcenadministrator
- `4` - Zeitplanadministrator
### Zubehör
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Alle Abrufen** | Liste aller Zubehörteile | ❌ |
| **Abrufen** | Einzelnes Zubehörteil | ❌ |
### Attribute
| Operation | Beschreibung | Admin-Rechte |
|-----------|-------------|--------------|
| **Abrufen** | Einzelnes Attribut | ❌ |
| **Nach Kategorie Abrufen** | Alle Attribute einer Kategorie | ❌ |
| **Erstellen** | Neues Attribut anlegen | ✅ |
| **Aktualisieren** | Attribut ändern | ✅ |
| **Löschen** | Attribut entfernen | ✅ |
#### Attribut-Kategorien
- `1` - Reservierung
- `2` - Benutzer
- `4` - Ressource
- `5` - Ressourcen-Typ
#### Attribut-Typen
- `1` - Einzeilig (Text)
- `2` - Mehrzeilig (Textarea)
- `3` - Auswahlliste
- `4` - Checkbox
- `5` - Datum/Zeit
---
## 💡 Beispiele
### Beispiel 1: Alle Reservierungen der nächsten Woche abrufen
```json
{
"nodes": [
{
"parameters": {
"resource": "reservation",
"operation": "getAll",
"filters": {
"startDateTime": "={{ $now.toISO() }}",
"endDateTime": "={{ $now.plus({days: 7}).toISO() }}"
}
},
"name": "LibreBooking",
"type": "n8n-nodes-librebooking.libreBooking",
"typeVersion": 1,
"position": [250, 300],
"credentials": {
"libreBookingApi": {
"id": "1",
"name": "LibreBooking"
}
}
}
]
}
```
### Beispiel 2: Neue Reservierung erstellen
```json
{
"parameters": {
"resource": "reservation",
"operation": "create",
"resourceId": 1,
"startDateTime": "2026-01-26T10:00:00",
"endDateTime": "2026-01-26T11:00:00",
"title": "Team Meeting",
"additionalFields": {
"description": "Wöchentliches Team-Meeting",
"participants": "2,3,4"
}
}
}
```
### Beispiel 3: Verfügbarkeit prüfen
```json
{
"parameters": {
"resource": "resource",
"operation": "getAvailability",
"resourceIdOptional": 1,
"availabilityDateTime": "2026-01-26T14:00:00"
}
}
```
### Beispiel 4: Benutzer mit Filter suchen
```json
{
"parameters": {
"resource": "user",
"operation": "getAll",
"userFilters": {
"organization": "Marketing",
"lastName": "Müller"
}
}
}
```
---
## ⚡ Trigger Node
Der **LibreBooking Trigger** ist ein Polling-basierter Trigger, der auf neue oder geänderte Reservierungen reagiert.
### Konfiguration
| Parameter | Beschreibung |
|-----------|-------------|
| **Event** | Art des Events (Neue/Geänderte/Alle Reservierungen) |
| **Filter** | Optional: Ressource, Zeitplan, Benutzer |
| **Zeitfenster** | Überwachungszeitraum (7/14/30/90 Tage) |
| **Detaillierte Daten** | Vollständige Reservierungsdetails abrufen |
### Event-Typen
- **Neue Reservierung**: Wird nur bei neuen Reservierungen ausgelöst
- **Geänderte Reservierung**: Wird bei Änderungen an bestehenden Reservierungen ausgelöst
- **Alle Reservierungen**: Wird bei neuen und geänderten Reservierungen ausgelöst
### Beispiel-Workflow: Benachrichtigung bei neuer Reservierung
```
[LibreBooking Trigger] → [IF: Ressource = 1] → [Slack: Nachricht senden]
```
### Deduplizierung
Der Trigger speichert Informationen über bereits verarbeitete Reservierungen und verhindert so doppelte Ausführungen. Bei Änderungen (Titel, Zeit, etc.) wird eine Reservierung als "geändert" erkannt.
---
## 🔧 Troubleshooting
### Häufige Fehler
#### "Authentifizierung fehlgeschlagen"
- Überprüfen Sie die LibreBooking-URL (ohne `/Web/Services`)
- Stellen Sie sicher, dass Benutzername und Passwort korrekt sind
- Prüfen Sie, ob die API in LibreBooking aktiviert ist
#### "Zugriff verweigert" (403)
- Die Operation erfordert Administrator-Rechte
- Verwenden Sie einen Admin-Account oder wählen Sie eine andere Operation
#### "Nicht gefunden" (404)
- Die angegebene ID (Ressource, Benutzer, etc.) existiert nicht
- Überprüfen Sie die Referenznummer bei Reservierungen
#### "Session abgelaufen"
- Der Session-Token ist abgelaufen
- Führen Sie den Workflow erneut aus
### API-Limitierungen
- LibreBooking hat keine dokumentierten Rate-Limits
- Bei vielen Anfragen empfehlen wir Pausen zwischen den Operationen
- Der Node authentifiziert sich bei jeder Ausführung neu und meldet sich am Ende ab
### Debug-Tipps
1. Aktivieren Sie die n8n-Logs für detaillierte Fehlermeldungen:
```bash
export N8N_LOG_LEVEL=debug
n8n start
```
2. Testen Sie die API direkt im Browser:
```
https://your-librebooking.com/Web/Services/index.php
```
3. Überprüfen Sie die Zeitzonen-Einstellungen in LibreBooking und n8n
---
## 🛠 Entwicklung
### Voraussetzungen
- Node.js 18.17.0 oder höher
- npm 9.x oder höher
- n8n (für Tests)
### Setup
```bash
# Repository klonen
git clone https://github.com/your-org/n8n-nodes-librebooking.git
cd n8n-nodes-librebooking
# Abhängigkeiten installieren
npm install
# TypeScript kompilieren
npm run build
# Für Entwicklung (Watch-Modus)
npm run dev
```
### Projektstruktur
```
n8n-nodes-librebooking/
├── credentials/
│ └── LibreBookingApi.credentials.ts
├── nodes/
│ ├── LibreBooking/
│ │ ├── LibreBooking.node.ts
│ │ └── librebooking.svg
│ └── LibreBookingTrigger/
│ ├── LibreBookingTrigger.node.ts
│ └── librebooking.svg
├── workflows/
│ └── example-workflows.json
├── test/
│ └── test-api.ts
├── package.json
├── tsconfig.json
└── README.md
```
### Tests ausführen
```bash
# API-Test mit echten Credentials
npm test
```
### Linting
```bash
npm run lint
npm run lint:fix
```
### Build für Produktion
```bash
npm run build
```
---
## 📄 Lizenz
MIT License - siehe [LICENSE](LICENSE) Datei
---
## 🤝 Beitragen
Beiträge sind willkommen! Bitte öffnen Sie einen Issue oder Pull Request.
1. Fork des Repositories
2. Feature-Branch erstellen (`git checkout -b feature/AmazingFeature`)
3. Änderungen committen (`git commit -m 'Add AmazingFeature'`)
4. Branch pushen (`git push origin feature/AmazingFeature`)
5. Pull Request öffnen
---
## 📞 Support
- **Issues**: [GitHub Issues](https://github.com/your-org/n8n-nodes-librebooking/issues)
- **LibreBooking Dokumentation**: [https://librebooking.org/docs](https://librebooking.org/docs)
- **n8n Community**: [https://community.n8n.io](https://community.n8n.io)
---
**Erstellt mit ❤️ für die n8n und LibreBooking Community**

103
SCHNELLSTART-DOCKER.md Normal file
View File

@ -0,0 +1,103 @@
# Schnellstart: Docker-Integration
Ultra-kurze Anleitung für erfahrene Docker-Nutzer.
---
## Automatische Installation (Empfohlen)
```bash
# Ins n8n Verzeichnis wechseln
cd /pfad/zu/deiner/n8n/installation
# Skript ausführen
./install-docker.sh
# Oder mit Pfad
./install-docker.sh -p /opt/n8n
```
---
## Manuelle Installation (3 Schritte)
### 1. Custom Nodes kopieren
```bash
cp -r custom-nodes /pfad/zu/n8n/
cd /pfad/zu/n8n/custom-nodes
npm install && npm run build
```
### 2. Override-Datei erstellen
```bash
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
```
### 3. Neustarten
```bash
docker-compose restart n8n
```
---
## Neues Setup mit Docker
```bash
# Beispiel-Konfiguration verwenden
cp docker-compose.example.yml docker-compose.yml
cp .env.docker .env
# .env anpassen, dann starten
docker-compose up -d
```
---
## Eigenes Image bauen
```bash
docker build -f Dockerfile.custom-nodes -t n8n-librebooking .
docker run -d -p 5678:5678 n8n-librebooking
```
---
## Verifizierung
```bash
# Node prüfen
docker exec n8n ls /home/node/.n8n/custom/n8n-nodes-librebooking/dist/
# In n8n: Nach "LibreBooking" suchen
```
---
## Bei Problemen
```bash
# Berechtigungen
sudo chown -R 1000:1000 custom-nodes/
# Logs
docker logs n8n | grep -i error
# Neustart
docker-compose down && docker-compose up -d
```
---
📖 **Ausführliche Anleitung:** [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md)

60
SCHNELLSTART.md Normal file
View File

@ -0,0 +1,60 @@
# Schnellstart - LibreBooking n8n Node
Diese Anleitung ist für erfahrene Benutzer, die schnell loslegen möchten.
## Option A: Mit Skript (empfohlen)
```bash
# Archiv entpacken
tar -xzf n8n-nodes-librebooking.tar.gz
cd n8n-nodes-librebooking
# Installieren
./install.sh
# n8n starten
n8n start
```
## Option B: Mit Docker
```bash
# Archiv entpacken
tar -xzf n8n-nodes-librebooking.tar.gz
cd n8n-nodes-librebooking
# Container starten
docker-compose up -d
# Browser öffnen
open http://localhost:5678
```
## Option C: Manuell
```bash
tar -xzf n8n-nodes-librebooking.tar.gz
cd n8n-nodes-librebooking
npm install
npm run build
npm link
n8n start
```
## Credentials einrichten
1. Öffne http://localhost:5678
2. Erstelle neuen Workflow
3. Füge "LibreBooking" Node hinzu
4. Erstelle neue Credentials:
- **URL:** `https://dein-server/Web/Services`
- **Username:** Admin-Benutzer
- **Password:** Passwort
## Fertig!
Der LibreBooking Node ist jetzt verfügbar.
---
Für detaillierte Anleitungen siehe [INSTALLATION.md](INSTALLATION.md)

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,31 @@
# Docker Compose Override für bestehende n8n Installationen
# Diese Datei erweitert eine bestehende docker-compose.yml um den LibreBooking Node
#
# Verwendung:
# 1. Diese Datei in das Verzeichnis mit der bestehenden docker-compose.yml kopieren
# 2. custom-nodes Verzeichnis in das gleiche Verzeichnis kopieren
# 3. docker-compose up -d ausführen
#
# HINWEIS: Diese Datei wird automatisch mit der bestehenden docker-compose.yml zusammengeführt
version: '3.8'
services:
n8n:
# Zusätzliche Volumes für Custom Nodes
volumes:
# LibreBooking Custom Node - Variante 1: Vorgebauter Node
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
# Alternative: Nur das dist-Verzeichnis (wenn bereits gebaut)
# - ./custom-nodes/dist:/home/node/.n8n/custom/n8n-nodes-librebooking/dist:ro
# - ./custom-nodes/package.json:/home/node/.n8n/custom/n8n-nodes-librebooking/package.json:ro
# Zusätzliche Umgebungsvariablen
environment:
# Pfad für Custom Nodes (normalerweise bereits gesetzt)
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
# Aktiviert erweiterte Node-Typen
- N8N_COMMUNITY_NODES_ENABLED=true
# Optional: Debug-Logging für Node-Entwicklung
# - N8N_LOG_LEVEL=debug

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

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';

349
install-docker.sh Executable file
View File

@ -0,0 +1,349 @@
#!/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
if command -v docker-compose &> /dev/null; then
COMPOSE_CMD="docker-compose"
print_success "docker-compose gefunden: $(docker-compose --version)"
elif docker compose version &> /dev/null; then
COMPOSE_CMD="docker compose"
print_success "docker compose gefunden: $(docker compose version)"
else
print_error "Docker Compose ist nicht installiert!"
echo " Bitte installieren Sie Docker Compose"
exit 1
fi
# 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
# =============================================================================
# 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 ""

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 ""

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

82
package.json Normal file
View File

@ -0,0 +1,82 @@
{
"name": "n8n-nodes-librebooking",
"version": "1.0.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"
},
"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"
}
}

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"
]
}

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}]]
}
}
}
]
}