Initial commit
This commit is contained in:
commit
1af67a06d1
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
node_modules/
|
||||||
|
dist/
|
||||||
|
.env
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Generated PDFs
|
||||||
|
*.pdf
|
||||||
|
|
@ -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/
|
||||||
|
|
@ -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*
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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! 🙏
|
||||||
|
|
@ -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*
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -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"]
|
||||||
|
|
@ -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*
|
||||||
|
|
@ -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.
|
||||||
|
|
@ -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 -->
|
||||||
|
[](https://www.npmjs.com/package/n8n-nodes-librebooking)
|
||||||
|
[](https://opensource.org/licenses/MIT)
|
||||||
|
[](https://n8n.io)
|
||||||
|
[](https://librebooking.org)
|
||||||
|
[](https://nodejs.org)
|
||||||
|
[](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**
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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)
|
||||||
|
|
@ -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}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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}}',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -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
|
|
@ -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 |
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 |
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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';
|
||||||
|
|
@ -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 ""
|
||||||
|
|
@ -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 ""
|
||||||
|
|
@ -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 ""
|
||||||
|
|
@ -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
|
|
@ -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 |
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 |
File diff suppressed because it is too large
Load Diff
|
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
|
@ -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"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -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}]]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue