Probleme mit der Installation in DOcker gelösst
This commit is contained in:
commit
43ee2813a0
File diff suppressed because one or more lines are too long
|
|
@ -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,16 @@
|
||||||
|
# npm Configuration für LibreBooking n8n Node
|
||||||
|
# Diese Datei konfiguriert npm für dieses Projekt
|
||||||
|
|
||||||
|
# Audit-Warnungen deaktivieren
|
||||||
|
# Die Vulnerabilities kommen von n8n-workflow Dependencies und sind
|
||||||
|
# in diesem Kontext nicht kritisch (siehe SECURITY.md)
|
||||||
|
audit=false
|
||||||
|
|
||||||
|
# Fund-Nachrichten deaktivieren
|
||||||
|
fund=false
|
||||||
|
|
||||||
|
# Optional: Legacy Peer Dependencies (für ältere n8n Versionen)
|
||||||
|
# legacy-peer-deps=true
|
||||||
|
|
||||||
|
# Optional: Engine-Strict deaktivieren
|
||||||
|
# engine-strict=false
|
||||||
|
|
@ -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,35 @@
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
Alle wichtigen Änderungen werden hier dokumentiert.
|
||||||
|
|
||||||
|
## [1.1.0] - 2026-01-25
|
||||||
|
|
||||||
|
### Geändert
|
||||||
|
- ⭐ **Vereinfachte Installation**: Fokus auf "auf dem Host bauen"
|
||||||
|
- Aktualisierte Dokumentation mit funktionierender Methode
|
||||||
|
- Neue npm scripts: `docker:deploy`, `docker:copy`, `docker:restart`
|
||||||
|
|
||||||
|
### Hinzugefügt
|
||||||
|
- `quick-install.sh` - Ultra-einfache Installation
|
||||||
|
- `update-node.sh` - Für Updates
|
||||||
|
- `git-commit.sh` - Git Commit Helper
|
||||||
|
- `git-cleanup.sh` - Cleanup alter Dateien
|
||||||
|
- `create-release.sh` - Release-Erstellung
|
||||||
|
- `GIT-COMMANDS.md` - Git-Befehlsreferenz
|
||||||
|
|
||||||
|
### Behoben
|
||||||
|
- TypeScript Installation Problem gelöst ("tsc not found")
|
||||||
|
- Read-only Volume Problem dokumentiert und gelöst
|
||||||
|
- npm audit Vulnerabilities dokumentiert
|
||||||
|
|
||||||
|
## [1.0.0] - 2026-01-24
|
||||||
|
|
||||||
|
### Hinzugefügt
|
||||||
|
- Vollständige LibreBooking API Integration
|
||||||
|
- 8 Ressourcen: Reservierung, Ressource, Zeitplan, Benutzer, Konto, Gruppe, Zubehör, Attribut
|
||||||
|
- Trigger Node für neue/geänderte Reservierungen
|
||||||
|
- Docker Support mit docker-compose.yml
|
||||||
|
- Automatische Installationsskripte
|
||||||
|
- Umfangreiche Dokumentation auf Deutsch
|
||||||
|
- Beispiel-Workflows
|
||||||
|
- Test-Skripte
|
||||||
|
|
@ -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,92 @@
|
||||||
|
# Docker Integration
|
||||||
|
|
||||||
|
## Empfohlene Methode: Auf dem Host bauen
|
||||||
|
|
||||||
|
Die zuverlässigste Methode für Docker-Installationen.
|
||||||
|
|
||||||
|
### Schritt-für-Schritt
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Auf dem Host
|
||||||
|
cd /pfad/zu/n8n-nodes-librebooking
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 2. In Container kopieren
|
||||||
|
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# 3. Neustarten
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mit Skript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./quick-install.sh n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mit npm
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run docker:deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## docker-compose.yml Beispiel
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
n8n:
|
||||||
|
image: n8nio/n8n:latest
|
||||||
|
container_name: n8n
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "5678:5678"
|
||||||
|
environment:
|
||||||
|
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
- N8N_COMMUNITY_NODES_ENABLED=true
|
||||||
|
- TZ=Europe/Berlin
|
||||||
|
volumes:
|
||||||
|
- n8n_data:/home/node/.n8n
|
||||||
|
# Optional: Custom Nodes Verzeichnis
|
||||||
|
# - ./custom-nodes:/home/node/.n8n/custom
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
n8n_data:
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Für Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./update-node.sh n8n
|
||||||
|
|
||||||
|
# Oder
|
||||||
|
npm run docker:deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verifikation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dateien prüfen
|
||||||
|
docker exec n8n ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# Sollte zeigen:
|
||||||
|
# dist/
|
||||||
|
# package.json
|
||||||
|
# node_modules/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Probleme?
|
||||||
|
|
||||||
|
Siehe [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
|
|
@ -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,65 @@
|
||||||
|
# Git-Befehle für LibreBooking n8n Node
|
||||||
|
|
||||||
|
## Schnellbefehle
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alle Änderungen committen
|
||||||
|
git add .
|
||||||
|
git commit -m "fix: Vereinfachte Installation"
|
||||||
|
|
||||||
|
# Push
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
## Release erstellen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Version in package.json anpassen (z.B. 1.1.0)
|
||||||
|
|
||||||
|
# 2. Alte Archive löschen
|
||||||
|
rm ../n8n-nodes-librebooking*.tar.gz
|
||||||
|
rm ../n8n-nodes-librebooking*.zip
|
||||||
|
|
||||||
|
# 3. Neue Archive erstellen
|
||||||
|
git archive --format=tar.gz --prefix=n8n-nodes-librebooking/ --output=../n8n-nodes-librebooking-v1.1.0.tar.gz HEAD
|
||||||
|
git archive --format=zip --prefix=n8n-nodes-librebooking/ --output=../n8n-nodes-librebooking-v1.1.0.zip HEAD
|
||||||
|
|
||||||
|
# 4. Tag erstellen
|
||||||
|
git tag -a v1.1.0 -m "Version 1.1.0 - Vereinfachte Installation"
|
||||||
|
|
||||||
|
# 5. Push
|
||||||
|
git push origin main
|
||||||
|
git push origin v1.1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mit Skripten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Commit
|
||||||
|
./git-commit.sh "fix: Beschreibung der Änderung"
|
||||||
|
|
||||||
|
# Cleanup
|
||||||
|
./git-cleanup.sh
|
||||||
|
|
||||||
|
# Release
|
||||||
|
./create-release.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Nützliche Befehle
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Status anzeigen
|
||||||
|
git status
|
||||||
|
|
||||||
|
# Änderungen anzeigen
|
||||||
|
git diff
|
||||||
|
|
||||||
|
# Log anzeigen
|
||||||
|
git log --oneline -10
|
||||||
|
|
||||||
|
# Tags anzeigen
|
||||||
|
git tag -l
|
||||||
|
|
||||||
|
# Remote anzeigen
|
||||||
|
git remote -v
|
||||||
|
```
|
||||||
|
|
@ -0,0 +1,101 @@
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
## Methode 1: Auf dem Host bauen (EMPFOHLEN) ⭐
|
||||||
|
|
||||||
|
Die zuverlässigste Methode für Docker-Installationen.
|
||||||
|
|
||||||
|
### Voraussetzungen
|
||||||
|
|
||||||
|
- Node.js 18+
|
||||||
|
- npm
|
||||||
|
- Docker
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Klonen
|
||||||
|
git clone https://github.com/your-org/n8n-nodes-librebooking.git
|
||||||
|
cd n8n-nodes-librebooking
|
||||||
|
|
||||||
|
# 2. Dependencies installieren
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# 3. Bauen
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 4. In Container kopieren
|
||||||
|
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# 5. Container neustarten
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mit Skript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./quick-install.sh n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mit npm scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run docker:deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Methode 2: Docker mit vorgebautem dist/
|
||||||
|
|
||||||
|
Für Read-only Volumes.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Bauen
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# 2. Kopieren
|
||||||
|
cp -r dist package.json node_modules /pfad/zu/custom-nodes/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# 3. docker-compose.yml
|
||||||
|
volumes:
|
||||||
|
- ./custom-nodes:/home/node/.n8n/custom:ro # Read-only möglich!
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Methode 3: Native Installation (ohne Docker)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Installieren
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
npm link
|
||||||
|
|
||||||
|
# 2. n8n starten
|
||||||
|
n8n start
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verifikation
|
||||||
|
|
||||||
|
Nach der Installation:
|
||||||
|
|
||||||
|
1. Öffne n8n: http://localhost:5678
|
||||||
|
2. Erstelle neuen Workflow
|
||||||
|
3. Suche nach "LibreBooking"
|
||||||
|
4. Wenn der Node erscheint → ✅ Installation erfolgreich
|
||||||
|
|
||||||
|
## Deinstallation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Docker
|
||||||
|
docker exec n8n rm -rf /home/node/.n8n/custom/n8n-nodes-librebooking
|
||||||
|
docker restart n8n
|
||||||
|
|
||||||
|
# Native
|
||||||
|
npm unlink -g n8n-nodes-librebooking
|
||||||
|
```
|
||||||
|
|
@ -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,328 @@
|
||||||
|
# Manuelle Installation im Docker Container
|
||||||
|
|
||||||
|
Diese Anleitung beschreibt, wie Sie den LibreBooking Node manuell im Docker Container installieren, wenn die Dateien bereits kopiert wurden, aber der Node nicht in n8n erscheint.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Situation
|
||||||
|
|
||||||
|
Sie haben die Dateien in den Container kopiert (z.B. nach `/opt/n8n/custom-nodes` oder `/home/node/.n8n/custom/n8n-nodes-librebooking`), aber:
|
||||||
|
|
||||||
|
- Der LibreBooking Node erscheint nicht in der n8n Node-Suche
|
||||||
|
- Die TypeScript-Dateien wurden nicht kompiliert
|
||||||
|
- Das `dist/` Verzeichnis fehlt oder ist leer
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Diagnose
|
||||||
|
|
||||||
|
### Schnell-Check mit einem Befehl
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Prüft Dateien und Status im Container
|
||||||
|
docker exec n8n sh -c "ls -la /home/node/.n8n/custom/*/dist/ 2>/dev/null || echo 'dist/ nicht gefunden'"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Ausführlicher Check mit Skript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check-Skript in Container kopieren und ausführen
|
||||||
|
docker cp check-installation.sh n8n:/tmp/
|
||||||
|
docker exec n8n sh /tmp/check-installation.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manuell im Container prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# In den Container einloggen
|
||||||
|
docker exec -it n8n sh
|
||||||
|
|
||||||
|
# Prüfen, was vorhanden ist
|
||||||
|
ls -la /home/node/.n8n/custom/
|
||||||
|
ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# Umgebungsvariablen prüfen
|
||||||
|
env | grep N8N
|
||||||
|
|
||||||
|
# Container verlassen
|
||||||
|
exit
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lösung 1: Automatisch mit Skript (empfohlen)
|
||||||
|
|
||||||
|
Das einfachste ist das All-in-One-Skript:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auf dem Host ausführen
|
||||||
|
./fix-node-installation.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Oder mit spezifischem Container-Namen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./fix-node-installation.sh -c mein-n8n-container
|
||||||
|
```
|
||||||
|
|
||||||
|
### Alternative: Nur das Installations-Skript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Skript in Container kopieren
|
||||||
|
docker cp install-in-container.sh n8n:/tmp/
|
||||||
|
|
||||||
|
# Skript im Container ausführen
|
||||||
|
docker exec -it n8n sh /tmp/install-in-container.sh
|
||||||
|
|
||||||
|
# Container neustarten
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lösung 2: Manuelle Schritte im Container
|
||||||
|
|
||||||
|
### Schritt 1: In den Container einloggen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec -it n8n sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 2: Zum Custom-Node-Verzeichnis wechseln
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Standard-Pfad (offizielles n8n Image)
|
||||||
|
cd /home/node/.n8n/custom/n8n-nodes-librebooking
|
||||||
|
|
||||||
|
# Oder falls an anderem Ort:
|
||||||
|
cd /opt/n8n/custom-nodes
|
||||||
|
# oder
|
||||||
|
cd /data/custom-nodes
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 3: Prüfen ob Dateien vorhanden sind
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -la
|
||||||
|
# Sollte zeigen: package.json, tsconfig.json, nodes/, credentials/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 4: Dependencies installieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartete Ausgabe:
|
||||||
|
```
|
||||||
|
added 50 packages in 10s
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 5: Node kompilieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
Erwartete Ausgabe:
|
||||||
|
```
|
||||||
|
> n8n-nodes-librebooking@1.0.0 build
|
||||||
|
> tsc && npm run copy:icons
|
||||||
|
```
|
||||||
|
|
||||||
|
### Schritt 6: Prüfen ob Build erfolgreich war
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls -la dist/
|
||||||
|
ls -la dist/nodes/LibreBooking/
|
||||||
|
```
|
||||||
|
|
||||||
|
Sollte zeigen:
|
||||||
|
- `dist/nodes/LibreBooking/LibreBooking.node.js`
|
||||||
|
- `dist/nodes/LibreBookingTrigger/LibreBookingTrigger.node.js`
|
||||||
|
- `dist/credentials/LibreBookingApi.credentials.js`
|
||||||
|
|
||||||
|
### Schritt 7: Container verlassen und neustarten
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Container verlassen
|
||||||
|
exit
|
||||||
|
|
||||||
|
# Container neustarten (auf dem Host)
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Lösung 3: Direkt mit docker exec (Ein-Befehl-Lösung)
|
||||||
|
|
||||||
|
Wenn Sie schnell zum Ziel kommen wollen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Alles in einem Befehl
|
||||||
|
docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"
|
||||||
|
|
||||||
|
# Container neustarten
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
Für anderen Pfad:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec n8n sh -c "cd /opt/n8n/custom-nodes && npm install && npm run build"
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Verifizierung
|
||||||
|
|
||||||
|
### 1. Logs prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Nach LibreBooking in den Logs suchen
|
||||||
|
docker logs n8n 2>&1 | grep -i "librebooking\|custom\|node"
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. In n8n prüfen
|
||||||
|
|
||||||
|
1. Öffnen Sie n8n im Browser (z.B. `http://localhost:5678`)
|
||||||
|
2. Erstellen Sie einen neuen Workflow oder öffnen Sie einen bestehenden
|
||||||
|
3. Klicken Sie auf `+` um einen neuen Node hinzuzufügen
|
||||||
|
4. Suchen Sie nach "LibreBooking"
|
||||||
|
5. Es sollten zwei Nodes erscheinen:
|
||||||
|
- **LibreBooking** - Hauptnode für alle Operationen
|
||||||
|
- **LibreBooking Trigger** - Trigger für Reservierungen
|
||||||
|
|
||||||
|
### 3. Node-Dateien im Container prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Prüfe ob .node.js Dateien existieren
|
||||||
|
docker exec n8n find /home/node/.n8n/custom -name "*.node.js" 2>/dev/null
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Problem: npm nicht gefunden
|
||||||
|
|
||||||
|
**Symptom:**
|
||||||
|
```
|
||||||
|
sh: npm: not found
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
Das verwendete Docker-Image enthält kein npm. Verwenden Sie ein Image mit Node.js:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Prüfen Sie das Image
|
||||||
|
docker inspect n8n --format='{{.Config.Image}}'
|
||||||
|
|
||||||
|
# Das offizielle n8n Image (n8nio/n8n) enthält npm
|
||||||
|
# Falls Sie ein minimales Image verwenden, wechseln Sie zu n8nio/n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Permission denied
|
||||||
|
|
||||||
|
**Symptom:**
|
||||||
|
```
|
||||||
|
EACCES: permission denied
|
||||||
|
npm ERR! could not create a lockfile
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
n8n läuft als User `node` (UID 1000). Setzen Sie die Berechtigungen:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auf dem Host (vor dem Kopieren)
|
||||||
|
sudo chown -R 1000:1000 custom-nodes/
|
||||||
|
|
||||||
|
# Oder im Container als root
|
||||||
|
docker exec -u root n8n chown -R node:node /home/node/.n8n/custom/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Build schlägt fehl
|
||||||
|
|
||||||
|
**Symptom:**
|
||||||
|
```
|
||||||
|
error TS2307: Cannot find module 'n8n-workflow'
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
```bash
|
||||||
|
# Im Container
|
||||||
|
cd /home/node/.n8n/custom/n8n-nodes-librebooking
|
||||||
|
rm -rf node_modules package-lock.json
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Node erscheint nach Neustart immer noch nicht
|
||||||
|
|
||||||
|
**Mögliche Ursachen:**
|
||||||
|
|
||||||
|
1. **Falscher Pfad**: N8N_CUSTOM_EXTENSIONS zeigt nicht auf das richtige Verzeichnis
|
||||||
|
```bash
|
||||||
|
docker exec n8n env | grep N8N_CUSTOM
|
||||||
|
# Sollte zeigen: N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **package.json n8n-Sektion fehlt**: Die `n8n` Konfiguration in package.json muss korrekt sein
|
||||||
|
```bash
|
||||||
|
docker exec n8n cat /home/node/.n8n/custom/n8n-nodes-librebooking/package.json | grep -A10 '"n8n"'
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Container-Cache**: Container komplett neu erstellen
|
||||||
|
```bash
|
||||||
|
docker compose down
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **n8n Version zu alt**: Der Node benötigt n8n >= 1.0.0
|
||||||
|
```bash
|
||||||
|
docker exec n8n n8n --version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Problem: Fehler bei npm install (Netzwerk)
|
||||||
|
|
||||||
|
**Symptom:**
|
||||||
|
```
|
||||||
|
npm ERR! network request failed
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
```bash
|
||||||
|
# DNS prüfen
|
||||||
|
docker exec n8n cat /etc/resolv.conf
|
||||||
|
|
||||||
|
# npm Registry prüfen
|
||||||
|
docker exec n8n npm config get registry
|
||||||
|
|
||||||
|
# Ggf. Registry setzen
|
||||||
|
docker exec n8n npm config set registry https://registry.npmjs.org/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Schnellreferenz
|
||||||
|
|
||||||
|
| Aktion | Befehl |
|
||||||
|
|--------|--------|
|
||||||
|
| Status prüfen | `docker exec n8n sh /tmp/check-installation.sh` |
|
||||||
|
| Installieren | `docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"` |
|
||||||
|
| Neustarten | `docker restart n8n` |
|
||||||
|
| Logs prüfen | `docker logs n8n 2>&1 \| grep -i libre` |
|
||||||
|
| Im Container | `docker exec -it n8n sh` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Weiterführende Dokumentation
|
||||||
|
|
||||||
|
- [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md) - Vollständige Docker-Anleitung
|
||||||
|
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Alle Troubleshooting-Themen
|
||||||
|
- [SCHNELLSTART-DOCKER.md](SCHNELLSTART-DOCKER.md) - Docker-Schnellstart
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Letzte Aktualisierung: Januar 2026*
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
# LibreBooking n8n Node
|
||||||
|
|
||||||
|
Integration von LibreBooking in n8n für automatisierte Reservierungs- und Ressourcenverwaltung.
|
||||||
|
|
||||||
|
## ⚡ Schnellstart (EMPFOHLEN)
|
||||||
|
|
||||||
|
**Die einfachste Methode: Auf dem Host bauen, in Docker kopieren**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Repository klonen
|
||||||
|
git clone https://github.com/your-org/n8n-nodes-librebooking.git
|
||||||
|
cd n8n-nodes-librebooking
|
||||||
|
|
||||||
|
# 2. Bauen und installieren
|
||||||
|
./quick-install.sh n8n
|
||||||
|
|
||||||
|
# Fertig! ✔
|
||||||
|
```
|
||||||
|
|
||||||
|
**Oder manuell:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Dependencies & Build
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# In Container kopieren
|
||||||
|
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# Container neustarten
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🛠️ npm Scripts
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run build # Baut den Node
|
||||||
|
npm run docker:deploy # Baut, kopiert & startet Container neu
|
||||||
|
npm run docker:copy # Kopiert in Container
|
||||||
|
npm run docker:restart # Startet Container neu
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📚 Dokumentation
|
||||||
|
|
||||||
|
- **[INSTALLATION.md](INSTALLATION.md)** - Alle Installationsmethoden
|
||||||
|
- **[SCHNELLSTART.md](SCHNELLSTART.md)** - Ultra-kurze Anleitung
|
||||||
|
- **[TROUBLESHOOTING.md](TROUBLESHOOTING.md)** - Problemlösung
|
||||||
|
- **[DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md)** - Docker-spezifische Anleitung
|
||||||
|
|
||||||
|
## 🔑 Credentials einrichten
|
||||||
|
|
||||||
|
1. Öffne n8n: http://localhost:5678
|
||||||
|
2. Gehe zu: **Einstellungen** → **Credentials** → **Add Credential**
|
||||||
|
3. Suche: **LibreBooking API**
|
||||||
|
4. Eingabe:
|
||||||
|
- **URL**: `https://deine-librebooking-url.de`
|
||||||
|
- **Benutzername**: Admin-Benutzer
|
||||||
|
- **Passwort**: Passwort
|
||||||
|
|
||||||
|
## 🌟 Features
|
||||||
|
|
||||||
|
### LibreBooking Node
|
||||||
|
- Reservierungen erstellen, bearbeiten, löschen
|
||||||
|
- Ressourcen und Verfügbarkeit verwalten
|
||||||
|
- Benutzer und Gruppen administrieren
|
||||||
|
- Zeitpläne und Zubehör konfigurieren
|
||||||
|
|
||||||
|
### LibreBooking Trigger Node
|
||||||
|
- Neue Reservierungen überwachen
|
||||||
|
- Geänderte Reservierungen erfassen
|
||||||
|
- Filter nach Ressource/Zeitplan/Benutzer
|
||||||
|
|
||||||
|
## 🔄 Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Nach Änderungen oder git pull
|
||||||
|
./update-node.sh n8n
|
||||||
|
|
||||||
|
# Oder mit npm
|
||||||
|
npm run docker:deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
## ❓ Problemlösung
|
||||||
|
|
||||||
|
### tsc not found?
|
||||||
|
→ **Lösung**: Auf dem Host bauen (siehe Schnellstart)
|
||||||
|
|
||||||
|
### Read-only Volume?
|
||||||
|
→ **Lösung**: dist/ in Container kopieren statt npm im Container
|
||||||
|
|
||||||
|
### npm audit Vulnerabilities?
|
||||||
|
→ Sind non-critical Dependencies von n8n-workflow. Siehe [SECURITY.md](SECURITY.md)
|
||||||
|
|
||||||
|
## 📄 Lizenz
|
||||||
|
|
||||||
|
MIT - Siehe [LICENSE](LICENSE)
|
||||||
|
|
||||||
|
## 🤝 Beitragen
|
||||||
|
|
||||||
|
Beiträge sind willkommen! Siehe [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||||
|
|
@ -0,0 +1,147 @@
|
||||||
|
# Docker Schnellstart - LibreBooking n8n Node
|
||||||
|
|
||||||
|
Schnelle Befehle für erfahrene Docker-Benutzer.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Neue Installation
|
||||||
|
|
||||||
|
### Option A: Mit docker-compose (empfohlen)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Starten
|
||||||
|
cd /pfad/zu/librebooking_n8n_node
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 2. Browser öffnen
|
||||||
|
open http://localhost:5678
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option B: In bestehende n8n Installation integrieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Custom Nodes kopieren
|
||||||
|
cp -r custom-nodes /pfad/zu/n8n/
|
||||||
|
|
||||||
|
# 2. Bauen
|
||||||
|
cd /pfad/zu/n8n/custom-nodes
|
||||||
|
npm install && npm run build
|
||||||
|
|
||||||
|
# 3. docker-compose.override.yml erstellen
|
||||||
|
cat > docker-compose.override.yml << 'EOF'
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
n8n:
|
||||||
|
volumes:
|
||||||
|
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
|
||||||
|
environment:
|
||||||
|
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
- N8N_COMMUNITY_NODES_ENABLED=true
|
||||||
|
EOF
|
||||||
|
|
||||||
|
# 4. Neustarten
|
||||||
|
docker compose restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Node im Container bauen/reparieren
|
||||||
|
|
||||||
|
### Quick-Fix (Ein Befehl)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build" && docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Mit Auto-Fix Skript
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./fix-node-installation.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status prüfen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check-Skript ausführen
|
||||||
|
docker cp check-installation.sh n8n:/tmp/
|
||||||
|
docker exec n8n sh /tmp/check-installation.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Häufige Befehle
|
||||||
|
|
||||||
|
| Aktion | Befehl |
|
||||||
|
|--------|--------|
|
||||||
|
| Container starten | `docker compose up -d` |
|
||||||
|
| Container stoppen | `docker compose down` |
|
||||||
|
| Container neustarten | `docker restart n8n` |
|
||||||
|
| Logs anzeigen | `docker logs -f n8n` |
|
||||||
|
| In Container einloggen | `docker exec -it n8n sh` |
|
||||||
|
| Node bauen | `docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"` |
|
||||||
|
| Status prüfen | `docker exec n8n sh /tmp/check-installation.sh` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pfade im Container
|
||||||
|
|
||||||
|
| Beschreibung | Pfad |
|
||||||
|
|--------------|------|
|
||||||
|
| n8n Home | `/home/node/.n8n` |
|
||||||
|
| Custom Nodes | `/home/node/.n8n/custom` |
|
||||||
|
| LibreBooking Node | `/home/node/.n8n/custom/n8n-nodes-librebooking` |
|
||||||
|
| Daten-Verzeichnis | `/home/node/.n8n` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Umgebungsvariablen
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
- N8N_COMMUNITY_NODES_ENABLED=true
|
||||||
|
- N8N_LOG_LEVEL=info
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Node erscheint nicht
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Build prüfen
|
||||||
|
docker exec n8n ls /home/node/.n8n/custom/n8n-nodes-librebooking/dist/
|
||||||
|
|
||||||
|
# 2. Falls leer - neu bauen
|
||||||
|
docker exec n8n sh -c "cd /home/node/.n8n/custom/n8n-nodes-librebooking && npm install && npm run build"
|
||||||
|
|
||||||
|
# 3. Neustarten
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission denied
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo chown -R 1000:1000 custom-nodes/
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker-compose: distutils Fehler (Python 3.12)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Lösung: Docker Compose v2 verwenden
|
||||||
|
sudo apt-get install docker-compose-plugin
|
||||||
|
docker compose up -d # Beachte: ohne Bindestrich
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Detaillierte Anleitungen
|
||||||
|
|
||||||
|
- [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md) - Vollständige Docker-Dokumentation
|
||||||
|
- [MANUELLE-INSTALLATION-CONTAINER.md](MANUELLE-INSTALLATION-CONTAINER.md) - Manuelle Installation
|
||||||
|
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - Alle Probleme und Lösungen
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Schnellstart-Guide für Docker-Profis*
|
||||||
|
|
@ -0,0 +1,40 @@
|
||||||
|
# Schnellstart
|
||||||
|
|
||||||
|
## ⚡ Installation in 4 Zeilen
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /pfad/zu/n8n-nodes-librebooking
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
./quick-install.sh n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
**Oder mit npm scripts:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install
|
||||||
|
npm run docker:deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔑 Credentials
|
||||||
|
|
||||||
|
n8n → Einstellungen → Credentials → Add → "LibreBooking API"
|
||||||
|
|
||||||
|
| Feld | Wert |
|
||||||
|
|------|------|
|
||||||
|
| URL | `https://deine-librebooking-url.de` |
|
||||||
|
| Benutzername | Admin-Benutzer |
|
||||||
|
| Passwort | Passwort |
|
||||||
|
|
||||||
|
## 🔄 Updates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git pull
|
||||||
|
./update-node.sh n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
## ❓ Probleme?
|
||||||
|
|
||||||
|
- **tsc not found**: Auf dem Host bauen (npm install && npm run build)
|
||||||
|
- **Read-only Volume**: `docker cp` verwenden
|
||||||
|
- Mehr: [TROUBLESHOOTING.md](TROUBLESHOOTING.md)
|
||||||
|
|
@ -0,0 +1,234 @@
|
||||||
|
# Sicherheitshinweise - LibreBooking n8n Node
|
||||||
|
|
||||||
|
Dieses Dokument erklärt die npm audit Vulnerabilities und wie man damit umgeht.
|
||||||
|
|
||||||
|
## Inhaltsverzeichnis
|
||||||
|
|
||||||
|
- [Übersicht der Vulnerabilities](#übersicht-der-vulnerabilities)
|
||||||
|
- [Warum diese Vulnerabilities existieren](#warum-diese-vulnerabilities-existieren)
|
||||||
|
- [Risikoeinschätzung](#risikoeinschätzung)
|
||||||
|
- [Empfehlungen](#empfehlungen)
|
||||||
|
- [Wie man sie beheben kann](#wie-man-sie-beheben-kann)
|
||||||
|
- [Produktionsumgebungen](#produktionsumgebungen)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Übersicht der Vulnerabilities
|
||||||
|
|
||||||
|
Beim Ausführen von `npm audit` werden möglicherweise folgende Vulnerabilities angezeigt:
|
||||||
|
|
||||||
|
### Critical: form-data
|
||||||
|
|
||||||
|
```
|
||||||
|
form-data <4.0.1
|
||||||
|
Severity: critical
|
||||||
|
Prototype Pollution in form-data
|
||||||
|
https://github.com/advisories/GHSA-xxx
|
||||||
|
```
|
||||||
|
|
||||||
|
### Moderate: lodash
|
||||||
|
|
||||||
|
```
|
||||||
|
lodash <4.17.21
|
||||||
|
Severity: moderate
|
||||||
|
Prototype Pollution in lodash
|
||||||
|
https://github.com/advisories/GHSA-xxx
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Warum diese Vulnerabilities existieren
|
||||||
|
|
||||||
|
Diese Vulnerabilities kommen **nicht direkt aus diesem Projekt**, sondern sind **transitive Dependencies** von `n8n-workflow` und `n8n-core`.
|
||||||
|
|
||||||
|
### Dependency-Kette:
|
||||||
|
|
||||||
|
```
|
||||||
|
n8n-nodes-librebooking
|
||||||
|
└── n8n-workflow (devDependency für Typen)
|
||||||
|
└── axios
|
||||||
|
└── form-data (vulnerable version)
|
||||||
|
└── lodash (vulnerable version)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wichtig zu verstehen:
|
||||||
|
|
||||||
|
1. **n8n-workflow** ist nur als `devDependency` und `peerDependency` deklariert
|
||||||
|
2. In Produktion verwendet n8n seine **eigene** n8n-workflow Version
|
||||||
|
3. Die vulnerable Dependencies werden nur beim **Entwickeln** installiert
|
||||||
|
4. Diese Package werden **nicht** in das finale dist/ Verzeichnis gebündelt
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Risikoeinschätzung
|
||||||
|
|
||||||
|
### Für dieses Projekt: **NIEDRIGES RISIKO**
|
||||||
|
|
||||||
|
| Aspekt | Risiko | Begründung |
|
||||||
|
|--------|--------|------------|
|
||||||
|
| Entwicklung | Niedrig | form-data/lodash werden nicht direkt verwendet |
|
||||||
|
| Produktion | Sehr niedrig | Keine transtiven Dependencies werden deployed |
|
||||||
|
| n8n Runtime | Abhängig von n8n | n8n selbst muss die Vulnerabilities beheben |
|
||||||
|
|
||||||
|
### Warum niedriges Risiko:
|
||||||
|
|
||||||
|
1. **form-data Vulnerability:**
|
||||||
|
- Betrifft nur das Parsen von multipart/form-data
|
||||||
|
- Dieser Node verwendet keine File-Uploads über form-data
|
||||||
|
- Die LibreBooking API verwendet JSON für alle Requests
|
||||||
|
|
||||||
|
2. **lodash Vulnerability:**
|
||||||
|
- Betrifft `_.set()` und `_.setWith()` Funktionen
|
||||||
|
- Dieser Node verwendet keine direkten lodash Aufrufe
|
||||||
|
- Die Vulnerability erfordert Angreifer-kontrollierten Input
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Empfehlungen
|
||||||
|
|
||||||
|
### Für Entwickler:
|
||||||
|
|
||||||
|
1. **Warnungen ignorieren** (wenn nicht kritisch):
|
||||||
|
```bash
|
||||||
|
npm install --ignore-scripts
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Audit bei npm install deaktivieren:**
|
||||||
|
```bash
|
||||||
|
# Einmalig:
|
||||||
|
npm install --no-audit
|
||||||
|
|
||||||
|
# Permanent via .npmrc:
|
||||||
|
echo "audit=false" >> .npmrc
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Overrides verwenden** (in package.json):
|
||||||
|
```json
|
||||||
|
"overrides": {
|
||||||
|
"form-data": "^4.0.1",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Für Produktionsumgebungen:
|
||||||
|
|
||||||
|
1. **n8n aktuell halten:** Die n8n-Entwickler aktualisieren regelmäßig ihre Dependencies
|
||||||
|
2. **Nur vertrauenswürdige Inputs:** Keine ungeprüften Daten an die Nodes übergeben
|
||||||
|
3. **Network Isolation:** n8n Container im isolierten Netzwerk betreiben
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Wie man sie beheben kann
|
||||||
|
|
||||||
|
### Option 1: Overrides in package.json (empfohlen)
|
||||||
|
|
||||||
|
Die package.json enthält bereits Overrides für bekannte Vulnerabilities:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"overrides": {
|
||||||
|
"form-data": "^4.0.1",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: npm audit fix (begrenzt)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Automatische Fixes (nur kompatible Updates)
|
||||||
|
npm audit fix
|
||||||
|
|
||||||
|
# Force Fixes (VORSICHT: kann Breaking Changes einführen)
|
||||||
|
npm audit fix --force
|
||||||
|
```
|
||||||
|
|
||||||
|
**Hinweis:** `npm audit fix` kann transitive Dependencies nur begrenzt beheben.
|
||||||
|
|
||||||
|
### Option 3: Update-Skript verwenden
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./update-dependencies.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Das Skript:
|
||||||
|
- Führt `npm audit fix` aus
|
||||||
|
- Aktualisiert alle Dependencies
|
||||||
|
- Testet ob alles funktioniert
|
||||||
|
- Gibt einen Report
|
||||||
|
|
||||||
|
### Option 4: Resolutions (für yarn/pnpm)
|
||||||
|
|
||||||
|
Wenn Sie yarn statt npm verwenden:
|
||||||
|
|
||||||
|
```json
|
||||||
|
"resolutions": {
|
||||||
|
"form-data": "^4.0.1",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Produktionsumgebungen
|
||||||
|
|
||||||
|
### Best Practices:
|
||||||
|
|
||||||
|
1. **Docker Image aktuell halten:**
|
||||||
|
```bash
|
||||||
|
docker pull n8nio/n8n:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Regelmäßige Updates:**
|
||||||
|
```bash
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Security Scanning:**
|
||||||
|
```bash
|
||||||
|
# Image auf Vulnerabilities prüfen
|
||||||
|
docker scan n8nio/n8n:latest
|
||||||
|
# Oder mit Trivy:
|
||||||
|
trivy image n8nio/n8n:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **Netzwerk-Isolation:**
|
||||||
|
- n8n nicht direkt im Internet exponieren
|
||||||
|
- Reverse Proxy mit TLS verwenden
|
||||||
|
- Firewall-Regeln setzen
|
||||||
|
|
||||||
|
5. **Zugriffskontrollen:**
|
||||||
|
- Starke Passwörter verwenden
|
||||||
|
- Basic Auth oder OAuth aktivieren
|
||||||
|
- API-Keys für LibreBooking sicher speichern
|
||||||
|
|
||||||
|
### Sicherheits-Checkliste:
|
||||||
|
|
||||||
|
- [ ] n8n Version aktuell?
|
||||||
|
- [ ] Docker Image aktuell?
|
||||||
|
- [ ] TLS/HTTPS aktiviert?
|
||||||
|
- [ ] Starke Passwörter?
|
||||||
|
- [ ] Netzwerk isoliert?
|
||||||
|
- [ ] Regelmäßige Backups?
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Weiterführende Links
|
||||||
|
|
||||||
|
- [n8n Security Best Practices](https://docs.n8n.io/hosting/security/)
|
||||||
|
- [npm audit Documentation](https://docs.npmjs.com/cli/v8/commands/npm-audit)
|
||||||
|
- [OWASP Dependency Check](https://owasp.org/www-project-dependency-check/)
|
||||||
|
- [Snyk Vulnerability Database](https://snyk.io/vuln/)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Meldung von Sicherheitsproblemen
|
||||||
|
|
||||||
|
Wenn Sie eine Sicherheitslücke **direkt in diesem Projekt** (nicht in Dependencies) finden:
|
||||||
|
|
||||||
|
1. **Nicht öffentlich melden** (kein GitHub Issue)
|
||||||
|
2. Kontaktieren Sie uns direkt per E-Mail
|
||||||
|
3. Geben Sie Zeit für einen Fix bevor öffentliche Disclosure
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Letzte Aktualisierung: Januar 2026*
|
||||||
|
|
@ -0,0 +1,141 @@
|
||||||
|
# Troubleshooting
|
||||||
|
|
||||||
|
## Inhaltsverzeichnis
|
||||||
|
|
||||||
|
1. [tsc not found](#tsc-not-found)
|
||||||
|
2. [Read-only Volume](#read-only-volume)
|
||||||
|
3. [npm audit Vulnerabilities](#npm-audit-vulnerabilities)
|
||||||
|
4. [Node nicht sichtbar](#node-nicht-sichtbar)
|
||||||
|
5. [Authentifizierung fehlgeschlagen](#authentifizierung-fehlgeschlagen)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## tsc not found
|
||||||
|
|
||||||
|
**Symptom:**
|
||||||
|
```
|
||||||
|
sh: 1: tsc: not found
|
||||||
|
npm error code 127
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ursache:** TypeScript ist nicht installiert (im Container oft nicht verfügbar).
|
||||||
|
|
||||||
|
**Lösung:** Auf dem Host bauen!
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Auf dem Host (nicht im Container)
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
# Dann in Container kopieren
|
||||||
|
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
**Oder mit Skript:**
|
||||||
|
```bash
|
||||||
|
./quick-install.sh n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Read-only Volume
|
||||||
|
|
||||||
|
**Symptom:**
|
||||||
|
```
|
||||||
|
EROFS: read-only file system
|
||||||
|
npm error EACCES: permission denied
|
||||||
|
```
|
||||||
|
|
||||||
|
**Ursache:** Volume ist mit `:ro` gemountet.
|
||||||
|
|
||||||
|
**Lösung 1:** Volume ohne `:ro` mounten
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# docker-compose.yml
|
||||||
|
volumes:
|
||||||
|
- ./custom-nodes:/home/node/.n8n/custom # Ohne :ro
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lösung 2:** Auf dem Host bauen und kopieren
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm install && npm run build
|
||||||
|
docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## npm audit Vulnerabilities
|
||||||
|
|
||||||
|
**Symptom:**
|
||||||
|
```
|
||||||
|
found 2 vulnerabilities (1 moderate, 1 critical)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Erklärung:** Diese kommen von `n8n-workflow` und sind für dieses Projekt nicht kritisch.
|
||||||
|
|
||||||
|
**Lösung:** Ignorieren oder `.npmrc` verwenden:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# .npmrc bereits konfiguriert
|
||||||
|
audit=false
|
||||||
|
```
|
||||||
|
|
||||||
|
Siehe [SECURITY.md](SECURITY.md) für Details.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Node nicht sichtbar
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
|
||||||
|
1. Container neustarten:
|
||||||
|
```bash
|
||||||
|
docker restart n8n
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Dateien prüfen:
|
||||||
|
```bash
|
||||||
|
docker exec n8n ls -la /home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Environment prüfen:
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Logs prüfen:
|
||||||
|
```bash
|
||||||
|
docker logs n8n | grep -i librebooking
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Authentifizierung fehlgeschlagen
|
||||||
|
|
||||||
|
**Symptom:** "401 Unauthorized" oder "Invalid credentials"
|
||||||
|
|
||||||
|
**Lösung:**
|
||||||
|
|
||||||
|
1. URL prüfen (ohne `/Web/` am Ende)
|
||||||
|
2. Benutzer muss Admin sein
|
||||||
|
3. API muss in LibreBooking aktiviert sein
|
||||||
|
|
||||||
|
Test:
|
||||||
|
```bash
|
||||||
|
curl -X POST https://librebooking.example.com/Web/Services/Authentication/Authenticate \
|
||||||
|
-H 'Content-Type: application/json' \
|
||||||
|
-d '{"username": "admin", "password": "pass"}'
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Weitere Hilfe
|
||||||
|
|
||||||
|
- [README.md](README.md) - Übersicht
|
||||||
|
- [INSTALLATION.md](INSTALLATION.md) - Installationsanleitung
|
||||||
|
- [DOCKER-INTEGRATION.md](DOCKER-INTEGRATION.md) - Docker Details
|
||||||
|
|
@ -0,0 +1,183 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# build-on-host.sh - Baut den Node auf dem Host (außerhalb des Containers)
|
||||||
|
#
|
||||||
|
# Verwendung: Wenn das Volume als read-only gemountet werden soll
|
||||||
|
#
|
||||||
|
# Dieses Skript:
|
||||||
|
# 1. Installiert Dependencies auf dem Host
|
||||||
|
# 2. Baut den Node auf dem Host
|
||||||
|
# 3. Kopiert nur die fertigen Dateien in den Container
|
||||||
|
# 4. Das Volume kann dann read-only gemountet werden
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Farben
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo -e "${BLUE} LibreBooking Node - Build auf Host${NC}"
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Hilfe anzeigen
|
||||||
|
show_help() {
|
||||||
|
echo "Verwendung: $0 [OPTIONEN]"
|
||||||
|
echo ""
|
||||||
|
echo "Dieses Skript baut den Node auf dem Host, sodass er als"
|
||||||
|
echo "read-only Volume gemountet werden kann."
|
||||||
|
echo ""
|
||||||
|
echo "Optionen:"
|
||||||
|
echo " -o, --output DIR Ausgabeverzeichnis (Standard: ./dist-for-docker)"
|
||||||
|
echo " -c, --copy-to PATH Kopiert direkt zu einem Pfad (z.B. custom-nodes/)"
|
||||||
|
echo " -h, --help Diese Hilfe anzeigen"
|
||||||
|
echo ""
|
||||||
|
echo "Beispiele:"
|
||||||
|
echo " $0 # Baut in ./dist-for-docker"
|
||||||
|
echo " $0 -c ../n8n/custom-nodes/n8n-nodes-librebooking"
|
||||||
|
echo ""
|
||||||
|
echo "Danach in docker-compose.yml:"
|
||||||
|
echo " volumes:"
|
||||||
|
echo " - ./dist-for-docker:/home/node/.n8n/custom/n8n-nodes-librebooking:ro"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
OUTPUT_DIR="$SCRIPT_DIR/dist-for-docker"
|
||||||
|
COPY_TO=""
|
||||||
|
|
||||||
|
# Parameter parsen
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-o|--output)
|
||||||
|
OUTPUT_DIR="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-c|--copy-to)
|
||||||
|
COPY_TO="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Unbekannte Option: $1${NC}"
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Schritt 1: Prüfe Voraussetzungen
|
||||||
|
log "Prüfe Voraussetzungen..."
|
||||||
|
|
||||||
|
if ! command -v node &>/dev/null; then
|
||||||
|
error "Node.js ist nicht installiert!\n\n Installieren Sie Node.js 18+: https://nodejs.org/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NODE_VERSION=$(node -v | cut -d'v' -f2 | cut -d'.' -f1)
|
||||||
|
if [ "$NODE_VERSION" -lt 18 ]; then
|
||||||
|
error "Node.js Version $NODE_VERSION ist zu alt. Mindestens v18 erforderlich."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v npm &>/dev/null; then
|
||||||
|
error "npm ist nicht installiert!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Node.js: $(node -v)"
|
||||||
|
log "npm: $(npm -v)"
|
||||||
|
|
||||||
|
# Schritt 2: Dependencies installieren
|
||||||
|
log "Installiere Dependencies..."
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
if npm install 2>&1 | tail -5; then
|
||||||
|
log "Dependencies installiert ✓"
|
||||||
|
else
|
||||||
|
error "npm install fehlgeschlagen!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 3: Bauen
|
||||||
|
log "Baue den Node..."
|
||||||
|
|
||||||
|
if npm run build 2>&1 | tail -10; then
|
||||||
|
log "Build erfolgreich ✓"
|
||||||
|
else
|
||||||
|
error "npm run build fehlgeschlagen!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 4: Prüfe Build
|
||||||
|
if [ ! -d "$SCRIPT_DIR/dist" ]; then
|
||||||
|
error "dist/ Verzeichnis wurde nicht erstellt!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
NODE_COUNT=$(find "$SCRIPT_DIR/dist" -name "*.node.js" | wc -l)
|
||||||
|
if [ "$NODE_COUNT" -eq 0 ]; then
|
||||||
|
error "Keine .node.js Dateien im dist/ Verzeichnis!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Gefunden: $NODE_COUNT Node-Datei(en)"
|
||||||
|
|
||||||
|
# Schritt 5: Erstelle Ausgabeverzeichnis
|
||||||
|
log "Erstelle Ausgabeverzeichnis: $OUTPUT_DIR"
|
||||||
|
|
||||||
|
mkdir -p "$OUTPUT_DIR"
|
||||||
|
|
||||||
|
# Kopiere alle notwendigen Dateien
|
||||||
|
cp -r "$SCRIPT_DIR/dist" "$OUTPUT_DIR/"
|
||||||
|
cp "$SCRIPT_DIR/package.json" "$OUTPUT_DIR/"
|
||||||
|
cp -r "$SCRIPT_DIR/node_modules" "$OUTPUT_DIR/" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Optional: Nur essentielle Dateien für minimales Image
|
||||||
|
log "Räume auf..."
|
||||||
|
rm -rf "$OUTPUT_DIR/node_modules/.cache" 2>/dev/null || true
|
||||||
|
|
||||||
|
# Schritt 6: Optional kopieren
|
||||||
|
if [ -n "$COPY_TO" ]; then
|
||||||
|
log "Kopiere zu: $COPY_TO"
|
||||||
|
mkdir -p "$COPY_TO"
|
||||||
|
cp -r "$OUTPUT_DIR/"* "$COPY_TO/"
|
||||||
|
log "Kopiert ✓"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo -e "${GREEN} Build abgeschlossen!${NC}"
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
log "Ausgabe in: $OUTPUT_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Kopieren Sie das Verzeichnis zu Ihrer n8n Installation:"
|
||||||
|
echo " cp -r $OUTPUT_DIR /pfad/zu/n8n/custom-nodes/n8n-nodes-librebooking"
|
||||||
|
echo ""
|
||||||
|
echo "2. Oder verwenden Sie es direkt in docker-compose.yml:"
|
||||||
|
echo ""
|
||||||
|
echo " services:"
|
||||||
|
echo " n8n:"
|
||||||
|
echo " volumes:"
|
||||||
|
echo " # Read-only möglich, da bereits gebaut!"
|
||||||
|
echo " - $OUTPUT_DIR:/home/node/.n8n/custom/n8n-nodes-librebooking:ro"
|
||||||
|
echo ""
|
||||||
|
echo "3. Starten Sie n8n neu:"
|
||||||
|
echo " docker compose restart n8n"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,281 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ============================================================================
|
||||||
|
# check-installation.sh - Debug-Skript für LibreBooking Node Installation
|
||||||
|
# Kann sowohl im Container als auch auf dem Host ausgeführt werden
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " LibreBooking Node - Installation Check"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Ausgeführt: $(date)"
|
||||||
|
echo "Hostname: $(hostname)"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Status-Zähler
|
||||||
|
OK_COUNT=0
|
||||||
|
WARN_COUNT=0
|
||||||
|
ERROR_COUNT=0
|
||||||
|
|
||||||
|
ok() {
|
||||||
|
echo "[✓] $1"
|
||||||
|
OK_COUNT=$((OK_COUNT + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo "[!] $1"
|
||||||
|
WARN_COUNT=$((WARN_COUNT + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo "[✗] $1"
|
||||||
|
ERROR_COUNT=$((ERROR_COUNT + 1))
|
||||||
|
}
|
||||||
|
|
||||||
|
info() {
|
||||||
|
echo "[i] $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " 1. UMGEBUNG"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
# Node.js/npm prüfen
|
||||||
|
if command -v node >/dev/null 2>&1; then
|
||||||
|
ok "Node.js installiert: $(node --version)"
|
||||||
|
else
|
||||||
|
error "Node.js nicht gefunden"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if command -v npm >/dev/null 2>&1; then
|
||||||
|
ok "npm installiert: $(npm --version)"
|
||||||
|
else
|
||||||
|
error "npm nicht gefunden"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Aktueller Benutzer
|
||||||
|
info "Aktueller Benutzer: $(whoami) (UID: $(id -u))"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " 2. UMGEBUNGSVARIABLEN"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
if [ -n "$N8N_CUSTOM_EXTENSIONS" ]; then
|
||||||
|
ok "N8N_CUSTOM_EXTENSIONS: $N8N_CUSTOM_EXTENSIONS"
|
||||||
|
else
|
||||||
|
warn "N8N_CUSTOM_EXTENSIONS nicht gesetzt"
|
||||||
|
info " Empfehlung: N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$N8N_COMMUNITY_NODES_ENABLED" = "true" ]; then
|
||||||
|
ok "N8N_COMMUNITY_NODES_ENABLED: true"
|
||||||
|
else
|
||||||
|
warn "N8N_COMMUNITY_NODES_ENABLED ist nicht 'true'"
|
||||||
|
info " Aktueller Wert: ${N8N_COMMUNITY_NODES_ENABLED:-<nicht gesetzt>}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Weitere n8n Variablen
|
||||||
|
if [ -n "$N8N_LOG_LEVEL" ]; then
|
||||||
|
info "N8N_LOG_LEVEL: $N8N_LOG_LEVEL"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " 3. VERZEICHNISSE PRÜFEN"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
# Mögliche Pfade
|
||||||
|
POSSIBLE_PATHS="
|
||||||
|
/home/node/.n8n/custom/n8n-nodes-librebooking
|
||||||
|
/home/node/.n8n/custom
|
||||||
|
/opt/n8n/custom-nodes
|
||||||
|
/data/custom-nodes
|
||||||
|
"
|
||||||
|
|
||||||
|
FOUND_PATH=""
|
||||||
|
|
||||||
|
for path in $POSSIBLE_PATHS; do
|
||||||
|
if [ -d "$path" ]; then
|
||||||
|
if [ -f "$path/package.json" ]; then
|
||||||
|
ok "Custom Node Verzeichnis: $path"
|
||||||
|
FOUND_PATH="$path"
|
||||||
|
break
|
||||||
|
elif [ -f "$path/n8n-nodes-librebooking/package.json" ]; then
|
||||||
|
ok "Custom Node Verzeichnis: $path/n8n-nodes-librebooking"
|
||||||
|
FOUND_PATH="$path/n8n-nodes-librebooking"
|
||||||
|
break
|
||||||
|
else
|
||||||
|
info "Verzeichnis existiert (ohne package.json): $path"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$FOUND_PATH" ]; then
|
||||||
|
error "Kein Custom Node Verzeichnis mit package.json gefunden!"
|
||||||
|
info " Geprüfte Pfade:"
|
||||||
|
for path in $POSSIBLE_PATHS; do
|
||||||
|
info " - $path"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " 4. NODE-DATEIEN PRÜFEN"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
if [ -n "$FOUND_PATH" ]; then
|
||||||
|
cd "$FOUND_PATH" 2>/dev/null || true
|
||||||
|
|
||||||
|
# package.json
|
||||||
|
if [ -f "package.json" ]; then
|
||||||
|
ok "package.json vorhanden"
|
||||||
|
info " Name: $(grep '"name":' package.json | head -1 | cut -d'"' -f4)"
|
||||||
|
info " Version: $(grep '"version":' package.json | head -1 | cut -d'"' -f4)"
|
||||||
|
else
|
||||||
|
error "package.json fehlt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# node_modules
|
||||||
|
if [ -d "node_modules" ]; then
|
||||||
|
MODULE_COUNT=$(ls -1 node_modules 2>/dev/null | wc -l)
|
||||||
|
ok "node_modules vorhanden ($MODULE_COUNT Pakete)"
|
||||||
|
else
|
||||||
|
error "node_modules fehlt - führen Sie 'npm install' aus"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# dist Verzeichnis
|
||||||
|
if [ -d "dist" ]; then
|
||||||
|
ok "dist/ Verzeichnis vorhanden"
|
||||||
|
|
||||||
|
# .node.js Dateien suchen
|
||||||
|
echo ""
|
||||||
|
info "Gefundene Node-Dateien:"
|
||||||
|
NODE_FILES=$(find dist -name "*.node.js" 2>/dev/null)
|
||||||
|
if [ -n "$NODE_FILES" ]; then
|
||||||
|
echo "$NODE_FILES" | while read -r f; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
ok " $f ($(ls -lh "$f" | awk '{print $5}'))"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
error " Keine .node.js Dateien gefunden!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Credentials
|
||||||
|
echo ""
|
||||||
|
info "Gefundene Credential-Dateien:"
|
||||||
|
CRED_FILES=$(find dist -name "*.credentials.js" 2>/dev/null)
|
||||||
|
if [ -n "$CRED_FILES" ]; then
|
||||||
|
echo "$CRED_FILES" | while read -r f; do
|
||||||
|
if [ -f "$f" ]; then
|
||||||
|
ok " $f"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
else
|
||||||
|
error " Keine .credentials.js Dateien gefunden!"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
error "dist/ Verzeichnis fehlt - führen Sie 'npm run build' aus"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Icons
|
||||||
|
echo ""
|
||||||
|
info "Icon-Dateien:"
|
||||||
|
find . -name "*.svg" 2>/dev/null | while read -r f; do
|
||||||
|
info " $f"
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " 5. BERECHTIGUNGEN"
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
# Funktion: Prüft ob ein Verzeichnis read-only ist
|
||||||
|
check_readonly() {
|
||||||
|
local dir="$1"
|
||||||
|
local test_file="$dir/.write_test_$$"
|
||||||
|
|
||||||
|
# Versuche eine Test-Datei zu erstellen
|
||||||
|
if touch "$test_file" 2>/dev/null; then
|
||||||
|
rm -f "$test_file" 2>/dev/null
|
||||||
|
return 0 # Schreibbar
|
||||||
|
else
|
||||||
|
return 1 # Read-only
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ -n "$FOUND_PATH" ]; then
|
||||||
|
# Prüfe Schreibrechte (inkl. read-only Volume Check)
|
||||||
|
if check_readonly "$FOUND_PATH"; then
|
||||||
|
ok "Schreibrechte auf $FOUND_PATH"
|
||||||
|
else
|
||||||
|
error "KEINE Schreibrechte auf $FOUND_PATH (Read-only Volume?)"
|
||||||
|
echo ""
|
||||||
|
info " PROBLEM: Das Verzeichnis ist möglicherweise als :ro gemountet."
|
||||||
|
info " LÖSUNG 1: Volume ohne :ro mounten:"
|
||||||
|
info " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
|
||||||
|
info " LÖSUNG 2: Auf dem Host bauen mit ./build-on-host.sh"
|
||||||
|
info " Siehe: TROUBLESHOOTING.md"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Besitzer prüfen
|
||||||
|
OWNER=$(ls -ld "$FOUND_PATH" | awk '{print $3}')
|
||||||
|
GROUP=$(ls -ld "$FOUND_PATH" | awk '{print $4}')
|
||||||
|
info "Besitzer: $OWNER:$GROUP"
|
||||||
|
|
||||||
|
# n8n läuft als node (UID 1000)
|
||||||
|
if [ "$(id -u)" = "1000" ]; then
|
||||||
|
ok "Läuft als UID 1000 (Standard für n8n)"
|
||||||
|
else
|
||||||
|
warn "Läuft nicht als UID 1000 (aktuell: $(id -u))"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " ZUSAMMENFASSUNG"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Ergebnisse:"
|
||||||
|
echo " ✓ OK: $OK_COUNT"
|
||||||
|
echo " ! Warnung: $WARN_COUNT"
|
||||||
|
echo " ✗ Fehler: $ERROR_COUNT"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ "$ERROR_COUNT" -gt 0 ]; then
|
||||||
|
echo "============================================"
|
||||||
|
echo " EMPFOHLENE AKTIONEN"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ ! -d "node_modules" ] 2>/dev/null; then
|
||||||
|
echo "1. Dependencies installieren:"
|
||||||
|
echo " cd $FOUND_PATH && npm install"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ ! -d "dist" ] 2>/dev/null; then
|
||||||
|
echo "2. Node bauen:"
|
||||||
|
echo " cd $FOUND_PATH && npm run build"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "3. Container neustarten:"
|
||||||
|
echo " docker restart n8n"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
elif [ "$WARN_COUNT" -gt 0 ]; then
|
||||||
|
echo "Status: TEILWEISE OK (Warnungen beachten)"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "Status: ALLES OK ✓"
|
||||||
|
echo ""
|
||||||
|
echo "Falls der Node trotzdem nicht erscheint:"
|
||||||
|
echo " 1. Starten Sie n8n neu: docker restart n8n"
|
||||||
|
echo " 2. Prüfen Sie die Logs: docker logs n8n | grep -i libre"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# create-release.sh - Erstellt neue Archive und Git Tag
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
# Version aus package.json lesen
|
||||||
|
VERSION=$(grep '"version"' "$SCRIPT_DIR/package.json" | head -1 | sed 's/.*"version": "\([^"]*\)".*/\1/')
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Release erstellen v$VERSION ===${NC}\n"
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# 1. Prüfe, ob alles committet ist
|
||||||
|
if [ -n "$(git status --porcelain)" ]; then
|
||||||
|
echo -e "${YELLOW}Warnung: Es gibt uncommittete Änderungen!${NC}"
|
||||||
|
git status --short
|
||||||
|
echo ""
|
||||||
|
read -p "Trotzdem fortfahren? (j/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Jj]$ ]]; then
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# 2. Alte Archive löschen
|
||||||
|
echo "[1/4] Lösche alte Archive..."
|
||||||
|
./git-cleanup.sh 2>/dev/null || true
|
||||||
|
|
||||||
|
# 3. Build prüfen
|
||||||
|
echo ""
|
||||||
|
echo "[2/4] Prüfe Build..."
|
||||||
|
if [ ! -d "dist" ] || [ -z "$(find dist -name '*.node.js' 2>/dev/null)" ]; then
|
||||||
|
echo "Baue Node..."
|
||||||
|
npm install --silent
|
||||||
|
npm run build --silent
|
||||||
|
fi
|
||||||
|
echo " ✓ Build OK"
|
||||||
|
|
||||||
|
# 4. Archive erstellen
|
||||||
|
echo ""
|
||||||
|
echo "[3/4] Erstelle Archive..."
|
||||||
|
|
||||||
|
TAR_FILE="$PARENT_DIR/n8n-nodes-librebooking-v${VERSION}.tar.gz"
|
||||||
|
ZIP_FILE="$PARENT_DIR/n8n-nodes-librebooking-v${VERSION}.zip"
|
||||||
|
|
||||||
|
git archive --format=tar.gz --prefix=n8n-nodes-librebooking/ --output="$TAR_FILE" HEAD
|
||||||
|
echo " ✓ $TAR_FILE"
|
||||||
|
|
||||||
|
git archive --format=zip --prefix=n8n-nodes-librebooking/ --output="$ZIP_FILE" HEAD
|
||||||
|
echo " ✓ $ZIP_FILE"
|
||||||
|
|
||||||
|
# 5. Git Tag (optional)
|
||||||
|
echo ""
|
||||||
|
echo "[4/4] Git Tag..."
|
||||||
|
|
||||||
|
if git tag | grep -q "v$VERSION"; then
|
||||||
|
echo -e " ${YELLOW}Tag v$VERSION existiert bereits${NC}"
|
||||||
|
else
|
||||||
|
read -p "Git Tag v$VERSION erstellen? (j/N) " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ $REPLY =~ ^[Jj]$ ]]; then
|
||||||
|
git tag -a "v$VERSION" -m "Version $VERSION - Vereinfachte Installation"
|
||||||
|
echo " ✓ Tag v$VERSION erstellt"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo -e "${GREEN} Release v$VERSION erstellt!${NC}"
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Archive:"
|
||||||
|
echo " $TAR_FILE"
|
||||||
|
echo " $ZIP_FILE"
|
||||||
|
echo ""
|
||||||
|
echo "Zum Pushen:"
|
||||||
|
echo " git push origin main"
|
||||||
|
echo " git push origin v$VERSION"
|
||||||
|
echo ""
|
||||||
|
|
@ -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,21 @@
|
||||||
|
# Docker Compose Override für LibreBooking n8n Node
|
||||||
|
#
|
||||||
|
# WICHTIG: custom-nodes Volume NICHT als read-only (:ro) mounten!
|
||||||
|
# Der Node benötigt Schreibrechte für npm install/build.
|
||||||
|
#
|
||||||
|
# Für read-only Volumes siehe: docker-compose.readonly.yml
|
||||||
|
# und führen Sie vorher build-on-host.sh aus.
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
n8n:
|
||||||
|
volumes:
|
||||||
|
# LibreBooking Custom Node
|
||||||
|
# OHNE :ro - Der Node muss beim Start gebaut werden können!
|
||||||
|
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking
|
||||||
|
|
||||||
|
environment:
|
||||||
|
# Custom Nodes aktivieren
|
||||||
|
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
- N8N_COMMUNITY_NODES_ENABLED=true
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Docker Compose Override für READ-ONLY Volume Mount
|
||||||
|
#
|
||||||
|
# VORAUSSETZUNG: Node wurde auf dem Host gebaut!
|
||||||
|
# Führen Sie zuerst aus: ./build-on-host.sh
|
||||||
|
#
|
||||||
|
# Verwendung:
|
||||||
|
# docker compose -f docker-compose.yml -f docker-compose.readonly.yml up -d
|
||||||
|
#
|
||||||
|
# Oder setzen Sie in Ihrer Umgebung:
|
||||||
|
# export COMPOSE_FILE=docker-compose.yml:docker-compose.readonly.yml
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
n8n:
|
||||||
|
volumes:
|
||||||
|
# LibreBooking Custom Node - READ-ONLY
|
||||||
|
# Das dist-for-docker Verzeichnis wurde mit build-on-host.sh erstellt
|
||||||
|
# und enthält bereits alle kompilierten Dateien.
|
||||||
|
- ./dist-for-docker:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
|
||||||
|
|
||||||
|
environment:
|
||||||
|
# Custom Nodes aktivieren
|
||||||
|
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
- N8N_COMMUNITY_NODES_ENABLED=true
|
||||||
|
|
@ -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,268 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# fix-node-installation.sh - All-in-One Lösung für Node-Installation im Container
|
||||||
|
# Führt alle Schritte automatisch aus (auf dem HOST ausführen!)
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Farben
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# Standard-Werte
|
||||||
|
CONTAINER_NAME="n8n"
|
||||||
|
CONTAINER_PATH="/home/node/.n8n/custom/n8n-nodes-librebooking"
|
||||||
|
SKIP_RESTART=false
|
||||||
|
VERBOSE=false
|
||||||
|
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo -e "${BLUE} LibreBooking Node - Auto-Fix Installation${NC}"
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Hilfe anzeigen
|
||||||
|
show_help() {
|
||||||
|
echo "Verwendung: $0 [OPTIONEN]"
|
||||||
|
echo ""
|
||||||
|
echo "Dieses Skript installiert den LibreBooking Node automatisch im Docker Container."
|
||||||
|
echo ""
|
||||||
|
echo "Optionen:"
|
||||||
|
echo " -c, --container NAME Container-Name (Standard: n8n)"
|
||||||
|
echo " -p, --path PATH Pfad im Container (Standard: /home/node/.n8n/custom/n8n-nodes-librebooking)"
|
||||||
|
echo " -n, --no-restart Container nicht neustarten"
|
||||||
|
echo " -v, --verbose Ausführliche Ausgabe"
|
||||||
|
echo " -h, --help Diese Hilfe anzeigen"
|
||||||
|
echo ""
|
||||||
|
echo "Beispiele:"
|
||||||
|
echo " $0 # Standard-Installation"
|
||||||
|
echo " $0 -c mein-n8n # Anderer Container-Name"
|
||||||
|
echo " $0 -p /opt/n8n/custom-nodes # Anderer Pfad"
|
||||||
|
echo " $0 -n # Ohne Neustart"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parameter parsen
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-c|--container)
|
||||||
|
CONTAINER_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-p|--path)
|
||||||
|
CONTAINER_PATH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-n|--no-restart)
|
||||||
|
SKIP_RESTART=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-v|--verbose)
|
||||||
|
VERBOSE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo -e "${RED}Unbekannte Option: $1${NC}"
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Funktion: Prüft ob ein Verzeichnis im Container read-only ist
|
||||||
|
check_readonly_container() {
|
||||||
|
local container="$1"
|
||||||
|
local dir="$2"
|
||||||
|
local test_file="$dir/.write_test_$$"
|
||||||
|
|
||||||
|
# Versuche eine Test-Datei im Container zu erstellen
|
||||||
|
if docker exec "$container" touch "$test_file" 2>/dev/null; then
|
||||||
|
docker exec "$container" rm -f "$test_file" 2>/dev/null
|
||||||
|
return 0 # Schreibbar
|
||||||
|
else
|
||||||
|
return 1 # Read-only
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_readonly_solution() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${RED}============================================${NC}"
|
||||||
|
echo -e "${RED} PROBLEM: Read-only Volume erkannt!${NC}"
|
||||||
|
echo -e "${RED}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Das custom-nodes Verzeichnis ist als read-only gemountet."
|
||||||
|
echo "npm install und npm run build benötigen Schreibrechte."
|
||||||
|
echo ""
|
||||||
|
echo "LÖSUNGEN:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Volume OHNE :ro mounten (empfohlen):"
|
||||||
|
echo " In docker-compose.yml oder docker-compose.override.yml:"
|
||||||
|
echo ""
|
||||||
|
echo " volumes:"
|
||||||
|
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
|
||||||
|
echo " # ENTFERNEN: :ro am Ende!"
|
||||||
|
echo ""
|
||||||
|
echo "2. Auf dem Host bauen (für read-only Volumes):"
|
||||||
|
echo " ./build-on-host.sh"
|
||||||
|
echo " # Dann docker-compose.readonly.yml verwenden"
|
||||||
|
echo ""
|
||||||
|
echo "3. docker-compose.override.yml anpassen:"
|
||||||
|
echo " cp docker-compose.override.yml docker-compose.override.yml.bak"
|
||||||
|
echo " # Entfernen Sie ':ro' aus der Volume-Definition"
|
||||||
|
echo ""
|
||||||
|
echo "Siehe: TROUBLESHOOTING.md und DOCKER-INTEGRATION.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Schritt 1: Docker prüfen
|
||||||
|
log "Prüfe Docker..."
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
error "Docker ist nicht installiert!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 2: Container prüfen
|
||||||
|
log "Prüfe Container '$CONTAINER_NAME'..."
|
||||||
|
if ! docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
# Prüfe ob Container existiert aber nicht läuft
|
||||||
|
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
warn "Container '$CONTAINER_NAME' existiert aber läuft nicht."
|
||||||
|
log "Starte Container..."
|
||||||
|
docker start "$CONTAINER_NAME"
|
||||||
|
sleep 3
|
||||||
|
else
|
||||||
|
error "Container '$CONTAINER_NAME' nicht gefunden! Prüfen Sie den Namen mit: docker ps -a"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
log "Container '$CONTAINER_NAME' läuft ✓"
|
||||||
|
|
||||||
|
# Schritt 3: Prüfe ob Pfad im Container existiert
|
||||||
|
log "Prüfe Pfad im Container: $CONTAINER_PATH"
|
||||||
|
if ! docker exec "$CONTAINER_NAME" test -d "$CONTAINER_PATH"; then
|
||||||
|
# Versuche alternative Pfade
|
||||||
|
ALTERNATIVE_PATHS=(
|
||||||
|
"/home/node/.n8n/custom/n8n-nodes-librebooking"
|
||||||
|
"/home/node/.n8n/custom"
|
||||||
|
"/opt/n8n/custom-nodes"
|
||||||
|
"/data/custom-nodes"
|
||||||
|
)
|
||||||
|
|
||||||
|
FOUND_PATH=""
|
||||||
|
for alt_path in "${ALTERNATIVE_PATHS[@]}"; do
|
||||||
|
if docker exec "$CONTAINER_NAME" test -f "$alt_path/package.json" 2>/dev/null; then
|
||||||
|
FOUND_PATH="$alt_path"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -n "$FOUND_PATH" ]; then
|
||||||
|
warn "Angegebener Pfad nicht gefunden, verwende: $FOUND_PATH"
|
||||||
|
CONTAINER_PATH="$FOUND_PATH"
|
||||||
|
else
|
||||||
|
error "Kein Custom-Node-Verzeichnis mit package.json gefunden!\n\n Bitte stellen Sie sicher, dass die Dateien korrekt kopiert wurden.\n Beispiel: docker cp custom-nodes/. $CONTAINER_NAME:/home/node/.n8n/custom/n8n-nodes-librebooking/"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
log "Pfad gefunden: $CONTAINER_PATH ✓"
|
||||||
|
|
||||||
|
# Schritt 3.5: Prüfe ob Verzeichnis schreibbar ist (read-only Volume?)
|
||||||
|
log "Prüfe Schreibrechte im Container..."
|
||||||
|
if ! check_readonly_container "$CONTAINER_NAME" "$CONTAINER_PATH"; then
|
||||||
|
print_readonly_solution
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
log "Schreibrechte vorhanden ✓"
|
||||||
|
|
||||||
|
# Schritt 4: Skript in Container kopieren
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
if [ -f "$SCRIPT_DIR/install-in-container.sh" ]; then
|
||||||
|
log "Kopiere install-in-container.sh in Container..."
|
||||||
|
docker cp "$SCRIPT_DIR/install-in-container.sh" "$CONTAINER_NAME:/tmp/"
|
||||||
|
else
|
||||||
|
warn "install-in-container.sh nicht gefunden, erstelle inline..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 5: npm install und build im Container
|
||||||
|
log "Führe npm install aus..."
|
||||||
|
if $VERBOSE; then
|
||||||
|
docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm install
|
||||||
|
else
|
||||||
|
if ! docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm install 2>&1 | tail -5; then
|
||||||
|
error "npm install fehlgeschlagen!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
log "Dependencies installiert ✓"
|
||||||
|
|
||||||
|
log "Führe npm run build aus..."
|
||||||
|
if $VERBOSE; then
|
||||||
|
docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm run build
|
||||||
|
else
|
||||||
|
if ! docker exec -w "$CONTAINER_PATH" "$CONTAINER_NAME" npm run build 2>&1 | tail -10; then
|
||||||
|
error "npm run build fehlgeschlagen!"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
log "Build erfolgreich ✓"
|
||||||
|
|
||||||
|
# Schritt 6: Prüfe Ergebnis
|
||||||
|
log "Prüfe Build-Ergebnis..."
|
||||||
|
NODE_COUNT=$(docker exec "$CONTAINER_NAME" find "$CONTAINER_PATH/dist" -name "*.node.js" 2>/dev/null | wc -l)
|
||||||
|
if [ "$NODE_COUNT" -gt 0 ]; then
|
||||||
|
log "$NODE_COUNT Node-Datei(en) gefunden ✓"
|
||||||
|
else
|
||||||
|
error "Keine Node-Dateien nach Build gefunden!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 7: Container neustarten
|
||||||
|
if $SKIP_RESTART; then
|
||||||
|
warn "Container-Neustart übersprungen (-n Option)"
|
||||||
|
echo ""
|
||||||
|
echo -e "${YELLOW}Bitte starten Sie den Container manuell neu:${NC}"
|
||||||
|
echo " docker restart $CONTAINER_NAME"
|
||||||
|
else
|
||||||
|
log "Starte Container neu..."
|
||||||
|
docker restart "$CONTAINER_NAME"
|
||||||
|
log "Container neugestartet ✓"
|
||||||
|
|
||||||
|
# Warte kurz auf Start
|
||||||
|
sleep 5
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 8: Abschluss-Check (optional)
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo -e "${GREEN} Installation abgeschlossen!${NC}"
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
log "Der LibreBooking Node sollte jetzt in n8n verfügbar sein."
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo " 1. Öffnen Sie n8n im Browser"
|
||||||
|
echo " 2. Erstellen Sie einen neuen Workflow"
|
||||||
|
echo " 3. Suchen Sie nach 'LibreBooking'"
|
||||||
|
echo ""
|
||||||
|
echo "Falls der Node nicht erscheint:"
|
||||||
|
echo " - Prüfen Sie die Logs: docker logs $CONTAINER_NAME 2>&1 | grep -i libre"
|
||||||
|
echo " - Führen Sie das Check-Skript aus: docker exec $CONTAINER_NAME sh /tmp/check-installation.sh"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Optional: Check-Skript kopieren und ausführen
|
||||||
|
if [ -f "$SCRIPT_DIR/check-installation.sh" ]; then
|
||||||
|
docker cp "$SCRIPT_DIR/check-installation.sh" "$CONTAINER_NAME:/tmp/"
|
||||||
|
echo "Tipp: Führen Sie für einen detaillierten Status aus:"
|
||||||
|
echo " docker exec $CONTAINER_NAME sh /tmp/check-installation.sh"
|
||||||
|
fi
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# git-cleanup.sh - Löscht alte Archive und temporäre Dateien
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
PARENT_DIR="$(dirname "$SCRIPT_DIR")"
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Git Cleanup ===${NC}\n"
|
||||||
|
|
||||||
|
# Alte Archive im übergeordneten Verzeichnis löschen
|
||||||
|
echo "Lösche alte Archive in $PARENT_DIR..."
|
||||||
|
find "$PARENT_DIR" -maxdepth 1 -name "n8n-nodes-librebooking*.tar.gz" -delete 2>/dev/null && echo " ✓ .tar.gz gelöscht" || echo " - Keine .tar.gz gefunden"
|
||||||
|
find "$PARENT_DIR" -maxdepth 1 -name "n8n-nodes-librebooking*.zip" -delete 2>/dev/null && echo " ✓ .zip gelöscht" || echo " - Keine .zip gefunden"
|
||||||
|
|
||||||
|
# Temporäre Dateien im Projekt löschen
|
||||||
|
echo ""
|
||||||
|
echo "Lösche temporäre Dateien..."
|
||||||
|
rm -rf "$SCRIPT_DIR/dist-for-docker" 2>/dev/null && echo " ✓ dist-for-docker/ gelöscht" || true
|
||||||
|
rm -rf "$SCRIPT_DIR/.tsbuildinfo" 2>/dev/null && echo " ✓ .tsbuildinfo gelöscht" || true
|
||||||
|
find "$SCRIPT_DIR" -name "*.log" -delete 2>/dev/null && echo " ✓ .log Dateien gelöscht" || true
|
||||||
|
find "$SCRIPT_DIR" -name ".DS_Store" -delete 2>/dev/null || true
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓ Cleanup abgeschlossen${NC}"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# git-commit.sh - Committet alle Änderungen
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
MESSAGE="${1:-fix: Vereinfachte Installation - auf dem Host bauen}"
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== Git Commit ===${NC}\n"
|
||||||
|
|
||||||
|
# Status anzeigen
|
||||||
|
echo "Geänderte Dateien:"
|
||||||
|
git status --short
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Alles hinzufügen
|
||||||
|
git add .
|
||||||
|
|
||||||
|
# Commit
|
||||||
|
echo "Commit mit Nachricht: $MESSAGE"
|
||||||
|
git commit -m "$MESSAGE"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓ Commit erfolgreich${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Zum Pushen:"
|
||||||
|
echo " git push origin main"
|
||||||
|
echo ""
|
||||||
|
|
@ -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,238 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LibreBooking n8n Node - Manuelle Docker Installation (ohne docker-compose)
|
||||||
|
# =============================================================================
|
||||||
|
# Diese Alternative verwendet nur "docker" Befehle für maximale Kompatibilität.
|
||||||
|
# Nutzen Sie dieses Skript wenn docker-compose Probleme macht.
|
||||||
|
#
|
||||||
|
# Verwendung: ./install-docker-manual.sh [OPTIONS]
|
||||||
|
#
|
||||||
|
# Optionen:
|
||||||
|
# -p, --port PORT n8n Port (Standard: 5678)
|
||||||
|
# -n, --name NAME Container Name (Standard: n8n-librebooking)
|
||||||
|
# -f, --force Bestehenden Container ersetzen
|
||||||
|
# -h, --help Diese Hilfe anzeigen
|
||||||
|
#
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Farben für Ausgabe
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Standardwerte
|
||||||
|
N8N_PORT=5678
|
||||||
|
CONTAINER_NAME="n8n-librebooking"
|
||||||
|
FORCE=false
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Hilfsfunktionen
|
||||||
|
print_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}[✓]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[!]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[FEHLER]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
echo "LibreBooking n8n Node - Manuelle Docker Installation"
|
||||||
|
echo ""
|
||||||
|
echo "Verwendung: $0 [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Optionen:"
|
||||||
|
echo " -p, --port PORT n8n Port (Standard: 5678)"
|
||||||
|
echo " -n, --name NAME Container Name (Standard: n8n-librebooking)"
|
||||||
|
echo " -f, --force Bestehenden Container ersetzen"
|
||||||
|
echo " -h, --help Diese Hilfe anzeigen"
|
||||||
|
echo ""
|
||||||
|
echo "Beispiele:"
|
||||||
|
echo " $0 # Standard-Installation"
|
||||||
|
echo " $0 -p 8080 # Anderer Port"
|
||||||
|
echo " $0 -n mein-n8n -p 9000 # Eigener Name und Port"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Argumente parsen
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-p|--port)
|
||||||
|
N8N_PORT="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-n|--name)
|
||||||
|
CONTAINER_NAME="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-f|--force)
|
||||||
|
FORCE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Unbekannte Option: $1"
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================="
|
||||||
|
echo " LibreBooking n8n Node - Manuelle Installation"
|
||||||
|
echo " (ohne docker-compose)"
|
||||||
|
echo "============================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Voraussetzungen prüfen
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Prüfe Voraussetzungen..."
|
||||||
|
|
||||||
|
# Docker prüfen
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
print_error "Docker ist nicht installiert!"
|
||||||
|
echo " Bitte installieren Sie Docker: https://docs.docker.com/get-docker/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
print_success "Docker gefunden: $(docker --version)"
|
||||||
|
|
||||||
|
# Prüfen ob Container bereits existiert
|
||||||
|
if docker ps -a --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
if [ "$FORCE" = true ]; then
|
||||||
|
print_warning "Bestehender Container '$CONTAINER_NAME' wird entfernt..."
|
||||||
|
docker stop "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
docker rm "$CONTAINER_NAME" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
print_error "Container '$CONTAINER_NAME' existiert bereits!"
|
||||||
|
echo " Nutzen Sie -f um ihn zu ersetzen, oder -n für einen anderen Namen."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Volume und Verzeichnisse vorbereiten
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Bereite Volumes vor..."
|
||||||
|
|
||||||
|
# Docker Volume für n8n Daten erstellen
|
||||||
|
VOLUME_NAME="${CONTAINER_NAME}_data"
|
||||||
|
if ! docker volume ls --format '{{.Name}}' | grep -q "^${VOLUME_NAME}$"; then
|
||||||
|
docker volume create "$VOLUME_NAME"
|
||||||
|
print_success "Volume '$VOLUME_NAME' erstellt"
|
||||||
|
else
|
||||||
|
print_info "Volume '$VOLUME_NAME' existiert bereits"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Custom Nodes Verzeichnis vorbereiten
|
||||||
|
CUSTOM_NODES_DIR="$SCRIPT_DIR/custom-nodes"
|
||||||
|
if [ ! -d "$CUSTOM_NODES_DIR" ]; then
|
||||||
|
print_error "custom-nodes Verzeichnis nicht gefunden: $CUSTOM_NODES_DIR"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Node bauen (wenn nicht bereits gebaut)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
if [ ! -d "$CUSTOM_NODES_DIR/dist" ]; then
|
||||||
|
print_info "Baue LibreBooking Node..."
|
||||||
|
|
||||||
|
if command -v npm &> /dev/null; then
|
||||||
|
cd "$CUSTOM_NODES_DIR"
|
||||||
|
npm install 2>/dev/null || print_warning "npm install hatte Warnungen"
|
||||||
|
npm run build 2>/dev/null || {
|
||||||
|
print_warning "Build fehlgeschlagen, versuche alternativen Ansatz..."
|
||||||
|
npx tsc 2>/dev/null || true
|
||||||
|
mkdir -p dist/nodes/LibreBooking dist/nodes/LibreBookingTrigger
|
||||||
|
cp nodes/LibreBooking/*.svg dist/nodes/LibreBooking/ 2>/dev/null || true
|
||||||
|
cp nodes/LibreBookingTrigger/*.svg dist/nodes/LibreBookingTrigger/ 2>/dev/null || true
|
||||||
|
}
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
print_success "Node erfolgreich gebaut"
|
||||||
|
else
|
||||||
|
print_warning "npm nicht gefunden - Node wird im Container gebaut"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# n8n Container starten
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Starte n8n Container..."
|
||||||
|
|
||||||
|
# Berechtigungen setzen
|
||||||
|
if [ "$(id -u)" = "0" ]; then
|
||||||
|
chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || true
|
||||||
|
else
|
||||||
|
sudo chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || print_warning "Konnte Berechtigungen nicht setzen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Container starten
|
||||||
|
docker run -d \
|
||||||
|
--name "$CONTAINER_NAME" \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p "${N8N_PORT}:5678" \
|
||||||
|
-v "${VOLUME_NAME}:/home/node/.n8n" \
|
||||||
|
-v "${CUSTOM_NODES_DIR}:/home/node/.n8n/custom/n8n-nodes-librebooking:ro" \
|
||||||
|
-e N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom \
|
||||||
|
-e N8N_COMMUNITY_NODES_ENABLED=true \
|
||||||
|
-e TZ=Europe/Berlin \
|
||||||
|
-e GENERIC_TIMEZONE=Europe/Berlin \
|
||||||
|
n8nio/n8n:latest
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Container gestartet!"
|
||||||
|
else
|
||||||
|
print_error "Container konnte nicht gestartet werden!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Warten auf Start
|
||||||
|
print_info "Warte auf n8n Start..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# Status prüfen
|
||||||
|
if docker ps --format '{{.Names}}' | grep -q "^${CONTAINER_NAME}$"; then
|
||||||
|
print_success "n8n Container läuft!"
|
||||||
|
else
|
||||||
|
print_error "Container läuft nicht - prüfen Sie: docker logs $CONTAINER_NAME"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Abschluss
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================="
|
||||||
|
print_success "Installation abgeschlossen!"
|
||||||
|
echo "============================================="
|
||||||
|
echo ""
|
||||||
|
echo "n8n ist erreichbar unter: http://localhost:${N8N_PORT}"
|
||||||
|
echo ""
|
||||||
|
echo "Nützliche Befehle:"
|
||||||
|
echo " docker logs $CONTAINER_NAME # Logs anzeigen"
|
||||||
|
echo " docker stop $CONTAINER_NAME # Container stoppen"
|
||||||
|
echo " docker start $CONTAINER_NAME # Container starten"
|
||||||
|
echo " docker restart $CONTAINER_NAME # Container neustarten"
|
||||||
|
echo " docker rm -f $CONTAINER_NAME # Container löschen"
|
||||||
|
echo ""
|
||||||
|
echo "Bei Problemen siehe: TROUBLESHOOTING.md"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,454 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# LibreBooking n8n Node - Docker Integration Script
|
||||||
|
# =============================================================================
|
||||||
|
# Automatische Integration des LibreBooking Nodes in bestehende n8n Docker-Installationen
|
||||||
|
#
|
||||||
|
# Verwendung: ./install-docker.sh [OPTIONS]
|
||||||
|
#
|
||||||
|
# Optionen:
|
||||||
|
# -p, --path PATH Pfad zur n8n Docker-Installation (Standard: aktuelles Verzeichnis)
|
||||||
|
# -b, --build Node im Container bauen statt vorgebaut kopieren
|
||||||
|
# -f, --force Bestehende Installation überschreiben
|
||||||
|
# -h, --help Diese Hilfe anzeigen
|
||||||
|
#
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Farben für Ausgabe
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Standardwerte
|
||||||
|
N8N_PATH="."
|
||||||
|
FORCE=false
|
||||||
|
BUILD_IN_CONTAINER=false
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Hilfsfunktionen
|
||||||
|
print_info() {
|
||||||
|
echo -e "${BLUE}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_success() {
|
||||||
|
echo -e "${GREEN}[✓]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_warning() {
|
||||||
|
echo -e "${YELLOW}[!]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
print_error() {
|
||||||
|
echo -e "${RED}[FEHLER]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
show_help() {
|
||||||
|
echo "LibreBooking n8n Node - Docker Integration Script"
|
||||||
|
echo ""
|
||||||
|
echo "Verwendung: $0 [OPTIONS]"
|
||||||
|
echo ""
|
||||||
|
echo "Optionen:"
|
||||||
|
echo " -p, --path PATH Pfad zur n8n Docker-Installation"
|
||||||
|
echo " -b, --build Node im Container bauen"
|
||||||
|
echo " -f, --force Bestehende Installation überschreiben"
|
||||||
|
echo " -h, --help Diese Hilfe anzeigen"
|
||||||
|
echo ""
|
||||||
|
echo "Beispiele:"
|
||||||
|
echo " $0 # Installation im aktuellen Verzeichnis"
|
||||||
|
echo " $0 -p /opt/n8n # Installation in /opt/n8n"
|
||||||
|
echo " $0 -f -p /home/user/n8n # Installation mit Überschreiben"
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Argumente parsen
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
-p|--path)
|
||||||
|
N8N_PATH="$2"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
-b|--build)
|
||||||
|
BUILD_IN_CONTAINER=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-f|--force)
|
||||||
|
FORCE=true
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
print_error "Unbekannte Option: $1"
|
||||||
|
show_help
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================="
|
||||||
|
echo " LibreBooking n8n Node - Docker Setup"
|
||||||
|
echo "========================================="
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Voraussetzungen prüfen
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Prüfe Voraussetzungen..."
|
||||||
|
|
||||||
|
# Docker prüfen
|
||||||
|
if ! command -v docker &> /dev/null; then
|
||||||
|
print_error "Docker ist nicht installiert!"
|
||||||
|
echo " Bitte installieren Sie Docker: https://docs.docker.com/get-docker/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
print_success "Docker gefunden: $(docker --version)"
|
||||||
|
|
||||||
|
# Docker Compose prüfen - v2 bevorzugen
|
||||||
|
detect_compose_command() {
|
||||||
|
# Zuerst docker compose (v2/Plugin) prüfen - bevorzugt
|
||||||
|
if docker compose version &> /dev/null 2>&1; then
|
||||||
|
COMPOSE_CMD="docker compose"
|
||||||
|
COMPOSE_VERSION="v2"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dann docker-compose (v1) prüfen
|
||||||
|
if command -v docker-compose &> /dev/null; then
|
||||||
|
# Prüfen ob es tatsächlich funktioniert (distutils Problem bei Python 3.12)
|
||||||
|
if docker-compose --version &> /dev/null 2>&1; then
|
||||||
|
COMPOSE_CMD="docker-compose"
|
||||||
|
COMPOSE_VERSION="v1"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# docker-compose existiert aber funktioniert nicht
|
||||||
|
print_warning "docker-compose (v1) ist installiert, funktioniert aber nicht!"
|
||||||
|
print_warning "Dies liegt wahrscheinlich am fehlenden 'distutils' Modul (Python 3.12+)"
|
||||||
|
echo ""
|
||||||
|
echo " Mögliche Lösungen:"
|
||||||
|
echo " 1. Docker Compose v2 installieren (empfohlen):"
|
||||||
|
echo " sudo apt-get update && sudo apt-get install docker-compose-plugin"
|
||||||
|
echo ""
|
||||||
|
echo " 2. distutils für Python installieren (Workaround):"
|
||||||
|
echo " sudo apt-get install python3-distutils"
|
||||||
|
echo " # Oder für neuere Systeme:"
|
||||||
|
echo " pip3 install setuptools"
|
||||||
|
echo ""
|
||||||
|
echo " Siehe TROUBLESHOOTING.md für weitere Details."
|
||||||
|
echo ""
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Compose Command ermitteln
|
||||||
|
if detect_compose_command; then
|
||||||
|
if [ "$COMPOSE_VERSION" = "v2" ]; then
|
||||||
|
print_success "Docker Compose v2 (Plugin) gefunden: $(docker compose version --short 2>/dev/null || docker compose version)"
|
||||||
|
else
|
||||||
|
print_success "docker-compose v1 gefunden: $(docker-compose --version)"
|
||||||
|
print_warning "Empfehlung: Upgrade zu Docker Compose v2 für bessere Kompatibilität"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_error "Docker Compose ist nicht installiert oder funktioniert nicht!"
|
||||||
|
echo ""
|
||||||
|
echo " Installation von Docker Compose v2 (empfohlen):"
|
||||||
|
echo " sudo apt-get update && sudo apt-get install docker-compose-plugin"
|
||||||
|
echo ""
|
||||||
|
echo " Oder siehe: https://docs.docker.com/compose/install/"
|
||||||
|
echo ""
|
||||||
|
echo " Alternativ: Verwenden Sie install-docker-manual.sh für Installation ohne docker-compose"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Hilfsfunktion für Compose-Befehle
|
||||||
|
run_compose() {
|
||||||
|
$COMPOSE_CMD "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Funktion: Prüft ob ein Verzeichnis read-only ist
|
||||||
|
# =============================================================================
|
||||||
|
check_readonly() {
|
||||||
|
local dir="$1"
|
||||||
|
local test_file="$dir/.write_test_$$"
|
||||||
|
|
||||||
|
# Versuche eine Test-Datei zu erstellen
|
||||||
|
if touch "$test_file" 2>/dev/null; then
|
||||||
|
rm -f "$test_file" 2>/dev/null
|
||||||
|
return 0 # Schreibbar
|
||||||
|
else
|
||||||
|
return 1 # Read-only
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_readonly_warning() {
|
||||||
|
local dir="$1"
|
||||||
|
print_error "Das Verzeichnis '$dir' ist read-only!"
|
||||||
|
echo ""
|
||||||
|
echo " Das custom-nodes Verzeichnis benötigt Schreibrechte für:"
|
||||||
|
echo " - npm install (Dependencies)"
|
||||||
|
echo " - npm run build (Kompilierung)"
|
||||||
|
echo ""
|
||||||
|
echo " LÖSUNGEN:"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Volume OHNE :ro mounten (empfohlen):"
|
||||||
|
echo " volumes:"
|
||||||
|
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
|
||||||
|
echo " # NICHT: - ./custom-nodes:/...:ro"
|
||||||
|
echo ""
|
||||||
|
echo " 2. Auf dem Host bauen (für read-only Volumes):"
|
||||||
|
echo " ./build-on-host.sh"
|
||||||
|
echo " # Dann docker-compose.readonly.yml verwenden"
|
||||||
|
echo ""
|
||||||
|
echo " 3. Berechtigungen prüfen:"
|
||||||
|
echo " ls -la $dir"
|
||||||
|
echo " sudo chown -R 1000:1000 $dir"
|
||||||
|
echo ""
|
||||||
|
echo " Siehe: TROUBLESHOOTING.md und SECURITY.md"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Zielpfad prüfen
|
||||||
|
if [ ! -d "$N8N_PATH" ]; then
|
||||||
|
print_error "Verzeichnis existiert nicht: $N8N_PATH"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
N8N_PATH=$(cd "$N8N_PATH" && pwd)
|
||||||
|
print_info "Zielverzeichnis: $N8N_PATH"
|
||||||
|
|
||||||
|
# docker-compose.yml prüfen
|
||||||
|
if [ ! -f "$N8N_PATH/docker-compose.yml" ] && [ ! -f "$N8N_PATH/docker-compose.yaml" ]; then
|
||||||
|
print_warning "Keine docker-compose.yml gefunden in $N8N_PATH"
|
||||||
|
echo ""
|
||||||
|
read -p "Soll eine Beispiel-Konfiguration erstellt werden? (j/n): " CREATE_EXAMPLE
|
||||||
|
if [[ "$CREATE_EXAMPLE" =~ ^[jJyY]$ ]]; then
|
||||||
|
print_info "Kopiere Beispiel-Konfiguration..."
|
||||||
|
cp "$SCRIPT_DIR/docker-compose.example.yml" "$N8N_PATH/docker-compose.yml"
|
||||||
|
cp "$SCRIPT_DIR/.env.docker" "$N8N_PATH/.env"
|
||||||
|
print_success "Beispiel-Konfiguration erstellt"
|
||||||
|
print_warning "Bitte $N8N_PATH/.env anpassen!"
|
||||||
|
else
|
||||||
|
print_error "Abbruch: Keine docker-compose.yml vorhanden"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# n8n Status prüfen
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Prüfe n8n Container Status..."
|
||||||
|
|
||||||
|
cd "$N8N_PATH"
|
||||||
|
|
||||||
|
N8N_RUNNING=false
|
||||||
|
if $COMPOSE_CMD ps 2>/dev/null | grep -q "n8n.*Up"; then
|
||||||
|
N8N_RUNNING=true
|
||||||
|
print_success "n8n Container läuft"
|
||||||
|
else
|
||||||
|
print_warning "n8n Container läuft nicht (wird später gestartet)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Custom Nodes Verzeichnis vorbereiten
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Bereite Custom Nodes Verzeichnis vor..."
|
||||||
|
|
||||||
|
CUSTOM_NODES_DIR="$N8N_PATH/custom-nodes"
|
||||||
|
|
||||||
|
# Prüfen ob bereits installiert
|
||||||
|
if [ -d "$CUSTOM_NODES_DIR" ]; then
|
||||||
|
if [ "$FORCE" = true ]; then
|
||||||
|
print_warning "Bestehendes custom-nodes Verzeichnis wird überschrieben"
|
||||||
|
rm -rf "$CUSTOM_NODES_DIR"
|
||||||
|
else
|
||||||
|
print_warning "custom-nodes Verzeichnis existiert bereits"
|
||||||
|
read -p "Überschreiben? (j/n): " OVERWRITE
|
||||||
|
if [[ "$OVERWRITE" =~ ^[jJyY]$ ]]; then
|
||||||
|
rm -rf "$CUSTOM_NODES_DIR"
|
||||||
|
else
|
||||||
|
print_info "Behalte bestehendes Verzeichnis"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Custom Nodes kopieren
|
||||||
|
if [ ! -d "$CUSTOM_NODES_DIR" ]; then
|
||||||
|
cp -r "$SCRIPT_DIR/custom-nodes" "$CUSTOM_NODES_DIR"
|
||||||
|
print_success "Custom Nodes kopiert nach: $CUSTOM_NODES_DIR"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prüfe ob Verzeichnis schreibbar ist
|
||||||
|
if ! check_readonly "$CUSTOM_NODES_DIR"; then
|
||||||
|
print_readonly_warning "$CUSTOM_NODES_DIR"
|
||||||
|
echo ""
|
||||||
|
read -p "Trotzdem fortfahren? (j/n): " CONTINUE_RO
|
||||||
|
if [[ ! "$CONTINUE_RO" =~ ^[jJyY]$ ]]; then
|
||||||
|
print_error "Abbruch wegen read-only Verzeichnis"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
print_warning "Fortsetzen trotz read-only - npm install wird fehlschlagen!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Node bauen (wenn nicht bereits gebaut)
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
if [ ! -d "$CUSTOM_NODES_DIR/dist" ] || [ "$BUILD_IN_CONTAINER" = true ]; then
|
||||||
|
print_info "Baue LibreBooking Node..."
|
||||||
|
|
||||||
|
cd "$CUSTOM_NODES_DIR"
|
||||||
|
|
||||||
|
# Prüfen ob node/npm vorhanden
|
||||||
|
if command -v npm &> /dev/null; then
|
||||||
|
npm install 2>/dev/null || print_warning "npm install hatte Warnungen"
|
||||||
|
npm run build 2>/dev/null || {
|
||||||
|
print_warning "Build fehlgeschlagen, versuche alternativen Ansatz..."
|
||||||
|
# Manueller Build
|
||||||
|
npx tsc 2>/dev/null || true
|
||||||
|
mkdir -p dist/nodes/LibreBooking dist/nodes/LibreBookingTrigger
|
||||||
|
cp nodes/LibreBooking/*.svg dist/nodes/LibreBooking/ 2>/dev/null || true
|
||||||
|
cp nodes/LibreBookingTrigger/*.svg dist/nodes/LibreBookingTrigger/ 2>/dev/null || true
|
||||||
|
}
|
||||||
|
print_success "Node erfolgreich gebaut"
|
||||||
|
else
|
||||||
|
print_warning "npm nicht gefunden - Node wird beim Container-Start gebaut"
|
||||||
|
print_info "Erstelle Build-Skript für Container..."
|
||||||
|
cat > "$CUSTOM_NODES_DIR/build.sh" << 'BUILDEOF'
|
||||||
|
#!/bin/sh
|
||||||
|
cd /home/node/.n8n/custom/n8n-nodes-librebooking
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
BUILDEOF
|
||||||
|
chmod +x "$CUSTOM_NODES_DIR/build.sh"
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$N8N_PATH"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Docker Compose Override erstellen/aktualisieren
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Erstelle docker-compose.override.yml..."
|
||||||
|
|
||||||
|
OVERRIDE_FILE="$N8N_PATH/docker-compose.override.yml"
|
||||||
|
|
||||||
|
if [ -f "$OVERRIDE_FILE" ]; then
|
||||||
|
print_warning "docker-compose.override.yml existiert bereits"
|
||||||
|
|
||||||
|
# Prüfen ob LibreBooking bereits konfiguriert
|
||||||
|
if grep -q "n8n-nodes-librebooking" "$OVERRIDE_FILE"; then
|
||||||
|
print_success "LibreBooking bereits in override.yml konfiguriert"
|
||||||
|
else
|
||||||
|
print_info "Füge LibreBooking Konfiguration hinzu..."
|
||||||
|
# Backup erstellen
|
||||||
|
cp "$OVERRIDE_FILE" "${OVERRIDE_FILE}.backup"
|
||||||
|
|
||||||
|
# Hinzufügen (vereinfacht - für komplexe Fälle manuell anpassen)
|
||||||
|
cat >> "$OVERRIDE_FILE" << 'OVERRIDEEOF'
|
||||||
|
|
||||||
|
# LibreBooking Node (automatisch hinzugefügt)
|
||||||
|
# Falls Konflikte auftreten, bitte manuell anpassen
|
||||||
|
# Siehe DOCKER-INTEGRATION.md für Details
|
||||||
|
OVERRIDEEOF
|
||||||
|
print_warning "Bitte $OVERRIDE_FILE manuell prüfen!"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# Neue Override-Datei erstellen
|
||||||
|
cat > "$OVERRIDE_FILE" << 'OVERRIDEEOF'
|
||||||
|
# Docker Compose Override für LibreBooking n8n Node
|
||||||
|
# Automatisch generiert von install-docker.sh
|
||||||
|
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
n8n:
|
||||||
|
volumes:
|
||||||
|
# LibreBooking Custom Node
|
||||||
|
- ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro
|
||||||
|
|
||||||
|
environment:
|
||||||
|
# Custom Nodes aktivieren
|
||||||
|
- N8N_CUSTOM_EXTENSIONS=/home/node/.n8n/custom
|
||||||
|
- N8N_COMMUNITY_NODES_ENABLED=true
|
||||||
|
OVERRIDEEOF
|
||||||
|
print_success "docker-compose.override.yml erstellt"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Berechtigungen setzen
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
print_info "Setze Berechtigungen..."
|
||||||
|
|
||||||
|
# n8n läuft oft als node User mit UID 1000
|
||||||
|
if [ "$(id -u)" = "0" ]; then
|
||||||
|
chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || print_warning "Konnte Berechtigungen nicht setzen"
|
||||||
|
else
|
||||||
|
# Versuche mit sudo falls verfügbar
|
||||||
|
if command -v sudo &> /dev/null; then
|
||||||
|
sudo chown -R 1000:1000 "$CUSTOM_NODES_DIR" 2>/dev/null || print_warning "Konnte Berechtigungen nicht setzen (sudo fehlgeschlagen)"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_success "Berechtigungen konfiguriert"
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Container neustarten
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
read -p "Soll der n8n Container jetzt neu gestartet werden? (j/n): " RESTART
|
||||||
|
|
||||||
|
if [[ "$RESTART" =~ ^[jJyY]$ ]]; then
|
||||||
|
print_info "Starte n8n Container neu..."
|
||||||
|
|
||||||
|
if [ "$N8N_RUNNING" = true ]; then
|
||||||
|
$COMPOSE_CMD restart n8n 2>/dev/null || $COMPOSE_CMD restart
|
||||||
|
else
|
||||||
|
$COMPOSE_CMD up -d n8n 2>/dev/null || $COMPOSE_CMD up -d
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Warten bis Container bereit
|
||||||
|
print_info "Warte auf Container-Start..."
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
# Status prüfen
|
||||||
|
if $COMPOSE_CMD ps | grep -q "n8n.*Up"; then
|
||||||
|
print_success "n8n Container läuft!"
|
||||||
|
else
|
||||||
|
print_warning "Container-Status unklar - bitte manuell prüfen"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
print_info "Container nicht neu gestartet"
|
||||||
|
echo " Führen Sie später aus: cd $N8N_PATH && $COMPOSE_CMD restart"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Abschluss
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "========================================="
|
||||||
|
print_success "Installation abgeschlossen!"
|
||||||
|
echo "========================================="
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo "1. Öffnen Sie n8n in Ihrem Browser"
|
||||||
|
echo "2. Gehen Sie zu: Settings > Community Nodes"
|
||||||
|
echo "3. Der 'LibreBooking' Node sollte sichtbar sein"
|
||||||
|
echo "4. Erstellen Sie neue Credentials für LibreBooking"
|
||||||
|
echo ""
|
||||||
|
echo "Bei Problemen siehe: DOCKER-INTEGRATION.md"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# ============================================================================
|
||||||
|
# install-in-container.sh - Installiert den LibreBooking Node IM Docker Container
|
||||||
|
# Dieses Skript wird INNERHALB des Containers ausgeführt!
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "============================================"
|
||||||
|
echo " LibreBooking Node - Container Installation"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Funktion: Prüft ob ein Verzeichnis read-only ist
|
||||||
|
check_readonly() {
|
||||||
|
local dir="$1"
|
||||||
|
local test_file="$dir/.write_test_$$"
|
||||||
|
|
||||||
|
# Versuche eine Test-Datei zu erstellen
|
||||||
|
if touch "$test_file" 2>/dev/null; then
|
||||||
|
rm -f "$test_file" 2>/dev/null
|
||||||
|
return 0 # Schreibbar (exit code 0 = true)
|
||||||
|
else
|
||||||
|
return 1 # Read-only (exit code 1 = false)
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_readonly_error() {
|
||||||
|
local dir="$1"
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " FEHLER: Read-only Volume!"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Das Verzeichnis '$dir' ist read-only gemountet."
|
||||||
|
echo "npm install und npm run build benötigen Schreibrechte."
|
||||||
|
echo ""
|
||||||
|
echo "LÖSUNGEN:"
|
||||||
|
echo ""
|
||||||
|
echo "1. Volume OHNE :ro mounten:"
|
||||||
|
echo " In docker-compose.yml oder docker-compose.override.yml:"
|
||||||
|
echo ""
|
||||||
|
echo " volumes:"
|
||||||
|
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking"
|
||||||
|
echo " # NICHT:"
|
||||||
|
echo " - ./custom-nodes:/home/node/.n8n/custom/n8n-nodes-librebooking:ro"
|
||||||
|
echo ""
|
||||||
|
echo "2. Auf dem Host bauen:"
|
||||||
|
echo " Führen Sie auf dem HOST aus: ./build-on-host.sh"
|
||||||
|
echo " Dann verwenden Sie: docker-compose.readonly.yml"
|
||||||
|
echo ""
|
||||||
|
echo "Siehe: TROUBLESHOOTING.md"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
# Mögliche Pfade für custom-nodes
|
||||||
|
POSSIBLE_PATHS="
|
||||||
|
/home/node/.n8n/custom/n8n-nodes-librebooking
|
||||||
|
/home/node/.n8n/custom
|
||||||
|
/opt/n8n/custom-nodes
|
||||||
|
/data/custom-nodes
|
||||||
|
"
|
||||||
|
|
||||||
|
CUSTOM_NODE_PATH=""
|
||||||
|
|
||||||
|
# Finde den custom-nodes Pfad
|
||||||
|
echo "[1/5] Suche custom-nodes Verzeichnis..."
|
||||||
|
for path in $POSSIBLE_PATHS; do
|
||||||
|
if [ -d "$path" ]; then
|
||||||
|
# Prüfe ob package.json vorhanden ist
|
||||||
|
if [ -f "$path/package.json" ]; then
|
||||||
|
CUSTOM_NODE_PATH="$path"
|
||||||
|
echo " ✓ Gefunden: $path"
|
||||||
|
break
|
||||||
|
elif [ -f "$path/n8n-nodes-librebooking/package.json" ]; then
|
||||||
|
CUSTOM_NODE_PATH="$path/n8n-nodes-librebooking"
|
||||||
|
echo " ✓ Gefunden: $CUSTOM_NODE_PATH"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ -z "$CUSTOM_NODE_PATH" ]; then
|
||||||
|
echo " ✗ Kein custom-nodes Verzeichnis mit package.json gefunden!"
|
||||||
|
echo ""
|
||||||
|
echo " Geprüfte Pfade:"
|
||||||
|
for path in $POSSIBLE_PATHS; do
|
||||||
|
if [ -d "$path" ]; then
|
||||||
|
echo " - $path (existiert, aber keine package.json)"
|
||||||
|
else
|
||||||
|
echo " - $path (existiert nicht)"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
echo ""
|
||||||
|
echo " Bitte stellen Sie sicher, dass die Dateien korrekt kopiert wurden."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Wechsle ins Verzeichnis
|
||||||
|
echo "[2/5] Wechsle ins Verzeichnis: $CUSTOM_NODE_PATH"
|
||||||
|
cd "$CUSTOM_NODE_PATH"
|
||||||
|
echo " ✓ Aktuelles Verzeichnis: $(pwd)"
|
||||||
|
|
||||||
|
# Prüfe ob npm verfügbar ist
|
||||||
|
echo "[3/5] Prüfe npm..."
|
||||||
|
if ! command -v npm >/dev/null 2>&1; then
|
||||||
|
echo " ✗ npm ist nicht installiert!"
|
||||||
|
echo ""
|
||||||
|
echo " Im n8n Docker-Image sollte npm vorhanden sein."
|
||||||
|
echo " Falls nicht, verwenden Sie ein Image mit Node.js."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ✓ npm Version: $(npm --version)"
|
||||||
|
echo " ✓ node Version: $(node --version)"
|
||||||
|
|
||||||
|
# Prüfe Schreibrechte (read-only Volume?)
|
||||||
|
echo "[3.5/5] Prüfe Schreibrechte..."
|
||||||
|
if ! check_readonly "$CUSTOM_NODE_PATH"; then
|
||||||
|
print_readonly_error "$CUSTOM_NODE_PATH"
|
||||||
|
fi
|
||||||
|
echo " ✓ Schreibrechte vorhanden"
|
||||||
|
|
||||||
|
# Dependencies installieren
|
||||||
|
echo "[4/5] Installiere Dependencies..."
|
||||||
|
echo " Führe 'npm install' aus..."
|
||||||
|
if npm install 2>&1; then
|
||||||
|
echo " ✓ Dependencies installiert"
|
||||||
|
else
|
||||||
|
echo " ✗ npm install fehlgeschlagen!"
|
||||||
|
echo ""
|
||||||
|
echo " Mögliche Lösungen:"
|
||||||
|
echo " - Prüfen Sie die Berechtigungen"
|
||||||
|
echo " - Prüfen Sie die Internetverbindung"
|
||||||
|
echo " - Führen Sie 'npm cache clean --force' aus"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build ausführen
|
||||||
|
echo "[5/5] Baue den Node..."
|
||||||
|
echo " Führe 'npm run build' aus..."
|
||||||
|
if npm run build 2>&1; then
|
||||||
|
echo " ✓ Build erfolgreich"
|
||||||
|
else
|
||||||
|
echo " ✗ Build fehlgeschlagen!"
|
||||||
|
echo ""
|
||||||
|
echo " Prüfen Sie die TypeScript-Fehler in der Ausgabe oben."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prüfe Ergebnis
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " Prüfe Installation..."
|
||||||
|
echo "============================================"
|
||||||
|
|
||||||
|
if [ -d "$CUSTOM_NODE_PATH/dist" ]; then
|
||||||
|
echo "✓ dist/ Verzeichnis existiert"
|
||||||
|
|
||||||
|
# Prüfe auf .node.js Dateien
|
||||||
|
NODE_FILES=$(find "$CUSTOM_NODE_PATH/dist" -name "*.node.js" 2>/dev/null | wc -l)
|
||||||
|
if [ "$NODE_FILES" -gt 0 ]; then
|
||||||
|
echo "✓ $NODE_FILES Node-Datei(en) gefunden:"
|
||||||
|
find "$CUSTOM_NODE_PATH/dist" -name "*.node.js" -exec echo " - {}" \;
|
||||||
|
else
|
||||||
|
echo "✗ Keine .node.js Dateien im dist/ Verzeichnis gefunden!"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Prüfe credentials
|
||||||
|
CRED_FILES=$(find "$CUSTOM_NODE_PATH/dist" -name "*.credentials.js" 2>/dev/null | wc -l)
|
||||||
|
if [ "$CRED_FILES" -gt 0 ]; then
|
||||||
|
echo "✓ $CRED_FILES Credential-Datei(en) gefunden"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "✗ dist/ Verzeichnis wurde nicht erstellt!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "============================================"
|
||||||
|
echo " Installation abgeschlossen!"
|
||||||
|
echo "============================================"
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo " 1. Verlassen Sie den Container: exit"
|
||||||
|
echo " 2. Starten Sie n8n neu: docker restart n8n"
|
||||||
|
echo " 3. Öffnen Sie n8n im Browser und suchen Sie nach 'LibreBooking'"
|
||||||
|
echo ""
|
||||||
|
echo "Falls der Node nicht erscheint:"
|
||||||
|
echo " - Prüfen Sie die Umgebungsvariable N8N_CUSTOM_EXTENSIONS"
|
||||||
|
echo " - Führen Sie ./check-installation.sh aus"
|
||||||
|
echo ""
|
||||||
|
|
@ -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 ""
|
||||||
Binary file not shown.
Binary file not shown.
|
|
@ -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,90 @@
|
||||||
|
{
|
||||||
|
"name": "n8n-nodes-librebooking",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"description": "n8n Node für LibreBooking - Ressourcen- und Reservierungsverwaltung",
|
||||||
|
"keywords": [
|
||||||
|
"n8n-community-node-package",
|
||||||
|
"n8n",
|
||||||
|
"n8n-node",
|
||||||
|
"workflow",
|
||||||
|
"automation",
|
||||||
|
"librebooking",
|
||||||
|
"booking",
|
||||||
|
"reservation",
|
||||||
|
"resource-management",
|
||||||
|
"room-booking",
|
||||||
|
"raumbuchung",
|
||||||
|
"terminbuchung",
|
||||||
|
"open-source"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"homepage": "https://github.com/your-org/n8n-nodes-librebooking#readme",
|
||||||
|
"author": {
|
||||||
|
"name": "LibreBooking n8n Integration",
|
||||||
|
"email": "support@example.com"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/your-org/n8n-nodes-librebooking.git"
|
||||||
|
},
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/your-org/n8n-nodes-librebooking/issues"
|
||||||
|
},
|
||||||
|
"main": "dist/index.js",
|
||||||
|
"types": "dist/index.d.ts",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc && npm run copy:icons",
|
||||||
|
"copy:icons": "cp nodes/LibreBooking/librebooking.svg dist/nodes/LibreBooking/ && cp nodes/LibreBookingTrigger/librebooking.svg dist/nodes/LibreBookingTrigger/",
|
||||||
|
"dev": "tsc --watch",
|
||||||
|
"clean": "rm -rf dist node_modules",
|
||||||
|
"rebuild": "npm run clean && npm install && npm run build",
|
||||||
|
"format": "prettier nodes credentials --write",
|
||||||
|
"lint": "eslint nodes credentials --ext .ts",
|
||||||
|
"lint:fix": "eslint nodes credentials --ext .ts --fix",
|
||||||
|
"prepack": "npm run build",
|
||||||
|
"prepublishOnly": "npm run lint && npm run build",
|
||||||
|
"postinstall": "echo 'Installation abgeschlossen. Führe npm run build aus.'",
|
||||||
|
"test": "ts-node test/test-api.ts",
|
||||||
|
"link:n8n": "npm link && echo 'Node verlinkt. Starte n8n neu mit: n8n start'",
|
||||||
|
"unlink": "npm unlink -g n8n-nodes-librebooking",
|
||||||
|
"install-in-docker": "npm install && npm run build && echo '\\n✓ Installation abgeschlossen. Bitte n8n Container neustarten: docker restart n8n'",
|
||||||
|
"docker:copy": "docker cp dist n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/ && docker cp package.json n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/ && docker cp node_modules n8n:/home/node/.n8n/custom/n8n-nodes-librebooking/",
|
||||||
|
"docker:restart": "docker restart n8n",
|
||||||
|
"docker:deploy": "npm run build && npm run docker:copy && npm run docker:restart"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist",
|
||||||
|
"README.md",
|
||||||
|
"LICENSE"
|
||||||
|
],
|
||||||
|
"n8n": {
|
||||||
|
"n8nNodesApiVersion": 1,
|
||||||
|
"credentials": [
|
||||||
|
"dist/credentials/LibreBookingApi.credentials.js"
|
||||||
|
],
|
||||||
|
"nodes": [
|
||||||
|
"dist/nodes/LibreBooking/LibreBooking.node.js",
|
||||||
|
"dist/nodes/LibreBookingTrigger/LibreBookingTrigger.node.js"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/node": "^20.10.0",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^6.13.0",
|
||||||
|
"@typescript-eslint/parser": "^6.13.0",
|
||||||
|
"eslint": "^8.54.0",
|
||||||
|
"n8n-workflow": "^1.20.0",
|
||||||
|
"prettier": "^3.1.0",
|
||||||
|
"ts-node": "^10.9.2",
|
||||||
|
"typescript": "^5.3.2"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"n8n-workflow": "*"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18.17.0"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"form-data": "^4.0.1",
|
||||||
|
"lodash": "^4.17.21"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# quick-install.sh - Schnellste Installation des LibreBooking n8n Nodes
|
||||||
|
#
|
||||||
|
# EMPFOHLENE METHODE: Auf dem Host bauen, in Container kopieren
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
CONTAINER_NAME="${1:-n8n}"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== LibreBooking Quick Install ===${NC}\n"
|
||||||
|
|
||||||
|
# Prüfe Voraussetzungen
|
||||||
|
if ! command -v node &>/dev/null; then
|
||||||
|
echo -e "${RED}Fehler: Node.js nicht installiert!${NC}"
|
||||||
|
echo "Installieren: https://nodejs.org/ (v18+)"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v docker &>/dev/null; then
|
||||||
|
echo -e "${RED}Fehler: Docker nicht installiert!${NC}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! docker ps | grep -q "$CONTAINER_NAME"; then
|
||||||
|
echo -e "${YELLOW}Warnung: Container '$CONTAINER_NAME' nicht gefunden oder läuft nicht.${NC}"
|
||||||
|
echo "Verfügbare Container:"
|
||||||
|
docker ps --format " {{.Names}}"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# 1. Dependencies installieren
|
||||||
|
echo "[1/4] Installiere Dependencies..."
|
||||||
|
npm install --silent
|
||||||
|
|
||||||
|
# 2. Bauen
|
||||||
|
echo "[2/4] Baue Node..."
|
||||||
|
npm run build --silent
|
||||||
|
|
||||||
|
# 3. In Container kopieren
|
||||||
|
echo "[3/4] Kopiere in Container '$CONTAINER_NAME'..."
|
||||||
|
|
||||||
|
# Erstelle Zielverzeichnis falls nötig
|
||||||
|
docker exec "$CONTAINER_NAME" mkdir -p /home/node/.n8n/custom/n8n-nodes-librebooking 2>/dev/null || true
|
||||||
|
|
||||||
|
# Kopiere Dateien
|
||||||
|
docker cp dist "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp package.json "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp node_modules "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# 4. Container neustarten
|
||||||
|
echo "[4/4] Starte Container neu..."
|
||||||
|
docker restart "$CONTAINER_NAME"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓ Installation abgeschlossen!${NC}"
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo " 1. Öffne n8n: http://localhost:5678"
|
||||||
|
echo " 2. Gehe zu: Einstellungen → Credentials → Add Credential"
|
||||||
|
echo " 3. Suche: 'LibreBooking API'"
|
||||||
|
echo ""
|
||||||
|
|
@ -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,112 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# update-dependencies.sh - Aktualisiert Dependencies und behebt Vulnerabilities
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
# Farben
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo -e "${BLUE} LibreBooking Node - Dependency Update${NC}"
|
||||||
|
echo -e "${BLUE}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
log() {
|
||||||
|
echo -e "${GREEN}[INFO]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
warn() {
|
||||||
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}[ERROR]${NC} $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# Schritt 1: Aktuelle Vulnerabilities anzeigen
|
||||||
|
log "Prüfe aktuelle Vulnerabilities..."
|
||||||
|
echo ""
|
||||||
|
echo "=== VOR dem Update ==="
|
||||||
|
npm audit 2>/dev/null || true
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Schritt 2: npm audit fix
|
||||||
|
log "Führe npm audit fix aus..."
|
||||||
|
if npm audit fix 2>&1; then
|
||||||
|
log "npm audit fix erfolgreich ✓"
|
||||||
|
else
|
||||||
|
warn "npm audit fix hatte Probleme (evtl. normale Warnungen)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 3: npm update
|
||||||
|
log "Führe npm update aus..."
|
||||||
|
if npm update 2>&1; then
|
||||||
|
log "npm update erfolgreich ✓"
|
||||||
|
else
|
||||||
|
warn "npm update hatte Probleme"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 4: Outdated Packages prüfen
|
||||||
|
log "Prüfe veraltete Packages..."
|
||||||
|
echo ""
|
||||||
|
npm outdated 2>/dev/null || log "Alle Packages sind aktuell ✓"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Schritt 5: Build testen
|
||||||
|
log "Teste Build..."
|
||||||
|
if npm run build 2>&1 | tail -5; then
|
||||||
|
log "Build erfolgreich ✓"
|
||||||
|
else
|
||||||
|
error "Build fehlgeschlagen!"
|
||||||
|
echo ""
|
||||||
|
echo "Versuchen Sie:"
|
||||||
|
echo " 1. rm -rf node_modules package-lock.json"
|
||||||
|
echo " 2. npm install"
|
||||||
|
echo " 3. npm run build"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Schritt 6: Finale Vulnerability-Prüfung
|
||||||
|
log "Finale Vulnerability-Prüfung..."
|
||||||
|
echo ""
|
||||||
|
echo "=== NACH dem Update ==="
|
||||||
|
npm audit 2>/dev/null || true
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Schritt 7: Report
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo -e "${GREEN} Update abgeschlossen!${NC}"
|
||||||
|
echo -e "${GREEN}============================================${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Verbleibende Vulnerabilities zählen
|
||||||
|
AUDIT_OUTPUT=$(npm audit 2>/dev/null || true)
|
||||||
|
if echo "$AUDIT_OUTPUT" | grep -q "found 0 vulnerabilities"; then
|
||||||
|
log "Keine Vulnerabilities mehr! ✓"
|
||||||
|
else
|
||||||
|
VULN_COUNT=$(echo "$AUDIT_OUTPUT" | grep -oP '\d+ vulnerabilities' | head -1 || echo "Einige")
|
||||||
|
warn "Verbleibende Vulnerabilities: $VULN_COUNT"
|
||||||
|
echo ""
|
||||||
|
echo "Diese kommen wahrscheinlich von n8n-workflow Dependencies."
|
||||||
|
echo "Siehe SECURITY.md für weitere Informationen."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Nächste Schritte:"
|
||||||
|
echo " 1. Testen Sie die Änderungen lokal"
|
||||||
|
echo " 2. Committen Sie die Änderungen:"
|
||||||
|
echo " git add package.json package-lock.json"
|
||||||
|
echo " git commit -m 'chore: update dependencies'"
|
||||||
|
echo " 3. Bauen Sie das Docker-Image neu:"
|
||||||
|
echo " docker compose build --no-cache"
|
||||||
|
echo ""
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# update-node.sh - Aktualisiert den LibreBooking n8n Node
|
||||||
|
#
|
||||||
|
# Verwendung nach git pull oder Änderungen am Code
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
CONTAINER_NAME="${1:-n8n}"
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
echo -e "${GREEN}=== LibreBooking Node Update ===${NC}\n"
|
||||||
|
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
|
||||||
|
# Git Pull (optional)
|
||||||
|
if [ -d ".git" ]; then
|
||||||
|
echo "[1/5] Hole neueste Änderungen..."
|
||||||
|
git pull 2>/dev/null || echo -e "${YELLOW}Git pull übersprungen${NC}"
|
||||||
|
else
|
||||||
|
echo "[1/5] Kein Git Repository - übersprungen"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Dependencies aktualisieren
|
||||||
|
echo "[2/5] Aktualisiere Dependencies..."
|
||||||
|
npm install --silent
|
||||||
|
|
||||||
|
# Bauen
|
||||||
|
echo "[3/5] Baue Node..."
|
||||||
|
npm run build --silent
|
||||||
|
|
||||||
|
# Prüfe Container
|
||||||
|
if ! docker ps | grep -q "$CONTAINER_NAME"; then
|
||||||
|
echo -e "${YELLOW}Container '$CONTAINER_NAME' nicht gefunden.${NC}"
|
||||||
|
echo "Verfügbare Container:"
|
||||||
|
docker ps --format " {{.Names}}"
|
||||||
|
echo ""
|
||||||
|
echo "Manuell kopieren:"
|
||||||
|
echo " docker cp dist <container>:/home/node/.n8n/custom/n8n-nodes-librebooking/"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# In Container kopieren
|
||||||
|
echo "[4/5] Kopiere in Container '$CONTAINER_NAME'..."
|
||||||
|
docker cp dist "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp package.json "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
docker cp node_modules "$CONTAINER_NAME":/home/node/.n8n/custom/n8n-nodes-librebooking/
|
||||||
|
|
||||||
|
# Container neustarten
|
||||||
|
echo "[5/5] Starte Container neu..."
|
||||||
|
docker restart "$CONTAINER_NAME"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${GREEN}✓ Update abgeschlossen!${NC}"
|
||||||
|
echo ""
|
||||||
|
|
@ -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