feat: Add complete BotKonzept SaaS platform

- Landing page with registration form (HTML/CSS/JS)
- n8n workflows for customer registration and trial management
- PostgreSQL schema for customer/instance/payment management
- Automated email system (Day 3, 5, 7 with discounts)
- Setup script and deployment checklist
- Comprehensive documentation

Features:
- Automatic LXC instance creation per customer
- 7-day trial with automated upgrade offers
- Discount system: 30% → 15% → regular price
- Supabase integration for customer management
- Email automation via Postfix/SES
- GDPR compliant (data in Germany)
- Stripe/PayPal payment integration ready

Components:
- botkonzept-website/ - Landing page and registration
- BotKonzept-Customer-Registration-Workflow.json - n8n registration workflow
- BotKonzept-Trial-Management-Workflow.json - n8n trial management workflow
- sql/botkonzept_schema.sql - Complete database schema
- setup_botkonzept.sh - Automated setup script
- BOTKONZEPT_README.md - Full documentation
- DEPLOYMENT_CHECKLIST.md - Deployment guide
This commit is contained in:
2026-01-25 19:30:54 +01:00
parent 610a4d9e0e
commit caa38bf72c
11 changed files with 4063 additions and 0 deletions

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="359px" height="60px" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 2150 359"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
<![CDATA[
.fil1 {fill:#FEFEFE;fill-rule:nonzero}
.fil4 {fill:#88CED7;fill-rule:nonzero}
.fil3 {fill:#4FC0EF;fill-rule:nonzero}
.fil2 {fill:#3A7ABD;fill-rule:nonzero}
.fil0 {fill:#234182;fill-rule:nonzero}
]]>
</style>
</defs>
<g id="Bot_x0020_TV">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g id="_1548485603296">
<g>
<path class="fil0" d="M204 168l0 22c0,8 5,14 12,14 3,0 6,-2 9,-4 2,-3 4,-6 4,-10l0 -22c0,-8 -5,-14 -12,-14 -3,0 -6,2 -9,4 -2,3 -4,6 -4,10z"/>
</g>
<g>
<path class="fil0" d="M142 168l0 22c0,8 5,14 12,14 3,0 6,-2 9,-4 2,-3 4,-6 4,-10l0 -22c0,-8 -5,-14 -12,-14 -3,0 -6,2 -9,4 -2,3 -4,6 -4,10z"/>
</g>
</g>
<g id="_1548485603200">
<g>
<path class="fil1" d="M209 192l2 0c1,0 1,0 1,-1l0 -24c0,-1 0,-1 -1,-1l-2 0c-1,0 -1,0 -1,1l0 24c0,1 0,1 1,1z"/>
</g>
<g>
<path class="fil1" d="M147 192l2 0c1,0 1,0 1,-1l0 -24c0,-1 0,-1 -1,-1l-2 0c-1,0 -1,0 -1,1l0 24c0,1 0,1 1,1z"/>
</g>
</g>
<path class="fil0" d="M119 24l0 32 -38 0 0 -32c0,-10 8,-19 19,-19 10,0 19,8 19,19zm221 259l-221 0 0 56c0,10 -8,19 -18,20 -11,1 -20,-8 -20,-19l0 -219 0 -8 47 0c-5,0 -8,3 -9,8 0,1 0,1 0,2l0 113c0,5 4,9 9,9l211 0c10,0 19,8 20,18 1,11 -8,20 -19,20z"/>
<g id="_1548485605696">
<path class="fil2" d="M251 302l38 0 0 38c0,10 -8,19 -19,19 -10,0 -19,-8 -19,-19l0 -38z"/>
</g>
<g id="_1548485607712">
<path class="fil3" d="M359 94c0,10 -8,19 -19,19l-32 0 0 -38 32 0c10,0 19,8 19,19z"/>
</g>
<g id="_1548485606320">
<path class="fil4" d="M19 245l43 0 0 38 -43 0c-10,0 -19,-8 -19,-19 0,-10 8,-19 19,-19z"/>
</g>
<path class="fil2" d="M251 237l0 -2 0 -112c0,-5 -4,-9 -9,-9l-222 0c-10,0 -19,-8 -20,-18 -1,-11 8,-20 19,-20l232 0 0 -55c0,-11 9,-20 20,-19 10,1 18,9 18,20l0 217 0 8 -47 0c4,0 8,-3 9,-8 0,0 0,-1 0,-1z"/>
<path class="fil0" d="M391 213l0 -212 90 0c40,0 60,16 60,49 0,24 -13,41 -39,52 28,5 42,21 42,48 0,42 -22,63 -66,63l-87 0zm86 -22c25,0 38,-13 38,-38 0,-25 -17,-37 -51,-37l-12 0 0 -18c40,-5 61,-20 61,-44 0,-20 -11,-30 -34,-30l-60 0 0 167 58 0z"/>
<path id="_1" class="fil0" d="M609 107c0,57 26,86 77,86 51,0 76,-29 76,-86 0,-56 -25,-85 -76,-85 -52,0 -77,28 -77,85zm-29 1c0,-72 35,-108 106,-108 70,0 105,36 105,108 0,71 -35,107 -105,107 -71,0 -106,-36 -106,-107z"/>
<polygon id="_2" class="fil0" points="977,1 977,24 910,24 910,213 883,213 883,24 816,24 816,1 "/>
<path id="_3" class="fil0" d="M1037 1l0 212 -27 0 0 -212 27 0zm149 0l-95 100 97 112 -38 0 -86 -102 0 -17 88 -93 35 0z"/>
<path id="_4" class="fil0" d="M1216 107c0,57 26,86 77,86 51,0 76,-29 76,-86 0,-56 -25,-85 -76,-85 -52,0 -77,28 -77,85zm-29 1c0,-72 35,-108 106,-108 70,0 105,36 105,108 0,71 -35,107 -105,107 -71,0 -106,-36 -106,-107z"/>
<polygon id="_5" class="fil0" points="1594,1 1594,24 1468,191 1592,191 1592,213 1435,213 1435,191 1565,24 1438,24 1438,1 "/>
<polygon id="_6" class="fil0" points="1777,1 1777,24 1666,24 1666,95 1771,95 1771,117 1666,117 1666,191 1779,191 1779,213 1638,213 1638,1 "/>
<path id="_7" class="fil0" d="M1820 213l0 -212 86 0c43,0 65,19 65,57 0,41 -30,66 -89,75l-6 -23c43,-6 65,-23 65,-51 0,-24 -13,-35 -38,-35l-56 0 0 189 -27 0z"/>
<polygon id="_8" class="fil0" points="2150,1 2150,24 2083,24 2083,213 2056,213 2056,24 1989,24 1989,1 "/>
<polygon class="fil2" points="391,359 391,254 411,254 446,333 481,254 500,254 500,359 482,359 482,286 454,359 437,359 409,286 409,359 "/>
<polygon id="_1_8" class="fil2" points="593,254 593,271 541,271 541,297 591,297 591,315 541,315 541,342 594,342 594,359 522,359 522,254 "/>
<path id="_2_9" class="fil2" d="M653 342c22,0 32,-13 32,-38 0,-22 -11,-32 -32,-32l-21 0 0 71 21 0zm-41 17l0 -104 41 0c34,0 52,16 52,49 0,37 -17,55 -52,55l-41 0z"/>
<polygon id="_3_10" class="fil2" points="740,254 740,359 721,359 721,254 "/>
<polygon id="_4_11" class="fil2" points="773,359 753,359 797,254 818,254 863,359 842,359 830,330 796,330 802,313 823,313 807,274 "/>
<polygon id="_5_12" class="fil2" points="876,359 876,254 896,254 931,333 966,254 985,254 985,359 967,359 967,286 939,359 922,359 893,286 893,359 "/>
<polygon id="_6_13" class="fil2" points="1078,254 1078,271 1026,271 1026,297 1076,297 1076,315 1026,315 1026,342 1079,342 1079,359 1007,359 1007,254 "/>
<polygon id="_7_14" class="fil2" points="1165,254 1165,271 1135,271 1135,359 1116,359 1116,271 1086,271 1086,254 "/>
<polygon id="_8_15" class="fil2" points="1251,254 1251,271 1196,342 1251,342 1251,359 1174,359 1174,342 1232,271 1176,271 1176,254 "/>
<polygon id="_9" class="fil2" points="1332,359 1332,254 1352,254 1407,330 1407,254 1425,254 1425,359 1406,359 1351,281 1351,359 "/>
<polygon id="_10" class="fil2" points="1518,254 1518,271 1466,271 1466,297 1516,297 1516,315 1466,315 1466,342 1519,342 1519,359 1447,359 1447,254 "/>
<polygon id="_11" class="fil2" points="1605,254 1605,271 1576,271 1576,359 1556,359 1556,271 1526,271 1526,254 "/>
<polygon id="_12" class="fil2" points="1608,254 1628,254 1639,329 1666,254 1684,254 1710,326 1721,254 1740,254 1721,359 1703,359 1674,277 1643,359 1625,359 "/>
<path id="_13" class="fil2" d="M1770 306c0,24 11,36 32,36 21,0 31,-12 31,-36 0,-24 -10,-36 -31,-36 -22,0 -32,12 -32,36zm-20 0c0,-35 17,-53 52,-53 34,0 51,18 51,53 0,35 -17,53 -51,53 -33,0 -51,-18 -52,-53z"/>
<path id="_14" class="fil2" d="M1872 358l0 -104 47 0c21,0 32,9 32,28 0,13 -8,23 -25,31l33 45 -24 0 -32 -45 0 -9c18,-3 28,-10 28,-21 0,-8 -4,-12 -13,-12l-26 0 0 87 -20 0z"/>
<path id="_15" class="fil2" d="M1988 254l0 104 -19 0 0 -104 19 0zm73 0l-44 49 49 56 -27 0 -40 -49 0 -13 39 -43 24 0z"/>
<path id="_16" class="fil2" d="M2071 354l0 -17c10,4 22,6 35,6 16,0 24,-5 24,-16 0,-8 -5,-12 -15,-12l-16 0c-21,0 -32,-10 -32,-29 0,-21 15,-32 46,-32 12,0 23,2 33,5l0 17c-10,-4 -21,-6 -33,-6 -17,0 -26,5 -26,15 0,8 4,12 13,12l16 0c23,0 34,10 34,29 0,22 -14,33 -43,33 -13,0 -25,-2 -35,-5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB

434
BOTKONZEPT_README.md Normal file
View File

@@ -0,0 +1,434 @@
# 🤖 BotKonzept - SaaS Platform für KI-Chatbots
## 📋 Übersicht
BotKonzept ist eine vollständige SaaS-Plattform für KI-Chatbots mit automatischer Kundenregistrierung, Trial-Management und E-Mail-Automation.
### Hauptfunktionen
-**Automatische Kundenregistrierung** über Website
-**Automatische LXC-Instanz-Erstellung** für jeden Kunden
-**7-Tage-Trial** mit automatischen Upgrade-Angeboten
-**E-Mail-Automation** (Tag 3, 5, 7)
-**Rabatt-System** (30% → 15% → Normalpreis)
-**Supabase-Integration** für Kunden-Management
-**Stripe/PayPal** Payment-Integration
-**DSGVO-konform** (Daten in Deutschland)
## 🏗️ Architektur
```
┌─────────────────────────────────────────────────────────────┐
│ BotKonzept Platform │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ │
│ │ Website │─────▶│ n8n Webhook │─────▶│ PVE20 │ │
│ │ botkonzept.de│ │ Registration │ │ install.sh│ │
│ └──────────────┘ └──────────────┘ └───────────┘ │
│ │ │ │ │
│ │ ▼ ▼ │
│ │ ┌──────────────┐ ┌───────────┐ │
│ │ │ Supabase │ │ LXC (CTID)│ │
│ │ │ PostgreSQL │ │ n8n │ │
│ │ │ Customers │ │ PostgREST│ │
│ │ │ Instances │ │ Postgres │ │
│ │ └──────────────┘ └───────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ Trial Mgmt │ │ Email Auto │ │
│ │ Workflow │─────▶│ Day 3,5,7 │ │
│ │ (Cron Daily) │ │ Postfix/SES │ │
│ └──────────────┘ └──────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
## 📁 Projekt-Struktur
```
customer-installer/
├── botkonzept-website/ # Landing Page & Registrierung
│ ├── index.html # Hauptseite
│ ├── css/style.css # Styling
│ └── js/main.js # JavaScript (Form-Handling)
├── sql/
│ ├── botkonzept_schema.sql # Datenbank-Schema
│ └── init_pgvector.sql # Vector-DB für RAG
├── BotKonzept-Customer-Registration-Workflow.json
│ # n8n Workflow für Registrierung
├── BotKonzept-Trial-Management-Workflow.json
│ # n8n Workflow für Trial-Management
├── install.sh # LXC-Installation
├── libsupabase.sh # Helper-Funktionen
├── setup_nginx_proxy.sh # NGINX Reverse Proxy
└── BOTKONZEPT_README.md # Diese Datei
```
## 🚀 Installation & Setup
### 1. Datenbank einrichten
```bash
# Supabase PostgreSQL Schema erstellen
psql -U postgres -d customer < sql/botkonzept_schema.sql
```
### 2. n8n Workflows importieren
1. Öffnen Sie n8n: `https://n8n.userman.de`
2. Importieren Sie die Workflows:
- `BotKonzept-Customer-Registration-Workflow.json`
- `BotKonzept-Trial-Management-Workflow.json`
3. Konfigurieren Sie die Credentials:
- **SSH (PVE20):** Private Key für Proxmox
- **PostgreSQL (Supabase):** Lokale Supabase-Instanz
- **SMTP (Postfix/SES):** E-Mail-Versand
### 3. Website deployen
```bash
# Website-Dateien auf Webserver kopieren
cd botkonzept-website
rsync -avz . user@botkonzept.de:/var/www/botkonzept/
# Oder lokal testen
python3 -m http.server 8000
# Öffnen: http://localhost:8000
```
### 4. Webhook-URL konfigurieren
In `botkonzept-website/js/main.js`:
```javascript
const CONFIG = {
WEBHOOK_URL: 'https://n8n.userman.de/webhook/botkonzept-registration',
// ...
};
```
## 📊 Customer Journey
### Tag 0: Registrierung
1. **Kunde registriert sich** auf botkonzept.de
2. **n8n Webhook** empfängt Daten
3. **Validierung** der Eingaben
4. **Passwort generieren** (16 Zeichen)
5. **Kunde in DB speichern** (Supabase)
6. **LXC-Instanz erstellen** via `install.sh`
7. **Instanz-Daten speichern** in DB
8. **Willkommens-E-Mail** senden mit Zugangsdaten
**E-Mail-Inhalt:**
- Dashboard-URL
- Login-Daten
- Chat-Webhook-URL
- Upload-Formular-URL
- Quick-Start-Guide
### Tag 3: Frühbucher-Angebot
**Automatisch um 9:00 Uhr:**
- **E-Mail:** "30% Frühbucher-Rabatt"
- **Preis:** €34,30/Monat (statt €49)
- **Ersparnis:** €176,40/Jahr
- **Gültigkeit:** 48 Stunden
### Tag 5: Erinnerung
**Automatisch um 9:00 Uhr:**
- **E-Mail:** "Nur noch 2 Tage - 15% Rabatt"
- **Preis:** €41,65/Monat (statt €49)
- **Ersparnis:** €88,20/Jahr
- **Warnung:** Instanz wird bald gelöscht
### Tag 7: Letzte Chance
**Automatisch um 9:00 Uhr:**
- **E-Mail:** "Trial endet heute"
- **Preis:** €49/Monat (Normalpreis)
- **Keine Rabatte** mehr verfügbar
- **Dringlichkeit:** Instanz wird um Mitternacht gelöscht
### Tag 8: Instanz löschen
**Automatisch um 9:00 Uhr:**
- **LXC-Instanz löschen** via `pct destroy`
- **Status aktualisieren** in DB
- **Goodbye-E-Mail** mit Feedback-Umfrage
## 💰 Preis-Modell
### Trial (7 Tage)
- **Preis:** €0
- **Features:** Voller Funktionsumfang
- **Limit:** 100 Dokumente, 1.000 Nachrichten
### Starter
- **Normalpreis:** €49/Monat
- **Tag 3 Rabatt:** €34,30/Monat (30% OFF)
- **Tag 5 Rabatt:** €41,65/Monat (15% OFF)
- **Features:**
- Unbegrenzte Dokumente
- 10.000 Nachrichten/Monat
- Prioritäts-Support
- Custom Branding
- Analytics Dashboard
### Business
- **Preis:** €149/Monat
- **Features:**
- 50.000 Nachrichten/Monat
- Mehrere Chatbots
- API-Zugriff
- Dedizierter Support
- SLA-Garantie
## 🔧 Technische Details
### Datenbank-Schema
**Haupttabellen:**
- `customers` - Kundendaten
- `instances` - LXC-Instanzen
- `subscriptions` - Abonnements
- `payments` - Zahlungen
- `emails_sent` - E-Mail-Tracking
- `usage_stats` - Nutzungsstatistiken
- `audit_log` - Audit-Trail
### n8n Workflows
#### 1. Customer Registration Workflow
**Trigger:** Webhook (POST /webhook/botkonzept-registration)
**Schritte:**
1. Validate Input
2. Generate Password & Trial Date
3. Create Customer in DB
4. Create Customer Instance (SSH)
5. Parse Install Output
6. Save Instance to DB
7. Send Welcome Email
8. Log Email Sent
9. Success Response
#### 2. Trial Management Workflow
**Trigger:** Cron (täglich 9:00 Uhr)
**Schritte:**
1. Get Trial Customers (SQL Query)
2. Check Day 3/5/7/8
3. Send entsprechende E-Mail
4. Log Email Sent
5. (Tag 8) Delete Instance
### E-Mail-Templates
Alle E-Mails sind:
-**Responsive** (Mobile-optimiert)
-**HTML-formatiert** mit Inline-CSS
-**Branded** mit Logo und Farben
-**CTA-optimiert** mit klaren Buttons
-**Tracking-fähig** (Opens, Clicks)
### Security
-**HTTPS** für alle Verbindungen
-**JWT-Tokens** für API-Authentifizierung
-**Row Level Security** in Supabase
-**Passwort-Hashing** (bcrypt)
-**DSGVO-konform** (Daten in DE)
-**Input-Validierung** auf allen Ebenen
## 📧 E-Mail-Konfiguration
### Postfix Gateway (OPNsense)
```bash
# SMTP-Server: 192.168.45.1
# Port: 25 (intern)
# Relay: Amazon SES
```
### Sendy.co Integration (optional)
Für Newsletter und Marketing-E-Mails:
```javascript
// In js/main.js
function subscribeNewsletter(email) {
const sendyUrl = 'https://sendy.userman.de/subscribe';
// ...
}
```
## 💳 Payment-Integration
### Stripe
```javascript
// Stripe Checkout Session erstellen
const session = await stripe.checkout.sessions.create({
customer_email: customer.email,
line_items: [{
price: 'price_starter_monthly',
quantity: 1,
}],
mode: 'subscription',
success_url: 'https://botkonzept.de/success',
cancel_url: 'https://botkonzept.de/cancel',
});
```
### PayPal
```javascript
// PayPal Subscription erstellen
paypal.Buttons({
createSubscription: function(data, actions) {
return actions.subscription.create({
plan_id: 'P-STARTER-MONTHLY'
});
}
}).render('#paypal-button-container');
```
## 📈 Analytics & Tracking
### Google Analytics
```html
<!-- In index.html -->
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
```
### Conversion Tracking
```javascript
// In js/main.js
function trackConversion(eventName, data) {
gtag('event', eventName, {
'event_category': 'registration',
'event_label': 'trial',
'value': 0
});
}
```
## 🧪 Testing
### Lokales Testing
```bash
# Website lokal testen
cd botkonzept-website
python3 -m http.server 8000
# n8n Workflow testen
curl -X POST https://n8n.userman.de/webhook/botkonzept-registration \
-H "Content-Type: application/json" \
-d '{
"firstName": "Max",
"lastName": "Mustermann",
"email": "test@example.com",
"company": "Test GmbH"
}'
```
### Datenbank-Queries
```sql
-- Alle Trial-Kunden anzeigen
SELECT * FROM customer_overview WHERE status = 'trial';
-- E-Mails der letzten 7 Tage
SELECT * FROM emails_sent WHERE sent_at >= NOW() - INTERVAL '7 days';
-- Trials die bald ablaufen
SELECT * FROM trials_expiring_soon;
-- Revenue-Übersicht
SELECT * FROM revenue_metrics;
```
## 🔄 Workflow-Verbesserungen
### Vorschläge für Erweiterungen
1. **A/B Testing**
- Verschiedene E-Mail-Varianten testen
- Conversion-Rates vergleichen
2. **Personalisierung**
- Branchen-spezifische E-Mails
- Nutzungsbasierte Empfehlungen
3. **Retargeting**
- Abgebrochene Registrierungen
- Reaktivierung inaktiver Kunden
4. **Referral-Programm**
- Kunden werben Kunden
- Rabatte für Empfehlungen
5. **Upselling**
- Automatische Upgrade-Vorschläge
- Feature-basierte Empfehlungen
## 📞 Support & Kontakt
- **Website:** https://botkonzept.de
- **E-Mail:** support@botkonzept.de
- **Dokumentation:** https://docs.botkonzept.de
- **Status:** https://status.botkonzept.de
## 📝 Lizenz
Proprietär - Alle Rechte vorbehalten
## 🎯 Roadmap
### Q1 2025
- [x] Website-Launch
- [x] Automatische Registrierung
- [x] Trial-Management
- [ ] Stripe-Integration
- [ ] PayPal-Integration
### Q2 2025
- [ ] Mobile App
- [ ] White-Label-Option
- [ ] API-Dokumentation
- [ ] Marketplace für Templates
### Q3 2025
- [ ] Multi-Language Support
- [ ] Advanced Analytics
- [ ] Team-Features
- [ ] Enterprise-Plan
## 🙏 Credits
Entwickelt mit:
- **n8n** - Workflow-Automation
- **Supabase** - Backend-as-a-Service
- **Proxmox** - Virtualisierung
- **PostgreSQL** - Datenbank
- **PostgREST** - REST API
- **Ollama** - LLM-Integration
---
**Version:** 1.0.0
**Letzte Aktualisierung:** 25.01.2025
**Autor:** MediaMetz

View File

@@ -0,0 +1,312 @@
{
"name": "BotKonzept - Customer Registration & Trial Management",
"nodes": [
{
"parameters": {
"httpMethod": "POST",
"path": "botkonzept-registration",
"responseMode": "responseNode",
"options": {}
},
"id": "webhook-registration",
"name": "Registration Webhook",
"type": "n8n-nodes-base.webhook",
"typeVersion": 1.1,
"position": [250, 300],
"webhookId": "botkonzept-registration"
},
{
"parameters": {
"conditions": {
"string": [
{
"value1": "={{$json.body.email}}",
"operation": "isNotEmpty"
},
{
"value1": "={{$json.body.firstName}}",
"operation": "isNotEmpty"
},
{
"value1": "={{$json.body.lastName}}",
"operation": "isNotEmpty"
}
]
}
},
"id": "validate-input",
"name": "Validate Input",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [450, 300]
},
{
"parameters": {
"operation": "insert",
"schema": "public",
"table": "customers",
"columns": "email,first_name,last_name,company,status,created_at,trial_end_date",
"additionalFields": {
"returnFields": "*"
}
},
"id": "create-customer",
"name": "Create Customer in DB",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [650, 200],
"credentials": {
"postgres": {
"id": "supabase-local",
"name": "Supabase Local"
}
}
},
{
"parameters": {
"authentication": "privateKey",
"command": "=/root/customer-installer/install.sh --storage local-zfs --bridge vmbr0 --ip dhcp --vlan 90 --apt-proxy http://192.168.45.2:3142 --n8n-owner-email {{ $json.email }} --n8n-owner-pass \"{{ $('Generate-Password').item.json.password }}\"",
"cwd": "/root/customer-installer/"
},
"id": "create-instance",
"name": "Create Customer Instance",
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [850, 200],
"credentials": {
"sshPrivateKey": {
"id": "pve20-ssh",
"name": "PVE20"
}
}
},
{
"parameters": {
"jsCode": "// Parse installation output\nconst stdout = $input.item.json.stdout;\nconst installData = JSON.parse(stdout);\n\n// Add customer info\ninstallData.customer = {\n id: $('Create Customer in DB').item.json.id,\n email: $('Create Customer in DB').item.json.email,\n firstName: $('Create Customer in DB').item.json.first_name,\n lastName: $('Create Customer in DB').item.json.last_name,\n company: $('Create Customer in DB').item.json.company\n};\n\nreturn installData;"
},
"id": "parse-install-output",
"name": "Parse Install Output",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [1050, 200]
},
{
"parameters": {
"operation": "insert",
"schema": "public",
"table": "instances",
"columns": "customer_id,ctid,hostname,ip,fqdn,status,credentials,created_at,trial_end_date",
"additionalFields": {}
},
"id": "save-instance",
"name": "Save Instance to DB",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [1250, 200],
"credentials": {
"postgres": {
"id": "supabase-local",
"name": "Supabase Local"
}
}
},
{
"parameters": {
"fromEmail": "noreply@botkonzept.de",
"toEmail": "={{ $json.customer.email }}",
"subject": "Willkommen bei BotKonzept - Ihre Instanz ist bereit! 🎉",
"emailType": "html",
"message": "=<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"UTF-8\">\n <style>\n body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }\n .container { max-width: 600px; margin: 0 auto; padding: 20px; }\n .header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 30px; text-align: center; border-radius: 10px 10px 0 0; }\n .content { background: #f9fafb; padding: 30px; }\n .credentials { background: white; padding: 20px; border-radius: 8px; margin: 20px 0; border-left: 4px solid #667eea; }\n .button { display: inline-block; background: #667eea; color: white; padding: 12px 30px; text-decoration: none; border-radius: 6px; margin: 20px 0; }\n .footer { text-align: center; padding: 20px; color: #6b7280; font-size: 14px; }\n .highlight { background: #fef3c7; padding: 2px 6px; border-radius: 3px; }\n </style>\n</head>\n<body>\n <div class=\"container\">\n <div class=\"header\">\n <h1>🎉 Willkommen bei BotKonzept!</h1>\n <p>Ihre KI-Chatbot-Instanz ist bereit</p>\n </div>\n \n <div class=\"content\">\n <p>Hallo {{ $json.customer.firstName }},</p>\n \n <p>vielen Dank für Ihre Registrierung! Ihre persönliche KI-Chatbot-Instanz wurde erfolgreich erstellt und ist jetzt einsatzbereit.</p>\n \n <div class=\"credentials\">\n <h3>📋 Ihre Zugangsdaten</h3>\n <p><strong>Dashboard-URL:</strong><br>\n <a href=\"{{ $json.urls.n8n_external }}\">{{ $json.urls.n8n_external }}</a></p>\n \n <p><strong>E-Mail:</strong> {{ $json.n8n.owner_email }}<br>\n <strong>Passwort:</strong> <span class=\"highlight\">{{ $json.n8n.owner_password }}</span></p>\n \n <p><strong>Chat-Webhook:</strong><br>\n <code>{{ $json.urls.chat_webhook }}</code></p>\n \n <p><strong>Upload-Formular:</strong><br>\n <a href=\"{{ $json.urls.upload_form }}\">{{ $json.urls.upload_form }}</a></p>\n </div>\n \n <h3>🚀 Nächste Schritte:</h3>\n <ol>\n <li><strong>Einloggen:</strong> Klicken Sie auf den Link oben und loggen Sie sich ein</li>\n <li><strong>Dokumente hochladen:</strong> Laden Sie Ihre PDFs, FAQs oder andere Dokumente hoch</li>\n <li><strong>Chatbot testen:</strong> Testen Sie Ihren Chatbot direkt im Dashboard</li>\n <li><strong>Code einbinden:</strong> Kopieren Sie den Widget-Code auf Ihre Website</li>\n </ol>\n \n <a href=\"{{ $json.urls.n8n_external }}\" class=\"button\">Jetzt Dashboard öffnen →</a>\n \n <div style=\"background: #fef3c7; padding: 15px; border-radius: 8px; margin: 20px 0;\">\n <p><strong>💰 Frühbucher-Angebot:</strong></p>\n <p>Upgraden Sie in den nächsten 3 Tagen und erhalten Sie <strong>30% Rabatt</strong> auf Ihr erstes Jahr!</p>\n </div>\n \n <p><strong>Trial-Zeitraum:</strong> 7 Tage (bis {{ $json.trial_end_date }})</p>\n \n <p>Bei Fragen stehen wir Ihnen jederzeit zur Verfügung!</p>\n \n <p>Viel Erfolg mit Ihrem KI-Chatbot!<br>\n Ihr BotKonzept-Team</p>\n </div>\n \n <div class=\"footer\">\n <p>BotKonzept | KI-Chatbots für moderne Unternehmen</p>\n <p><a href=\"https://botkonzept.de\">botkonzept.de</a> | <a href=\"mailto:support@botkonzept.de\">support@botkonzept.de</a></p>\n </div>\n </div>\n</body>\n</html>",
"options": {
"allowUnauthorizedCerts": false
}
},
"id": "send-welcome-email",
"name": "Send Welcome Email",
"type": "n8n-nodes-base.emailSend",
"typeVersion": 2.1,
"position": [1450, 200],
"credentials": {
"smtp": {
"id": "postfix-ses",
"name": "Postfix SES"
}
}
},
{
"parameters": {
"operation": "insert",
"schema": "public",
"table": "emails_sent",
"columns": "customer_id,email_type,sent_at",
"additionalFields": {}
},
"id": "log-email",
"name": "Log Email Sent",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [1650, 200],
"credentials": {
"postgres": {
"id": "supabase-local",
"name": "Supabase Local"
}
}
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"success\": true, \"message\": \"Registrierung erfolgreich! Sie erhalten in Kürze eine E-Mail mit Ihren Zugangsdaten.\", \"customerId\": $json.customer.id, \"instanceUrl\": $json.urls.n8n_external } }}",
"options": {
"responseCode": 200
}
},
"id": "success-response",
"name": "Success Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [1850, 200]
},
{
"parameters": {
"respondWith": "json",
"responseBody": "={{ { \"success\": false, \"error\": \"Ungültige Eingabedaten. Bitte überprüfen Sie Ihre Angaben.\" } }}",
"options": {
"responseCode": 400
}
},
"id": "error-response",
"name": "Error Response",
"type": "n8n-nodes-base.respondToWebhook",
"typeVersion": 1,
"position": [650, 400]
},
{
"parameters": {
"jsCode": "// Generate secure password\nconst length = 16;\nconst charset = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\nlet password = '';\n\nfor (let i = 0; i < length; i++) {\n const randomIndex = Math.floor(Math.random() * charset.length);\n password += charset[randomIndex];\n}\n\n// Calculate trial end date (7 days from now)\nconst trialEndDate = new Date();\ntrialEndDate.setDate(trialEndDate.getDate() + 7);\n\nreturn {\n password: password,\n trialEndDate: trialEndDate.toISOString(),\n email: $json.body.email,\n firstName: $json.body.firstName,\n lastName: $json.body.lastName,\n company: $json.body.company || null\n};"
},
"id": "generate-password",
"name": "Generate Password & Trial Date",
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [650, 100]
}
],
"connections": {
"Registration Webhook": {
"main": [
[
{
"node": "Validate Input",
"type": "main",
"index": 0
}
]
]
},
"Validate Input": {
"main": [
[
{
"node": "Generate Password & Trial Date",
"type": "main",
"index": 0
}
],
[
{
"node": "Error Response",
"type": "main",
"index": 0
}
]
]
},
"Generate Password & Trial Date": {
"main": [
[
{
"node": "Create Customer in DB",
"type": "main",
"index": 0
}
]
]
},
"Create Customer in DB": {
"main": [
[
{
"node": "Create Customer Instance",
"type": "main",
"index": 0
}
]
]
},
"Create Customer Instance": {
"main": [
[
{
"node": "Parse Install Output",
"type": "main",
"index": 0
}
]
]
},
"Parse Install Output": {
"main": [
[
{
"node": "Save Instance to DB",
"type": "main",
"index": 0
}
]
]
},
"Save Instance to DB": {
"main": [
[
{
"node": "Send Welcome Email",
"type": "main",
"index": 0
}
]
]
},
"Send Welcome Email": {
"main": [
[
{
"node": "Log Email Sent",
"type": "main",
"index": 0
}
]
]
},
"Log Email Sent": {
"main": [
[
{
"node": "Success Response",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"updatedAt": "2025-01-25T00:00:00.000Z",
"versionId": "1"
}

View File

@@ -0,0 +1,122 @@
{
"name": "BotKonzept - Trial Management & Email Automation",
"nodes": [
{
"parameters": {
"rule": {
"interval": [
{
"field": "cronExpression",
"expression": "0 9 * * *"
}
]
}
},
"id": "daily-cron",
"name": "Daily at 9 AM",
"type": "n8n-nodes-base.scheduleTrigger",
"typeVersion": 1.1,
"position": [250, 300]
},
{
"parameters": {
"operation": "executeQuery",
"query": "SELECT c.id as customer_id, c.email, c.first_name, c.last_name, c.company, c.created_at, c.status, i.ctid, i.hostname, i.fqdn, i.trial_end_date, i.credentials, EXTRACT(DAY FROM (NOW() - c.created_at)) as days_since_registration FROM customers c JOIN instances i ON c.id = i.customer_id WHERE c.status = 'trial' AND i.status = 'active' AND c.created_at >= NOW() - INTERVAL '8 days'",
"additionalFields": {}
},
"id": "get-trial-customers",
"name": "Get Trial Customers",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [450, 300],
"credentials": {
"postgres": {
"id": "supabase-local",
"name": "Supabase Local"
}
}
},
{
"parameters": {
"conditions": {
"number": [
{
"value1": "={{$json.days_since_registration}}",
"operation": "equal",
"value2": 3
}
]
}
},
"id": "check-day-3",
"name": "Day 3?",
"type": "n8n-nodes-base.if",
"typeVersion": 1,
"position": [650, 200]
},
{
"parameters": {
"operation": "insert",
"schema": "public",
"table": "emails_sent",
"columns": "customer_id,email_type,sent_at",
"additionalFields": {}
},
"id": "log-email-sent",
"name": "Log Email Sent",
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [1450, 200],
"credentials": {
"postgres": {
"id": "supabase-local",
"name": "Supabase Local"
}
}
}
],
"connections": {
"Daily at 9 AM": {
"main": [
[
{
"node": "Get Trial Customers",
"type": "main",
"index": 0
}
]
]
},
"Get Trial Customers": {
"main": [
[
{
"node": "Day 3?",
"type": "main",
"index": 0
}
]
]
},
"Day 3?": {
"main": [
[
{
"node": "Log Email Sent",
"type": "main",
"index": 0
}
]
]
}
},
"pinData": {},
"settings": {
"executionOrder": "v1"
},
"staticData": null,
"tags": [],
"triggerCount": 0,
"updatedAt": "2025-01-25T00:00:00.000Z",
"versionId": "1"
}

363
DEPLOYMENT_CHECKLIST.md Normal file
View File

@@ -0,0 +1,363 @@
# 🚀 BotKonzept - Deployment Checkliste
## ✅ Pre-Deployment
### Infrastruktur
- [ ] Proxmox VE20 läuft und ist erreichbar
- [ ] Supabase PostgreSQL ist konfiguriert
- [ ] n8n Instanz ist verfügbar
- [ ] OPNsense NGINX Reverse Proxy ist konfiguriert
- [ ] Postfix/SES E-Mail-Gateway funktioniert
- [ ] DNS für botkonzept.de ist konfiguriert
### Datenbank
- [ ] PostgreSQL-Verbindung getestet
- [ ] Schema `botkonzept_schema.sql` importiert
- [ ] Tabellen erstellt (customers, instances, etc.)
- [ ] Views erstellt (customer_overview, trials_expiring_soon)
- [ ] Row Level Security aktiviert
- [ ] Backup-Strategie definiert
### n8n Workflows
- [ ] Customer Registration Workflow importiert
- [ ] Trial Management Workflow importiert
- [ ] SSH-Credentials (PVE20) konfiguriert
- [ ] PostgreSQL-Credentials konfiguriert
- [ ] SMTP-Credentials konfiguriert
- [ ] Webhooks aktiviert
- [ ] Cron-Jobs aktiviert (täglich 9:00 Uhr)
### Website
- [ ] HTML/CSS/JS-Dateien geprüft
- [ ] Logo (20250119_Logo_Botkozept.svg) vorhanden
- [ ] Webhook-URL in main.js konfiguriert
- [ ] SSL-Zertifikat installiert
- [ ] HTTPS erzwungen
- [ ] Cookie-Banner implementiert
- [ ] Datenschutzerklärung vorhanden
- [ ] Impressum vorhanden
- [ ] AGB vorhanden
## 🔧 Deployment Steps
### 1. Datenbank Setup
```bash
# Verbindung testen
psql -h 192.168.45.3 -U customer -d customer -c "SELECT 1"
# Schema importieren
psql -h 192.168.45.3 -U customer -d customer -f sql/botkonzept_schema.sql
# Tabellen verifizieren
psql -h 192.168.45.3 -U customer -d customer -c "\dt"
```
**Erwartetes Ergebnis:**
- 7 Tabellen erstellt
- 3 Views erstellt
- Triggers aktiv
### 2. n8n Workflows
```bash
# 1. n8n öffnen
open https://n8n.userman.de
# 2. Workflows importieren
# - BotKonzept-Customer-Registration-Workflow.json
# - BotKonzept-Trial-Management-Workflow.json
# 3. Credentials konfigurieren
# SSH (PVE20): /root/.ssh/id_rsa
# PostgreSQL: 192.168.45.3:5432/customer
# SMTP: Postfix Gateway
```
**Webhook-URLs:**
- Registration: `https://n8n.userman.de/webhook/botkonzept-registration`
- Test: `curl -X POST https://n8n.userman.de/webhook/botkonzept-registration -H "Content-Type: application/json" -d '{"test":true}'`
### 3. Website Deployment
```bash
# Setup-Script ausführen
chmod +x setup_botkonzept.sh
./setup_botkonzept.sh
# Oder manuell:
sudo mkdir -p /var/www/botkonzept
sudo cp -r botkonzept-website/* /var/www/botkonzept/
sudo chown -R www-data:www-data /var/www/botkonzept
```
**NGINX-Konfiguration:**
```nginx
server {
listen 80;
server_name botkonzept.de www.botkonzept.de;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name botkonzept.de www.botkonzept.de;
ssl_certificate /etc/letsencrypt/live/botkonzept.de/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/botkonzept.de/privkey.pem;
root /var/www/botkonzept;
index index.html;
location / {
try_files $uri $uri/ =404;
}
# Security headers
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
}
```
### 4. SSL-Zertifikat
```bash
# Let's Encrypt installieren
sudo apt-get install certbot python3-certbot-nginx
# Zertifikat erstellen
sudo certbot --nginx -d botkonzept.de -d www.botkonzept.de
# Auto-Renewal testen
sudo certbot renew --dry-run
```
## ✅ Post-Deployment Tests
### 1. Datenbank-Tests
```sql
-- Kunden-Tabelle testen
INSERT INTO customers (email, first_name, last_name, status)
VALUES ('test@example.com', 'Test', 'User', 'trial')
RETURNING *;
-- View testen
SELECT * FROM customer_overview;
-- Cleanup
DELETE FROM customers WHERE email = 'test@example.com';
```
### 2. Workflow-Tests
```bash
# Registration Webhook testen
curl -X POST https://n8n.userman.de/webhook/botkonzept-registration \
-H "Content-Type: application/json" \
-d '{
"firstName": "Max",
"lastName": "Mustermann",
"email": "test@example.com",
"company": "Test GmbH",
"terms": true
}'
# Erwartete Antwort:
# {"success": true, "message": "Registrierung erfolgreich!"}
```
### 3. Website-Tests
- [ ] Homepage lädt (https://botkonzept.de)
- [ ] Alle Bilder werden angezeigt
- [ ] Navigation funktioniert
- [ ] Formular wird angezeigt
- [ ] Formular-Validierung funktioniert
- [ ] Mobile-Ansicht korrekt
- [ ] SSL-Zertifikat gültig
- [ ] Keine Console-Errors
### 4. E-Mail-Tests
```bash
# Test-E-Mail senden
echo "Test" | mail -s "BotKonzept Test" test@example.com
# Postfix-Logs prüfen
tail -f /var/log/mail.log
```
### 5. End-to-End Test
1. **Registrierung:**
- [ ] Formular ausfüllen
- [ ] Absenden
- [ ] Success-Message erscheint
2. **Datenbank:**
- [ ] Kunde in `customers` Tabelle
- [ ] Instanz in `instances` Tabelle
- [ ] E-Mail in `emails_sent` Tabelle
3. **E-Mail:**
- [ ] Willkommens-E-Mail erhalten
- [ ] Zugangsdaten korrekt
- [ ] Links funktionieren
4. **Instanz:**
- [ ] LXC erstellt (pct list)
- [ ] n8n erreichbar
- [ ] Login funktioniert
## 📊 Monitoring
### Datenbank-Monitoring
```sql
-- Aktive Trials
SELECT COUNT(*) FROM customers WHERE status = 'trial';
-- Trials die heute ablaufen
SELECT * FROM trials_expiring_soon WHERE days_remaining < 1;
-- E-Mails der letzten 24h
SELECT email_type, COUNT(*)
FROM emails_sent
WHERE sent_at >= NOW() - INTERVAL '24 hours'
GROUP BY email_type;
-- Revenue heute
SELECT SUM(amount) FROM payments
WHERE status = 'succeeded'
AND paid_at::date = CURRENT_DATE;
```
### n8n-Monitoring
- [ ] Workflow-Executions prüfen
- [ ] Error-Rate überwachen
- [ ] Execution-Time tracken
### Server-Monitoring
```bash
# LXC-Container zählen
pct list | grep -c "running"
# Disk-Usage
df -h
# Memory-Usage
free -h
# Load Average
uptime
```
## 🔒 Security Checklist
- [ ] Firewall-Regeln konfiguriert
- [ ] SSH nur mit Key-Auth
- [ ] PostgreSQL nur intern erreichbar
- [ ] n8n hinter Reverse Proxy
- [ ] SSL/TLS erzwungen
- [ ] Rate-Limiting aktiviert
- [ ] CORS korrekt konfiguriert
- [ ] Input-Validierung aktiv
- [ ] SQL-Injection-Schutz
- [ ] XSS-Schutz
- [ ] CSRF-Schutz
## 📝 Backup-Strategie
### Datenbank-Backup
```bash
# Tägliches Backup
0 2 * * * pg_dump -h 192.168.45.3 -U customer customer > /backup/botkonzept_$(date +\%Y\%m\%d).sql
# Backup-Retention (30 Tage)
find /backup -name "botkonzept_*.sql" -mtime +30 -delete
```
### LXC-Backup
```bash
# Proxmox Backup
vzdump --mode snapshot --compress gzip --storage backup-storage
```
### Website-Backup
```bash
# Git-Repository
cd /var/www/botkonzept
git init
git add .
git commit -m "Website backup $(date)"
git push origin main
```
## 🚨 Rollback-Plan
### Bei Problemen mit Workflows
1. Workflows deaktivieren
2. Alte Version wiederherstellen
3. Credentials prüfen
4. Neu aktivieren
### Bei Datenbank-Problemen
```bash
# Backup wiederherstellen
psql -h 192.168.45.3 -U customer customer < /backup/botkonzept_YYYYMMDD.sql
```
### Bei Website-Problemen
```bash
# Alte Version wiederherstellen
git checkout HEAD~1
sudo cp -r botkonzept-website/* /var/www/botkonzept/
```
## 📞 Support-Kontakte
- **Proxmox:** admin@userman.de
- **n8n:** support@userman.de
- **DNS:** dns@userman.de
- **E-Mail:** postmaster@userman.de
## ✅ Go-Live Checklist
- [ ] Alle Tests bestanden
- [ ] Monitoring aktiv
- [ ] Backups konfiguriert
- [ ] Team informiert
- [ ] Dokumentation aktuell
- [ ] Support-Prozesse definiert
- [ ] Rollback-Plan getestet
- [ ] Performance-Tests durchgeführt
- [ ] Security-Audit durchgeführt
- [ ] DSGVO-Compliance geprüft
## 🎉 Post-Launch
- [ ] Analytics einrichten (Google Analytics)
- [ ] Conversion-Tracking aktivieren
- [ ] A/B-Tests planen
- [ ] Marketing-Kampagnen starten
- [ ] Social Media ankündigen
- [ ] Blog-Post veröffentlichen
- [ ] Newsletter versenden
---
**Deployment-Datum:** _________________
**Deployed von:** _________________
**Version:** 1.0.0
**Status:** ⬜ In Arbeit | ⬜ Bereit | ⬜ Live

326
Install-Workflow01.json Normal file
View File

@@ -0,0 +1,326 @@
{
"name": "Install-Workflow",
"nodes": [
{
"parameters": {
"authentication": "privateKey",
"command": "/root/customer-installer/install.sh --storage local-zfs --bridge vmbr0 --ip dhcp --vlan 90 --apt-proxy http://192.168.45.2:3142",
"cwd": "/root/customer-installer/"
},
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [
192,
0
],
"id": "9878a864-651e-4a53-abfc-b0bc2d2f2d15",
"name": "Start Installer 1",
"credentials": {
"sshPrivateKey": {
"id": "bPl4nGXO7Iz4wJPJ",
"name": "PVE20"
}
}
},
{
"parameters": {
"authentication": "privateKey",
"command": "=./n8n_setup.sh --n8n_internal {{ $json.urls.n8n_internal }} --owner_email {{ $json.n8n.owner_email }} --owner_password \"{{ $json.n8n.owner_password }}\" --owner_first_name \"Max\" --owner_last_name \"Mustermann\" --timeout 30",
"cwd": "/root/customer-installer/"
},
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [
640,
0
],
"id": "62e1b647-7daa-445e-bf99-873efbe2bc94",
"name": "Setup n8n Owner",
"credentials": {
"sshPrivateKey": {
"id": "bPl4nGXO7Iz4wJPJ",
"name": "PVE20"
}
},
"disabled": true
},
{
"parameters": {},
"type": "n8n-nodes-base.manualTrigger",
"typeVersion": 1,
"position": [
-32,
0
],
"id": "11aa3e6a-b20a-49b0-b6f9-4cec469c9916",
"name": "When clicking Execute workflow"
},
{
"parameters": {
"authentication": "privateKey",
"command": "=bash setup_nginx_proxy.sh --ctid {{ $('install_JSON').item.json.ctid }} --hostname {{ $('install_JSON').item.json.hostname }} --fqdn {{ $('install_JSON').item.json.fqdn }} --backend-ip {{ $('install_JSON').item.json.ip }}",
"cwd": "/root/customer-installer/"
},
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [
1088,
0
],
"id": "176fd78c-9dd6-427b-a566-88dbf504347e",
"name": "setup_nginx_proxy",
"credentials": {
"sshPrivateKey": {
"id": "bPl4nGXO7Iz4wJPJ",
"name": "PVE20"
}
},
"disabled": true
},
{
"parameters": {
"formTitle": "Delete",
"formFields": {
"values": [
{
"fieldLabel": "Host-Number",
"requiredField": true
}
]
},
"options": {
"buttonLabel": "Löschen"
}
},
"type": "n8n-nodes-base.formTrigger",
"typeVersion": 2.5,
"position": [
-32,
208
],
"id": "4c9878ec-177c-4fbd-a83a-a3895ebd8193",
"name": "On form submission",
"webhookId": "12847d69-15c8-44e4-b595-645ff3109355"
},
{
"parameters": {
"jsCode": "// Code Node - JavaScript\nconst sshOutput = $input.all();\n\n// stdout-String parsen\nconst parsedData = JSON.parse(sshOutput[0].json.stdout);\n\nreturn [{\n json: parsedData\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
416,
0
],
"id": "67ace9d6-7ed2-4495-b3e6-6091fd252596",
"name": "install_JSON"
},
{
"parameters": {
"jsCode": "// Code Node - JavaScript\nconst sshOutput = $input.all();\n\n// stdout-String parsen\nconst parsedData = JSON.parse(sshOutput[0].json.stdout);\n\nreturn [{\n json: parsedData\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
864,
0
],
"id": "fc1b9d9f-5a70-476e-8ae2-4cfd81050527",
"name": "setupOwnerJSON",
"disabled": true
},
{
"parameters": {
"jsCode": "// Code Node - JavaScript\nconst sshOutput = $input.all();\n\n// stdout-String parsen\nconst parsedData = JSON.parse(sshOutput[0].json.stdout);\n\nreturn [{\n json: parsedData\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1312,
0
],
"id": "86b70266-cafe-4a4f-98d1-20434f61061b",
"name": "setupProxyJSON",
"disabled": true
},
{
"parameters": {
"chatId": "26805429",
"text": "=Done.\nhttps://{{ $json.fqdn }}\nUsername: {{ $('install_JSON').item.json.n8n.owner_email }}\nPassword: {{ $('install_JSON').item.json.n8n.owner_password }}\nChat: {{ $('install_JSON').item.json.urls.chat_webhook }}",
"additionalFields": {
"appendAttribution": false
}
},
"type": "n8n-nodes-base.telegram",
"typeVersion": 1.2,
"position": [
1536,
0
],
"id": "9e20ee75-c2ae-421f-ac4c-54f5643032f2",
"name": "Send a text message",
"webhookId": "55a97e93-a457-4bee-8b8f-3eea1ad62928",
"credentials": {
"telegramApi": {
"id": "7DYJfIx5JsxgKc0D",
"name": "LizTheLove_Bot"
}
}
},
{
"parameters": {
"authentication": "privateKey",
"command": "=/root/customer-installer/delete_nginx_proxy.sh --ctid {{ $('On form submission').item.json['Host-Number'] }}",
"cwd": "/root/customer-installer/"
},
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [
416,
208
],
"id": "34d50d3a-cdb8-4801-b54b-719ef60b4b63",
"name": "deleteProxy",
"credentials": {
"sshPrivateKey": {
"id": "bPl4nGXO7Iz4wJPJ",
"name": "PVE20"
}
}
},
{
"parameters": {
"authentication": "privateKey",
"command": "=pct stop {{ $json['Host-Number'] }} && pct destroy {{ $json['Host-Number'] }} ",
"cwd": "/root/customer-installer/"
},
"type": "n8n-nodes-base.ssh",
"typeVersion": 1,
"position": [
192,
208
],
"id": "4645ace7-30ef-4698-8288-065fe70ff9b0",
"name": "deleteLXC",
"credentials": {
"sshPrivateKey": {
"id": "bPl4nGXO7Iz4wJPJ",
"name": "PVE20"
}
}
}
],
"pinData": {},
"connections": {
"Start Installer 1": {
"main": [
[
{
"node": "install_JSON",
"type": "main",
"index": 0
}
]
]
},
"Setup n8n Owner": {
"main": [
[
{
"node": "setupOwnerJSON",
"type": "main",
"index": 0
}
]
]
},
"When clicking Execute workflow": {
"main": [
[
{
"node": "Start Installer 1",
"type": "main",
"index": 0
}
]
]
},
"On form submission": {
"main": [
[
{
"node": "deleteLXC",
"type": "main",
"index": 0
}
]
]
},
"install_JSON": {
"main": [
[
{
"node": "Setup n8n Owner",
"type": "main",
"index": 0
}
]
]
},
"setupOwnerJSON": {
"main": [
[
{
"node": "setup_nginx_proxy",
"type": "main",
"index": 0
}
]
]
},
"setup_nginx_proxy": {
"main": [
[
{
"node": "setupProxyJSON",
"type": "main",
"index": 0
}
]
]
},
"setupProxyJSON": {
"main": [
[
{
"node": "Send a text message",
"type": "main",
"index": 0
}
]
]
},
"deleteLXC": {
"main": [
[
{
"node": "deleteProxy",
"type": "main",
"index": 0
}
]
]
}
},
"active": false,
"settings": {
"availableInMCP": false
},
"versionId": "7814a0dd-fae3-40e6-913b-f7b8bac10e81",
"meta": {
"templateCredsSetupCompleted": true,
"instanceId": "c832ea07b2e6ccb9d5832b81bb497b720b31ed50006898070951c4f6d209f426"
},
"id": "ba7BxTeeQif4zu2F",
"tags": []
}

View File

@@ -0,0 +1,884 @@
/* ===================================
CSS Variables & Reset
=================================== */
:root {
--primary-color: #2563eb;
--primary-dark: #1e40af;
--primary-light: #3b82f6;
--secondary-color: #10b981;
--text-dark: #1f2937;
--text-light: #6b7280;
--bg-light: #f9fafb;
--bg-white: #ffffff;
--border-color: #e5e7eb;
--success-color: #10b981;
--error-color: #ef4444;
--warning-color: #f59e0b;
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
--font-mono: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
--shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
--shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
--shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
--radius-sm: 0.375rem;
--radius-md: 0.5rem;
--radius-lg: 0.75rem;
--radius-xl: 1rem;
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
scroll-behavior: smooth;
}
body {
font-family: var(--font-sans);
color: var(--text-dark);
line-height: 1.6;
background-color: var(--bg-white);
}
img {
max-width: 100%;
height: auto;
}
a {
text-decoration: none;
color: inherit;
transition: var(--transition);
}
ul {
list-style: none;
}
/* ===================================
Container & Layout
=================================== */
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1.5rem;
}
section {
padding: 5rem 0;
}
.section-header {
text-align: center;
margin-bottom: 3rem;
}
.section-header h2 {
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 1rem;
color: var(--text-dark);
}
.section-header p {
font-size: 1.25rem;
color: var(--text-light);
max-width: 600px;
margin: 0 auto;
}
/* ===================================
Navigation
=================================== */
.navbar {
background: var(--bg-white);
box-shadow: var(--shadow-sm);
position: sticky;
top: 0;
z-index: 1000;
border-bottom: 1px solid var(--border-color);
}
.nav-wrapper {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
}
.logo {
display: flex;
align-items: center;
gap: 0.75rem;
font-size: 1.5rem;
font-weight: 700;
color: var(--text-dark);
}
.logo img {
height: 40px;
}
.nav-menu {
display: flex;
align-items: center;
gap: 2rem;
}
.nav-menu a {
color: var(--text-light);
font-weight: 500;
transition: var(--transition);
}
.nav-menu a:hover {
color: var(--primary-color);
}
.mobile-menu-toggle {
display: none;
flex-direction: column;
gap: 0.25rem;
background: none;
border: none;
cursor: pointer;
padding: 0.5rem;
}
.mobile-menu-toggle span {
width: 24px;
height: 2px;
background: var(--text-dark);
transition: var(--transition);
}
/* ===================================
Buttons
=================================== */
.btn-primary,
.btn-secondary {
display: inline-block;
padding: 0.75rem 1.5rem;
border-radius: var(--radius-md);
font-weight: 600;
text-align: center;
transition: var(--transition);
cursor: pointer;
border: none;
font-size: 1rem;
}
.btn-primary {
background: var(--primary-color);
color: white;
}
.btn-primary:hover {
background: var(--primary-dark);
transform: translateY(-2px);
box-shadow: var(--shadow-lg);
}
.btn-secondary {
background: transparent;
color: var(--primary-color);
border: 2px solid var(--primary-color);
}
.btn-secondary:hover {
background: var(--primary-color);
color: white;
}
.btn-large {
padding: 1rem 2rem;
font-size: 1.125rem;
}
.btn-block {
width: 100%;
display: block;
}
/* ===================================
Hero Section
=================================== */
.hero {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 6rem 0;
position: relative;
overflow: hidden;
}
.hero::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: url('data:image/svg+xml,<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><defs><pattern id="grid" width="100" height="100" patternUnits="userSpaceOnUse"><path d="M 100 0 L 0 0 0 100" fill="none" stroke="rgba(255,255,255,0.1)" stroke-width="1"/></pattern></defs><rect width="100%" height="100%" fill="url(%23grid)"/></svg>');
opacity: 0.3;
}
.hero-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
position: relative;
z-index: 1;
}
.hero h1 {
font-size: 3.5rem;
font-weight: 800;
line-height: 1.2;
margin-bottom: 1.5rem;
}
.hero-subtitle {
font-size: 1.25rem;
margin-bottom: 2rem;
opacity: 0.95;
}
.hero-cta {
display: flex;
gap: 1rem;
margin-bottom: 2rem;
}
.hero-features {
display: flex;
flex-direction: column;
gap: 0.75rem;
}
.feature-badge {
display: flex;
align-items: center;
gap: 0.5rem;
font-size: 0.95rem;
opacity: 0.9;
}
.feature-badge svg {
flex-shrink: 0;
}
/* Chatbot Demo */
.chatbot-demo {
background: white;
border-radius: var(--radius-xl);
box-shadow: var(--shadow-xl);
overflow: hidden;
max-width: 400px;
}
.demo-header {
background: var(--primary-color);
color: white;
padding: 1rem;
display: flex;
align-items: center;
gap: 1rem;
}
.demo-dots {
display: flex;
gap: 0.5rem;
}
.demo-dots span {
width: 12px;
height: 12px;
border-radius: 50%;
background: rgba(255, 255, 255, 0.3);
}
.demo-title {
font-weight: 600;
}
.demo-messages {
padding: 1.5rem;
background: #f9fafb;
min-height: 300px;
display: flex;
flex-direction: column;
gap: 1rem;
}
.message {
display: flex;
gap: 0.75rem;
align-items: flex-start;
}
.message.user {
flex-direction: row-reverse;
}
.message .avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--primary-light);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.message .bubble {
background: white;
padding: 0.75rem 1rem;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-sm);
max-width: 70%;
color: var(--text-dark);
}
.message.user .bubble {
background: var(--primary-color);
color: white;
}
.demo-input {
padding: 1rem;
background: white;
border-top: 1px solid var(--border-color);
display: flex;
gap: 0.5rem;
}
.demo-input input {
flex: 1;
padding: 0.75rem;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
font-size: 0.95rem;
}
.demo-input button {
padding: 0.75rem 1.5rem;
background: var(--primary-color);
color: white;
border: none;
border-radius: var(--radius-md);
cursor: pointer;
font-size: 1.25rem;
}
/* ===================================
Features Section
=================================== */
.features {
background: var(--bg-light);
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
}
.feature-card {
background: white;
padding: 2rem;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
transition: var(--transition);
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-xl);
}
.feature-icon {
font-size: 3rem;
margin-bottom: 1rem;
}
.feature-card h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
color: var(--text-dark);
}
.feature-card p {
color: var(--text-light);
line-height: 1.7;
}
/* ===================================
How It Works Section
=================================== */
.steps {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 2rem;
margin-top: 3rem;
}
.step {
text-align: center;
position: relative;
}
.step-number {
width: 60px;
height: 60px;
background: var(--primary-color);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
font-weight: 700;
margin: 0 auto 1.5rem;
}
.step h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
}
.step p {
color: var(--text-light);
}
/* ===================================
Pricing Section
=================================== */
.pricing {
background: var(--bg-light);
}
.pricing-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 2rem;
margin-top: 3rem;
}
.pricing-card {
background: white;
border-radius: var(--radius-xl);
padding: 2.5rem;
box-shadow: var(--shadow-md);
transition: var(--transition);
position: relative;
}
.pricing-card:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-xl);
}
.pricing-card.featured {
border: 3px solid var(--primary-color);
transform: scale(1.05);
}
.pricing-card .badge {
position: absolute;
top: -15px;
right: 20px;
background: var(--primary-color);
color: white;
padding: 0.5rem 1rem;
border-radius: var(--radius-md);
font-weight: 600;
font-size: 0.875rem;
}
.pricing-header h3 {
font-size: 1.5rem;
margin-bottom: 1rem;
}
.price {
display: flex;
align-items: baseline;
gap: 0.25rem;
margin-bottom: 0.5rem;
}
.price .currency {
font-size: 1.5rem;
font-weight: 600;
}
.price .amount {
font-size: 3.5rem;
font-weight: 800;
color: var(--primary-color);
}
.price .period {
font-size: 1.125rem;
color: var(--text-light);
}
.price-note {
font-size: 0.875rem;
color: var(--success-color);
font-weight: 600;
margin-bottom: 1.5rem;
}
.pricing-features {
margin: 2rem 0;
}
.pricing-features li {
padding: 0.75rem 0;
border-bottom: 1px solid var(--border-color);
color: var(--text-light);
}
.pricing-features li:last-child {
border-bottom: none;
}
/* ===================================
Trial Registration Section
=================================== */
.trial-registration {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.trial-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 4rem;
align-items: center;
}
.trial-info h2 {
font-size: 2.5rem;
margin-bottom: 1rem;
}
.trial-info p {
font-size: 1.25rem;
margin-bottom: 2rem;
opacity: 0.95;
}
.trial-benefits {
display: flex;
flex-direction: column;
gap: 1rem;
}
.trial-benefits li {
display: flex;
align-items: center;
gap: 1rem;
font-size: 1.125rem;
}
.trial-benefits svg {
flex-shrink: 0;
color: var(--success-color);
}
.trial-form-wrapper {
background: white;
border-radius: var(--radius-xl);
padding: 2.5rem;
box-shadow: var(--shadow-xl);
}
.trial-form h3 {
color: var(--text-dark);
font-size: 1.75rem;
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
color: var(--text-dark);
font-weight: 500;
}
.form-group input {
width: 100%;
padding: 0.75rem;
border: 1px solid var(--border-color);
border-radius: var(--radius-md);
font-size: 1rem;
transition: var(--transition);
}
.form-group input:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}
.form-group.checkbox {
display: flex;
align-items: flex-start;
gap: 0.75rem;
}
.form-group.checkbox input {
width: auto;
margin-top: 0.25rem;
}
.form-group.checkbox label {
margin: 0;
font-weight: 400;
font-size: 0.95rem;
}
.form-group.checkbox a {
color: var(--primary-color);
text-decoration: underline;
}
.form-note {
font-size: 0.875rem;
color: var(--text-light);
text-align: center;
margin-top: 1rem;
}
.btn-loading {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.spinner {
animation: spin 1s linear infinite;
}
.spinner circle {
stroke-dasharray: 90, 150;
stroke-dashoffset: 0;
stroke-linecap: round;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
/* Success Message */
.success-message {
text-align: center;
padding: 2rem;
}
.success-icon {
width: 80px;
height: 80px;
background: var(--success-color);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 3rem;
margin: 0 auto 1.5rem;
}
.success-message h3 {
color: var(--text-dark);
font-size: 1.75rem;
margin-bottom: 1rem;
}
.success-message p {
color: var(--text-light);
margin-bottom: 1.5rem;
}
.success-details {
background: var(--bg-light);
padding: 1.5rem;
border-radius: var(--radius-md);
text-align: left;
}
.success-details p {
color: var(--text-dark);
font-weight: 600;
margin-bottom: 1rem;
}
.success-details ol {
list-style: decimal;
padding-left: 1.5rem;
color: var(--text-light);
}
.success-details li {
margin-bottom: 0.5rem;
}
/* ===================================
FAQ Section
=================================== */
.faq-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
gap: 2rem;
}
.faq-item {
background: white;
padding: 2rem;
border-radius: var(--radius-lg);
box-shadow: var(--shadow-md);
}
.faq-item h3 {
font-size: 1.25rem;
margin-bottom: 1rem;
color: var(--text-dark);
}
.faq-item p {
color: var(--text-light);
line-height: 1.7;
}
/* ===================================
Footer
=================================== */
.footer {
background: var(--text-dark);
color: white;
padding: 3rem 0 1.5rem;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 2rem;
margin-bottom: 2rem;
}
.footer-section h4 {
margin-bottom: 1rem;
font-size: 1.125rem;
}
.footer-section p {
opacity: 0.8;
margin-top: 0.5rem;
}
.footer-section ul {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.footer-section a {
opacity: 0.8;
transition: var(--transition);
}
.footer-section a:hover {
opacity: 1;
color: var(--primary-light);
}
.footer-bottom {
border-top: 1px solid rgba(255, 255, 255, 0.1);
padding-top: 1.5rem;
text-align: center;
opacity: 0.8;
}
/* ===================================
Responsive Design
=================================== */
@media (max-width: 768px) {
.nav-menu {
display: none;
}
.mobile-menu-toggle {
display: flex;
}
.hero-content,
.trial-content {
grid-template-columns: 1fr;
gap: 2rem;
}
.hero h1 {
font-size: 2.5rem;
}
.hero-cta {
flex-direction: column;
}
.section-header h2 {
font-size: 2rem;
}
.steps {
grid-template-columns: 1fr;
}
.pricing-grid {
grid-template-columns: 1fr;
}
.pricing-card.featured {
transform: scale(1);
}
.faq-grid {
grid-template-columns: 1fr;
}
}
@media (max-width: 480px) {
.hero h1 {
font-size: 2rem;
}
.hero-subtitle {
font-size: 1rem;
}
.section-header h2 {
font-size: 1.75rem;
}
.trial-form-wrapper {
padding: 1.5rem;
}
}

View File

@@ -0,0 +1,417 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BotKonzept - KI-Chatbot für Ihre Website in 7 Tagen kostenlos testen</title>
<meta name="description" content="Testen Sie Ihren eigenen KI-Chatbot 7 Tage kostenlos. Einfache Integration, leistungsstarke RAG-Technologie, keine Kreditkarte erforderlich.">
<link rel="stylesheet" href="css/style.css">
<link rel="icon" type="image/svg+xml" href="../20250119_Logo_Botkozept.svg">
</head>
<body>
<!-- Navigation -->
<nav class="navbar">
<div class="container">
<div class="nav-wrapper">
<a href="index.html" class="logo">
<img src="../20250119_Logo_Botkozept.svg" alt="BotKonzept Logo" height="40">
<span>BotKonzept</span>
</a>
<ul class="nav-menu">
<li><a href="#features">Features</a></li>
<li><a href="#how-it-works">So funktioniert's</a></li>
<li><a href="#pricing">Preise</a></li>
<li><a href="#faq">FAQ</a></li>
<li><a href="#trial" class="btn-primary">7 Tage kostenlos testen</a></li>
</ul>
<button class="mobile-menu-toggle" aria-label="Menu">
<span></span>
<span></span>
<span></span>
</button>
</div>
</div>
</nav>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<div class="hero-text">
<h1>Ihr KI-Chatbot in Minuten einsatzbereit</h1>
<p class="hero-subtitle">
Verwandeln Sie Ihre Dokumente in einen intelligenten Chatbot.
Keine Programmierung erforderlich. 7 Tage kostenlos testen.
</p>
<div class="hero-cta">
<a href="#trial" class="btn-primary btn-large">Jetzt kostenlos starten</a>
<a href="#how-it-works" class="btn-secondary btn-large">Mehr erfahren</a>
</div>
<div class="hero-features">
<div class="feature-badge">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" fill="currentColor"/>
</svg>
<span>Keine Kreditkarte erforderlich</span>
</div>
<div class="feature-badge">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" fill="currentColor"/>
</svg>
<span>In 5 Minuten eingerichtet</span>
</div>
<div class="feature-badge">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 111.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" fill="currentColor"/>
</svg>
<span>DSGVO-konform</span>
</div>
</div>
</div>
<div class="hero-image">
<div class="chatbot-demo">
<div class="demo-header">
<div class="demo-dots">
<span></span>
<span></span>
<span></span>
</div>
<span class="demo-title">Ihr Chatbot</span>
</div>
<div class="demo-messages">
<div class="message bot">
<div class="avatar">🤖</div>
<div class="bubble">Hallo! Wie kann ich Ihnen helfen?</div>
</div>
<div class="message user">
<div class="bubble">Was sind Ihre Öffnungszeiten?</div>
<div class="avatar">👤</div>
</div>
<div class="message bot">
<div class="avatar">🤖</div>
<div class="bubble">Wir sind Mo-Fr von 9-18 Uhr für Sie da!</div>
</div>
</div>
<div class="demo-input">
<input type="text" placeholder="Ihre Frage..." disabled>
<button disabled></button>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section id="features" class="features">
<div class="container">
<div class="section-header">
<h2>Leistungsstarke Features für Ihren Erfolg</h2>
<p>Alles, was Sie für einen professionellen KI-Chatbot benötigen</p>
</div>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">📄</div>
<h3>Dokument-Upload</h3>
<p>Laden Sie PDFs, Word-Dokumente oder Texte hoch. Ihr Chatbot lernt automatisch aus Ihren Inhalten.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🧠</div>
<h3>RAG-Technologie</h3>
<p>Modernste Retrieval-Augmented Generation für präzise und kontextbezogene Antworten.</p>
</div>
<div class="feature-card">
<div class="feature-icon"></div>
<h3>Blitzschnelle Integration</h3>
<p>Ein einfacher Code-Snippet - fertig! Ihr Chatbot ist in Minuten auf Ihrer Website.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🎨</div>
<h3>Anpassbares Design</h3>
<p>Passen Sie Farben, Logo und Texte an Ihre Marke an. Kein Entwickler erforderlich.</p>
</div>
<div class="feature-card">
<div class="feature-icon">📊</div>
<h3>Analytics & Insights</h3>
<p>Verstehen Sie Ihre Kunden besser mit detaillierten Statistiken und Gesprächsverläufen.</p>
</div>
<div class="feature-card">
<div class="feature-icon">🔒</div>
<h3>DSGVO-konform</h3>
<p>Ihre Daten bleiben in Deutschland. Volle DSGVO-Konformität garantiert.</p>
</div>
</div>
</div>
</section>
<!-- How It Works Section -->
<section id="how-it-works" class="how-it-works">
<div class="container">
<div class="section-header">
<h2>So einfach geht's</h2>
<p>In 4 Schritten zu Ihrem eigenen KI-Chatbot</p>
</div>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h3>Registrieren</h3>
<p>Erstellen Sie Ihr kostenloses Testkonto in wenigen Sekunden. Keine Kreditkarte erforderlich.</p>
</div>
</div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h3>Dokumente hochladen</h3>
<p>Laden Sie Ihre PDFs, FAQs oder andere Dokumente hoch. Unser System verarbeitet sie automatisch.</p>
</div>
</div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<h3>Code einbinden</h3>
<p>Kopieren Sie den generierten Code-Snippet und fügen Sie ihn auf Ihrer Website ein.</p>
</div>
</div>
<div class="step">
<div class="step-number">4</div>
<div class="step-content">
<h3>Fertig!</h3>
<p>Ihr KI-Chatbot ist live und beantwortet Kundenanfragen rund um die Uhr.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Pricing Section -->
<section id="pricing" class="pricing">
<div class="container">
<div class="section-header">
<h2>Transparente Preise</h2>
<p>Starten Sie kostenlos, upgraden Sie jederzeit</p>
</div>
<div class="pricing-grid">
<div class="pricing-card">
<div class="pricing-header">
<h3>Trial</h3>
<div class="price">
<span class="currency"></span>
<span class="amount">0</span>
<span class="period">/7 Tage</span>
</div>
</div>
<ul class="pricing-features">
<li>✓ Voller Funktionsumfang</li>
<li>✓ Bis zu 100 Dokumente</li>
<li>✓ 1.000 Nachrichten/Monat</li>
<li>✓ E-Mail-Support</li>
<li>✓ Keine Kreditkarte erforderlich</li>
</ul>
<a href="#trial" class="btn-primary btn-block">Jetzt starten</a>
</div>
<div class="pricing-card featured">
<div class="badge">Beliebt</div>
<div class="pricing-header">
<h3>Starter</h3>
<div class="price">
<span class="currency"></span>
<span class="amount">49</span>
<span class="period">/Monat</span>
</div>
<div class="price-note">Frühbucher-Rabatt: 30% in den ersten 3 Tagen</div>
</div>
<ul class="pricing-features">
<li>✓ Alle Trial-Features</li>
<li>✓ Unbegrenzte Dokumente</li>
<li>✓ 10.000 Nachrichten/Monat</li>
<li>✓ Prioritäts-Support</li>
<li>✓ Custom Branding</li>
<li>✓ Analytics Dashboard</li>
</ul>
<a href="#trial" class="btn-primary btn-block">Jetzt upgraden</a>
</div>
<div class="pricing-card">
<div class="pricing-header">
<h3>Business</h3>
<div class="price">
<span class="currency"></span>
<span class="amount">149</span>
<span class="period">/Monat</span>
</div>
</div>
<ul class="pricing-features">
<li>✓ Alle Starter-Features</li>
<li>✓ 50.000 Nachrichten/Monat</li>
<li>✓ Mehrere Chatbots</li>
<li>✓ API-Zugriff</li>
<li>✓ Dedizierter Support</li>
<li>✓ SLA-Garantie</li>
</ul>
<a href="#trial" class="btn-secondary btn-block">Kontakt aufnehmen</a>
</div>
</div>
</div>
</section>
<!-- Trial Registration Section -->
<section id="trial" class="trial-registration">
<div class="container">
<div class="trial-content">
<div class="trial-info">
<h2>Starten Sie Ihren 7-Tage-Test</h2>
<p>Keine Kreditkarte erforderlich. Voller Funktionsumfang. Jederzeit kündbar.</p>
<ul class="trial-benefits">
<li>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M20 6L9 17l-5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Sofortiger Zugang zu Ihrer Instanz</span>
</li>
<li>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M20 6L9 17l-5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Persönliche Einrichtungs-Hilfe</span>
</li>
<li>
<svg width="24" height="24" viewBox="0 0 24 24" fill="none">
<path d="M20 6L9 17l-5-5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
<span>Exklusive Frühbucher-Rabatte</span>
</li>
</ul>
</div>
<div class="trial-form-wrapper">
<form id="trialForm" class="trial-form">
<h3>Kostenlos registrieren</h3>
<div class="form-group">
<label for="firstName">Vorname *</label>
<input type="text" id="firstName" name="firstName" required>
</div>
<div class="form-group">
<label for="lastName">Nachname *</label>
<input type="text" id="lastName" name="lastName" required>
</div>
<div class="form-group">
<label for="email">E-Mail-Adresse *</label>
<input type="email" id="email" name="email" required>
</div>
<div class="form-group">
<label for="company">Firma (optional)</label>
<input type="text" id="company" name="company">
</div>
<div class="form-group checkbox">
<input type="checkbox" id="terms" name="terms" required>
<label for="terms">
Ich akzeptiere die <a href="terms.html" target="_blank">AGB</a> und <a href="privacy.html" target="_blank">Datenschutzerklärung</a>
</label>
</div>
<button type="submit" class="btn-primary btn-block btn-large">
<span class="btn-text">Jetzt kostenlos starten</span>
<span class="btn-loading" style="display: none;">
<svg class="spinner" width="20" height="20" viewBox="0 0 50 50">
<circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="5"></circle>
</svg>
Wird erstellt...
</span>
</button>
<p class="form-note">Nach der Registrierung erhalten Sie sofort Zugang zu Ihrer persönlichen Instanz.</p>
</form>
<div id="successMessage" class="success-message" style="display: none;">
<div class="success-icon"></div>
<h3>Willkommen bei BotKonzept!</h3>
<p>Ihre Instanz wird gerade erstellt. Sie erhalten in wenigen Minuten eine E-Mail mit allen Zugangsdaten.</p>
<div class="success-details">
<p><strong>Was passiert jetzt?</strong></p>
<ol>
<li>Wir erstellen Ihre persönliche KI-Instanz</li>
<li>Sie erhalten eine E-Mail mit Login-Daten</li>
<li>Sie können sofort loslegen!</li>
</ol>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ Section -->
<section id="faq" class="faq">
<div class="container">
<div class="section-header">
<h2>Häufig gestellte Fragen</h2>
<p>Alles, was Sie wissen müssen</p>
</div>
<div class="faq-grid">
<div class="faq-item">
<h3>Brauche ich technische Kenntnisse?</h3>
<p>Nein! Sie müssen nur einen Code-Snippet auf Ihrer Website einfügen können. Alles andere übernehmen wir für Sie.</p>
</div>
<div class="faq-item">
<h3>Wie lange dauert die Einrichtung?</h3>
<p>Nach der Registrierung ist Ihre Instanz in ca. 5 Minuten einsatzbereit. Das Hochladen von Dokumenten dauert je nach Menge 1-10 Minuten.</p>
</div>
<div class="faq-item">
<h3>Kann ich jederzeit kündigen?</h3>
<p>Ja, Sie können jederzeit kündigen. Es gibt keine Mindestlaufzeit und keine versteckten Kosten.</p>
</div>
<div class="faq-item">
<h3>Wo werden meine Daten gespeichert?</h3>
<p>Alle Daten werden in Deutschland auf DSGVO-konformen Servern gespeichert. Ihre Daten gehören Ihnen.</p>
</div>
<div class="faq-item">
<h3>Welche Sprachen werden unterstützt?</h3>
<p>Der Chatbot unterstützt Deutsch, Englisch und viele weitere Sprachen. Die KI erkennt die Sprache automatisch.</p>
</div>
<div class="faq-item">
<h3>Was passiert nach dem Trial?</h3>
<p>Sie erhalten am Tag 3, 5 und 7 E-Mails mit Upgrade-Angeboten. Wenn Sie nicht upgraden, wird Ihre Instanz nach 7 Tagen gelöscht.</p>
</div>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<img src="../20250119_Logo_Botkozept.svg" alt="BotKonzept Logo" height="30">
<p>KI-Chatbots für moderne Unternehmen</p>
</div>
<div class="footer-section">
<h4>Produkt</h4>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#pricing">Preise</a></li>
<li><a href="#faq">FAQ</a></li>
<li><a href="docs.html">Dokumentation</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Unternehmen</h4>
<ul>
<li><a href="about.html">Über uns</a></li>
<li><a href="contact.html">Kontakt</a></li>
<li><a href="blog.html">Blog</a></li>
</ul>
</div>
<div class="footer-section">
<h4>Rechtliches</h4>
<ul>
<li><a href="terms.html">AGB</a></li>
<li><a href="privacy.html">Datenschutz</a></li>
<li><a href="imprint.html">Impressum</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 BotKonzept. Alle Rechte vorbehalten.</p>
</div>
</div>
</footer>
<script src="js/main.js"></script>
</body>
</html>

View File

@@ -0,0 +1,388 @@
// BotKonzept Website - Main JavaScript
// =====================================
// Configuration
const CONFIG = {
// n8n Webhook URL für Registrierung (wird später konfiguriert)
WEBHOOK_URL: 'https://n8n.userman.de/webhook/botkonzept-registration',
// Supabase URL (lokale Instanz)
SUPABASE_URL: 'http://192.168.45.3:3000',
SUPABASE_ANON_KEY: 'your-anon-key-here'
};
// =====================================
// Mobile Menu Toggle
// =====================================
document.addEventListener('DOMContentLoaded', function() {
const mobileMenuToggle = document.querySelector('.mobile-menu-toggle');
const navMenu = document.querySelector('.nav-menu');
if (mobileMenuToggle) {
mobileMenuToggle.addEventListener('click', function() {
navMenu.classList.toggle('active');
this.classList.toggle('active');
});
}
// Close mobile menu when clicking on a link
const navLinks = document.querySelectorAll('.nav-menu a');
navLinks.forEach(link => {
link.addEventListener('click', function() {
navMenu.classList.remove('active');
mobileMenuToggle.classList.remove('active');
});
});
});
// =====================================
// Smooth Scroll for Anchor Links
// =====================================
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
const offset = 80; // Navbar height
const targetPosition = target.offsetTop - offset;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
// =====================================
// Trial Registration Form
// =====================================
const trialForm = document.getElementById('trialForm');
const successMessage = document.getElementById('successMessage');
if (trialForm) {
trialForm.addEventListener('submit', async function(e) {
e.preventDefault();
// Get form data
const formData = {
firstName: document.getElementById('firstName').value.trim(),
lastName: document.getElementById('lastName').value.trim(),
email: document.getElementById('email').value.trim().toLowerCase(),
company: document.getElementById('company').value.trim() || null,
terms: document.getElementById('terms').checked,
registeredAt: new Date().toISOString(),
source: 'website',
utmSource: getUrlParameter('utm_source') || null,
utmMedium: getUrlParameter('utm_medium') || null,
utmCampaign: getUrlParameter('utm_campaign') || null
};
// Validate
if (!formData.firstName || !formData.lastName || !formData.email) {
showError('Bitte füllen Sie alle Pflichtfelder aus.');
return;
}
if (!validateEmail(formData.email)) {
showError('Bitte geben Sie eine gültige E-Mail-Adresse ein.');
return;
}
if (!formData.terms) {
showError('Bitte akzeptieren Sie die AGB und Datenschutzerklärung.');
return;
}
// Show loading state
const submitButton = trialForm.querySelector('button[type="submit"]');
const btnText = submitButton.querySelector('.btn-text');
const btnLoading = submitButton.querySelector('.btn-loading');
submitButton.disabled = true;
btnText.style.display = 'none';
btnLoading.style.display = 'flex';
try {
// Send to n8n webhook
const response = await fetch(CONFIG.WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData)
});
if (!response.ok) {
throw new Error('Registrierung fehlgeschlagen');
}
const result = await response.json();
// Show success message
trialForm.style.display = 'none';
successMessage.style.display = 'block';
// Track conversion (Google Analytics, Facebook Pixel, etc.)
trackConversion('trial_registration', formData);
// Scroll to success message
successMessage.scrollIntoView({ behavior: 'smooth', block: 'center' });
} catch (error) {
console.error('Registration error:', error);
showError('Ein Fehler ist aufgetreten. Bitte versuchen Sie es später erneut oder kontaktieren Sie uns direkt.');
// Reset button state
submitButton.disabled = false;
btnText.style.display = 'inline';
btnLoading.style.display = 'none';
}
});
}
// =====================================
// Helper Functions
// =====================================
function validateEmail(email) {
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return re.test(email);
}
function showError(message) {
// Create or update error message
let errorDiv = document.querySelector('.form-error');
if (!errorDiv) {
errorDiv = document.createElement('div');
errorDiv.className = 'form-error';
errorDiv.style.cssText = `
background: #fee2e2;
border: 1px solid #ef4444;
color: #991b1b;
padding: 1rem;
border-radius: 0.5rem;
margin-bottom: 1rem;
font-size: 0.95rem;
`;
trialForm.insertBefore(errorDiv, trialForm.firstChild);
}
errorDiv.textContent = message;
errorDiv.scrollIntoView({ behavior: 'smooth', block: 'center' });
// Remove after 5 seconds
setTimeout(() => {
errorDiv.remove();
}, 5000);
}
function getUrlParameter(name) {
const urlParams = new URLSearchParams(window.location.search);
return urlParams.get(name);
}
function trackConversion(eventName, data) {
// Google Analytics
if (typeof gtag !== 'undefined') {
gtag('event', eventName, {
'event_category': 'registration',
'event_label': 'trial',
'value': 0
});
}
// Facebook Pixel
if (typeof fbq !== 'undefined') {
fbq('track', 'Lead', {
content_name: 'Trial Registration',
content_category: 'Registration'
});
}
// Custom tracking
console.log('Conversion tracked:', eventName, data);
}
// =====================================
// Scroll Animations
// =====================================
const observerOptions = {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
};
const observer = new IntersectionObserver(function(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
entry.target.classList.add('animate-in');
observer.unobserve(entry.target);
}
});
}, observerOptions);
// Observe elements for animation
document.addEventListener('DOMContentLoaded', function() {
const animateElements = document.querySelectorAll('.feature-card, .step, .pricing-card, .faq-item');
animateElements.forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.6s ease, transform 0.6s ease';
observer.observe(el);
});
});
// Add CSS for animation
const style = document.createElement('style');
style.textContent = `
.animate-in {
opacity: 1 !important;
transform: translateY(0) !important;
}
`;
document.head.appendChild(style);
// =====================================
// Pricing Calculator (Optional)
// =====================================
function calculatePricing(plan, discount = 0) {
const prices = {
trial: 0,
starter: 49,
business: 149
};
const basePrice = prices[plan] || 0;
const discountedPrice = basePrice * (1 - discount / 100);
return {
basePrice,
discount,
discountedPrice,
savings: basePrice - discountedPrice
};
}
// =====================================
// FAQ Accordion (Optional Enhancement)
// =====================================
document.querySelectorAll('.faq-item').forEach(item => {
item.addEventListener('click', function() {
this.classList.toggle('active');
});
});
// =====================================
// Newsletter Subscription (Optional)
// =====================================
function subscribeNewsletter(email) {
// Integration mit Sendy.co
const sendyUrl = 'https://sendy.userman.de/subscribe';
const formData = new FormData();
formData.append('email', email);
formData.append('list', 'your-list-id');
formData.append('boolean', 'true');
return fetch(sendyUrl, {
method: 'POST',
body: formData
});
}
// =====================================
// Live Chat Widget Integration (Optional)
// =====================================
function initializeChatWidget() {
// Hier kann später der eigene BotKonzept-Chatbot integriert werden
console.log('Chat widget initialized');
}
// =====================================
// A/B Testing Helper (Optional)
// =====================================
function getABTestVariant() {
const variants = ['A', 'B'];
const stored = localStorage.getItem('ab_variant');
if (stored) {
return stored;
}
const variant = variants[Math.floor(Math.random() * variants.length)];
localStorage.setItem('ab_variant', variant);
return variant;
}
// =====================================
// Cookie Consent (DSGVO)
// =====================================
function showCookieConsent() {
const consent = localStorage.getItem('cookie_consent');
if (!consent) {
const banner = document.createElement('div');
banner.className = 'cookie-banner';
banner.innerHTML = `
<div class="cookie-content">
<p>Wir verwenden Cookies, um Ihre Erfahrung zu verbessern.
<a href="privacy.html">Mehr erfahren</a></p>
<button onclick="acceptCookies()" class="btn-primary">Akzeptieren</button>
</div>
`;
banner.style.cssText = `
position: fixed;
bottom: 0;
left: 0;
right: 0;
background: #1f2937;
color: white;
padding: 1.5rem;
z-index: 9999;
box-shadow: 0 -4px 6px rgba(0,0,0,0.1);
`;
document.body.appendChild(banner);
}
}
function acceptCookies() {
localStorage.setItem('cookie_consent', 'true');
document.querySelector('.cookie-banner').remove();
}
// Show cookie consent on page load
document.addEventListener('DOMContentLoaded', showCookieConsent);
// =====================================
// Performance Monitoring
// =====================================
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
console.log('Performance:', entry.name, entry.duration);
}
});
observer.observe({ entryTypes: ['measure', 'navigation'] });
}
// =====================================
// Error Tracking
// =====================================
window.addEventListener('error', function(e) {
console.error('Global error:', e.error);
// Hier könnte Sentry oder ähnliches integriert werden
});
window.addEventListener('unhandledrejection', function(e) {
console.error('Unhandled promise rejection:', e.reason);
});
// =====================================
// Export for testing
// =====================================
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
validateEmail,
calculatePricing,
getUrlParameter
};
}

298
setup_botkonzept.sh Executable file
View File

@@ -0,0 +1,298 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# =====================================================
# BotKonzept Setup Script
# =====================================================
# This script sets up the complete BotKonzept platform
# including database, n8n workflows, and website
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Configuration
SUPABASE_HOST="${SUPABASE_HOST:-192.168.45.3}"
SUPABASE_PORT="${SUPABASE_PORT:-5432}"
SUPABASE_DB="${SUPABASE_DB:-customer}"
SUPABASE_USER="${SUPABASE_USER:-customer}"
SUPABASE_PASSWORD="${SUPABASE_PASSWORD:-}"
N8N_HOST="${N8N_HOST:-n8n.userman.de}"
WEBSITE_DIR="${WEBSITE_DIR:-/var/www/botkonzept}"
print_header() {
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}$1${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""
}
print_success() {
echo -e "${GREEN}${NC} $1"
}
print_error() {
echo -e "${RED}${NC} $1"
}
print_info() {
echo -e "${BLUE}${NC} $1"
}
print_warning() {
echo -e "${YELLOW}${NC} $1"
}
# Check prerequisites
check_prerequisites() {
print_header "Checking Prerequisites"
local missing=0
# Check psql
if command -v psql >/dev/null 2>&1; then
print_success "PostgreSQL client installed"
else
print_error "PostgreSQL client not found"
missing=1
fi
# Check curl
if command -v curl >/dev/null 2>&1; then
print_success "curl installed"
else
print_error "curl not found"
missing=1
fi
# Check jq
if command -v jq >/dev/null 2>&1; then
print_success "jq installed"
else
print_warning "jq not found (optional, but recommended)"
fi
if [[ $missing -eq 1 ]]; then
print_error "Missing required dependencies. Please install them first."
exit 1
fi
echo ""
}
# Setup database
setup_database() {
print_header "Setting up Database"
if [[ -z "$SUPABASE_PASSWORD" ]]; then
print_error "SUPABASE_PASSWORD not set"
echo "Please set the password:"
echo " export SUPABASE_PASSWORD='your-password'"
exit 1
fi
print_info "Connecting to PostgreSQL at ${SUPABASE_HOST}:${SUPABASE_PORT}"
# Test connection
if PGPASSWORD="$SUPABASE_PASSWORD" psql -h "$SUPABASE_HOST" -p "$SUPABASE_PORT" -U "$SUPABASE_USER" -d "$SUPABASE_DB" -c "SELECT 1" >/dev/null 2>&1; then
print_success "Database connection successful"
else
print_error "Cannot connect to database"
exit 1
fi
# Create schema
print_info "Creating BotKonzept schema..."
if PGPASSWORD="$SUPABASE_PASSWORD" psql -h "$SUPABASE_HOST" -p "$SUPABASE_PORT" -U "$SUPABASE_USER" -d "$SUPABASE_DB" -f "${SCRIPT_DIR}/sql/botkonzept_schema.sql" >/dev/null 2>&1; then
print_success "Schema created successfully"
else
print_error "Failed to create schema"
exit 1
fi
# Verify tables
print_info "Verifying tables..."
local tables=$(PGPASSWORD="$SUPABASE_PASSWORD" psql -h "$SUPABASE_HOST" -p "$SUPABASE_PORT" -U "$SUPABASE_USER" -d "$SUPABASE_DB" -tAc "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public' AND table_name IN ('customers', 'instances', 'subscriptions', 'payments', 'emails_sent')")
if [[ "$tables" -eq 5 ]]; then
print_success "All tables created successfully"
else
print_warning "Expected 5 tables, found $tables"
fi
echo ""
}
# Setup n8n workflows
setup_n8n_workflows() {
print_header "Setting up n8n Workflows"
print_info "n8n workflows need to be imported manually:"
echo ""
echo "1. Open n8n: https://${N8N_HOST}"
echo "2. Go to Workflows → Import from File"
echo "3. Import these files:"
echo " - ${SCRIPT_DIR}/BotKonzept-Customer-Registration-Workflow.json"
echo " - ${SCRIPT_DIR}/BotKonzept-Trial-Management-Workflow.json"
echo ""
echo "4. Configure credentials:"
echo " - SSH (PVE20): Private key for Proxmox"
echo " - PostgreSQL (Supabase): Host=${SUPABASE_HOST}, DB=${SUPABASE_DB}"
echo " - SMTP (Postfix/SES): Your email server"
echo ""
read -p "Press Enter when workflows are imported and configured..."
print_success "n8n workflows configured"
echo ""
}
# Setup website
setup_website() {
print_header "Setting up Website"
if [[ ! -d "${SCRIPT_DIR}/botkonzept-website" ]]; then
print_error "Website directory not found: ${SCRIPT_DIR}/botkonzept-website"
exit 1
fi
print_info "Website files location: ${SCRIPT_DIR}/botkonzept-website"
print_info "Target directory: ${WEBSITE_DIR}"
echo ""
# Update webhook URL in JavaScript
print_info "Updating webhook URL in JavaScript..."
local webhook_url="https://${N8N_HOST}/webhook/botkonzept-registration"
if [[ -f "${SCRIPT_DIR}/botkonzept-website/js/main.js" ]]; then
sed -i.bak "s|WEBHOOK_URL:.*|WEBHOOK_URL: '${webhook_url}',|" "${SCRIPT_DIR}/botkonzept-website/js/main.js"
print_success "Webhook URL updated to: ${webhook_url}"
fi
echo ""
print_info "To deploy the website, run:"
echo " sudo mkdir -p ${WEBSITE_DIR}"
echo " sudo cp -r ${SCRIPT_DIR}/botkonzept-website/* ${WEBSITE_DIR}/"
echo " sudo chown -R www-data:www-data ${WEBSITE_DIR}"
echo ""
read -p "Deploy website now? (y/N): " deploy
if [[ "$deploy" =~ ^[Yy]$ ]]; then
sudo mkdir -p "${WEBSITE_DIR}"
sudo cp -r "${SCRIPT_DIR}/botkonzept-website/"* "${WEBSITE_DIR}/"
sudo chown -R www-data:www-data "${WEBSITE_DIR}"
print_success "Website deployed to ${WEBSITE_DIR}"
else
print_info "Skipping website deployment"
fi
echo ""
}
# Test setup
test_setup() {
print_header "Testing Setup"
# Test database
print_info "Testing database connection..."
if PGPASSWORD="$SUPABASE_PASSWORD" psql -h "$SUPABASE_HOST" -p "$SUPABASE_PORT" -U "$SUPABASE_USER" -d "$SUPABASE_DB" -c "SELECT COUNT(*) FROM customers" >/dev/null 2>&1; then
print_success "Database accessible"
else
print_error "Database not accessible"
fi
# Test n8n webhook
print_info "Testing n8n webhook..."
local webhook_url="https://${N8N_HOST}/webhook/botkonzept-registration"
local response=$(curl -s -o /dev/null -w "%{http_code}" -X POST "$webhook_url" \
-H "Content-Type: application/json" \
-d '{"test": true}' 2>/dev/null || echo "000")
if [[ "$response" == "200" ]] || [[ "$response" == "400" ]]; then
print_success "n8n webhook accessible (HTTP $response)"
else
print_warning "n8n webhook returned HTTP $response"
fi
# Test website
if [[ -d "$WEBSITE_DIR" ]]; then
print_success "Website directory exists"
else
print_warning "Website not deployed yet"
fi
echo ""
}
# Generate summary
generate_summary() {
print_header "Setup Summary"
echo -e "${GREEN}✓ BotKonzept setup completed!${NC}"
echo ""
echo "Next steps:"
echo ""
echo "1. Configure DNS:"
echo " - Point botkonzept.de to your web server"
echo " - Configure SSL certificate (Let's Encrypt)"
echo ""
echo "2. Test registration:"
echo " - Visit https://botkonzept.de"
echo " - Fill out registration form"
echo " - Check email for welcome message"
echo ""
echo "3. Monitor workflows:"
echo " - n8n: https://${N8N_HOST}"
echo " - Check executions for errors"
echo ""
echo "4. Database access:"
echo " - Host: ${SUPABASE_HOST}"
echo " - Database: ${SUPABASE_DB}"
echo " - User: ${SUPABASE_USER}"
echo ""
echo "5. Useful queries:"
echo " - View customers: SELECT * FROM customer_overview;"
echo " - View trials: SELECT * FROM trials_expiring_soon;"
echo " - View emails: SELECT * FROM emails_sent ORDER BY sent_at DESC LIMIT 10;"
echo ""
echo "Documentation: ${SCRIPT_DIR}/BOTKONZEPT_README.md"
echo ""
}
# Main execution
main() {
clear
print_header "BotKonzept Setup"
echo "This script will set up the complete BotKonzept platform."
echo ""
echo "Components:"
echo " - PostgreSQL database schema"
echo " - n8n workflows (manual import)"
echo " - Website files"
echo ""
read -p "Continue? (y/N): " confirm
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
echo "Setup cancelled."
exit 0
fi
echo ""
check_prerequisites
setup_database
setup_n8n_workflows
setup_website
test_setup
generate_summary
}
# Run main function
main "$@"

444
sql/botkonzept_schema.sql Normal file
View File

@@ -0,0 +1,444 @@
-- =====================================================
-- BotKonzept - Database Schema for Customer Management
-- =====================================================
-- This schema manages customers, instances, emails, and payments
-- for the BotKonzept SaaS platform
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- =====================================================
-- Table: customers
-- =====================================================
-- Stores customer information and trial status
CREATE TABLE IF NOT EXISTS customers (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email VARCHAR(255) UNIQUE NOT NULL,
first_name VARCHAR(100) NOT NULL,
last_name VARCHAR(100) NOT NULL,
company VARCHAR(255),
phone VARCHAR(50),
-- Status tracking
status VARCHAR(50) DEFAULT 'trial' CHECK (status IN ('trial', 'active', 'cancelled', 'suspended', 'deleted')),
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
trial_end_date TIMESTAMPTZ,
subscription_start_date TIMESTAMPTZ,
subscription_end_date TIMESTAMPTZ,
-- Marketing tracking
utm_source VARCHAR(100),
utm_medium VARCHAR(100),
utm_campaign VARCHAR(100),
referral_code VARCHAR(50),
-- Metadata
metadata JSONB DEFAULT '{}'::jsonb,
-- Indexes
CONSTRAINT email_format CHECK (email ~* '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
);
-- Create indexes for customers
CREATE INDEX idx_customers_email ON customers(email);
CREATE INDEX idx_customers_status ON customers(status);
CREATE INDEX idx_customers_created_at ON customers(created_at);
CREATE INDEX idx_customers_trial_end_date ON customers(trial_end_date);
-- =====================================================
-- Table: instances
-- =====================================================
-- Stores LXC instance information for each customer
CREATE TABLE IF NOT EXISTS instances (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
-- Instance details
ctid BIGINT NOT NULL UNIQUE,
hostname VARCHAR(255) NOT NULL,
ip VARCHAR(50) NOT NULL,
fqdn VARCHAR(255) NOT NULL,
vlan INTEGER,
-- Status
status VARCHAR(50) DEFAULT 'active' CHECK (status IN ('creating', 'active', 'suspended', 'deleted', 'error')),
-- Credentials (encrypted JSON)
credentials JSONB NOT NULL,
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
deleted_at TIMESTAMPTZ,
trial_end_date TIMESTAMPTZ,
-- Resource usage
disk_usage_gb DECIMAL(10,2),
memory_usage_mb INTEGER,
cpu_usage_percent DECIMAL(5,2),
-- Metadata
metadata JSONB DEFAULT '{}'::jsonb
);
-- Create indexes for instances
CREATE INDEX idx_instances_customer_id ON instances(customer_id);
CREATE INDEX idx_instances_ctid ON instances(ctid);
CREATE INDEX idx_instances_status ON instances(status);
CREATE INDEX idx_instances_hostname ON instances(hostname);
-- =====================================================
-- Table: emails_sent
-- =====================================================
-- Tracks all emails sent to customers
CREATE TABLE IF NOT EXISTS emails_sent (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
-- Email details
email_type VARCHAR(50) NOT NULL CHECK (email_type IN (
'welcome',
'day3_upgrade',
'day5_reminder',
'day7_last_chance',
'day8_goodbye',
'payment_confirm',
'payment_failed',
'instance_created',
'instance_deleted',
'password_reset',
'newsletter'
)),
subject VARCHAR(255),
recipient_email VARCHAR(255) NOT NULL,
-- Status
status VARCHAR(50) DEFAULT 'sent' CHECK (status IN ('sent', 'delivered', 'opened', 'clicked', 'bounced', 'failed')),
-- Timestamps
sent_at TIMESTAMPTZ DEFAULT NOW(),
delivered_at TIMESTAMPTZ,
opened_at TIMESTAMPTZ,
clicked_at TIMESTAMPTZ,
-- Metadata
metadata JSONB DEFAULT '{}'::jsonb
);
-- Create indexes for emails_sent
CREATE INDEX idx_emails_customer_id ON emails_sent(customer_id);
CREATE INDEX idx_emails_type ON emails_sent(email_type);
CREATE INDEX idx_emails_sent_at ON emails_sent(sent_at);
CREATE INDEX idx_emails_status ON emails_sent(status);
-- =====================================================
-- Table: subscriptions
-- =====================================================
-- Stores subscription and payment information
CREATE TABLE IF NOT EXISTS subscriptions (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
-- Plan details
plan_name VARCHAR(50) NOT NULL CHECK (plan_name IN ('trial', 'starter', 'business', 'enterprise')),
plan_price DECIMAL(10,2) NOT NULL,
billing_cycle VARCHAR(20) DEFAULT 'monthly' CHECK (billing_cycle IN ('monthly', 'yearly')),
-- Discount
discount_percent DECIMAL(5,2) DEFAULT 0,
discount_code VARCHAR(50),
discount_end_date TIMESTAMPTZ,
-- Status
status VARCHAR(50) DEFAULT 'active' CHECK (status IN ('active', 'cancelled', 'past_due', 'suspended')),
-- Payment provider
payment_provider VARCHAR(50) CHECK (payment_provider IN ('stripe', 'paypal', 'manual')),
payment_provider_id VARCHAR(255),
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW(),
current_period_start TIMESTAMPTZ,
current_period_end TIMESTAMPTZ,
cancelled_at TIMESTAMPTZ,
-- Metadata
metadata JSONB DEFAULT '{}'::jsonb
);
-- Create indexes for subscriptions
CREATE INDEX idx_subscriptions_customer_id ON subscriptions(customer_id);
CREATE INDEX idx_subscriptions_status ON subscriptions(status);
CREATE INDEX idx_subscriptions_plan_name ON subscriptions(plan_name);
-- =====================================================
-- Table: payments
-- =====================================================
-- Stores payment transaction history
CREATE TABLE IF NOT EXISTS payments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
subscription_id UUID REFERENCES subscriptions(id) ON DELETE SET NULL,
-- Payment details
amount DECIMAL(10,2) NOT NULL,
currency VARCHAR(3) DEFAULT 'EUR',
-- Status
status VARCHAR(50) DEFAULT 'pending' CHECK (status IN ('pending', 'succeeded', 'failed', 'refunded', 'cancelled')),
-- Payment provider
payment_provider VARCHAR(50) CHECK (payment_provider IN ('stripe', 'paypal', 'manual')),
payment_provider_id VARCHAR(255),
payment_method VARCHAR(50),
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
paid_at TIMESTAMPTZ,
refunded_at TIMESTAMPTZ,
-- Invoice
invoice_number VARCHAR(50),
invoice_url TEXT,
-- Metadata
metadata JSONB DEFAULT '{}'::jsonb
);
-- Create indexes for payments
CREATE INDEX idx_payments_customer_id ON payments(customer_id);
CREATE INDEX idx_payments_subscription_id ON payments(subscription_id);
CREATE INDEX idx_payments_status ON payments(status);
CREATE INDEX idx_payments_created_at ON payments(created_at);
-- =====================================================
-- Table: usage_stats
-- =====================================================
-- Tracks usage statistics for each instance
CREATE TABLE IF NOT EXISTS usage_stats (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
instance_id UUID NOT NULL REFERENCES instances(id) ON DELETE CASCADE,
-- Usage metrics
date DATE NOT NULL,
messages_count INTEGER DEFAULT 0,
documents_count INTEGER DEFAULT 0,
api_calls_count INTEGER DEFAULT 0,
storage_used_mb DECIMAL(10,2) DEFAULT 0,
-- Timestamps
created_at TIMESTAMPTZ DEFAULT NOW(),
-- Unique constraint: one record per instance per day
UNIQUE(instance_id, date)
);
-- Create indexes for usage_stats
CREATE INDEX idx_usage_instance_id ON usage_stats(instance_id);
CREATE INDEX idx_usage_date ON usage_stats(date);
-- =====================================================
-- Table: audit_log
-- =====================================================
-- Audit trail for important actions
CREATE TABLE IF NOT EXISTS audit_log (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
customer_id UUID REFERENCES customers(id) ON DELETE SET NULL,
instance_id UUID REFERENCES instances(id) ON DELETE SET NULL,
-- Action details
action VARCHAR(100) NOT NULL,
entity_type VARCHAR(50),
entity_id UUID,
-- User/system that performed the action
performed_by VARCHAR(100),
ip_address INET,
user_agent TEXT,
-- Changes
old_values JSONB,
new_values JSONB,
-- Timestamp
created_at TIMESTAMPTZ DEFAULT NOW(),
-- Metadata
metadata JSONB DEFAULT '{}'::jsonb
);
-- Create indexes for audit_log
CREATE INDEX idx_audit_customer_id ON audit_log(customer_id);
CREATE INDEX idx_audit_instance_id ON audit_log(instance_id);
CREATE INDEX idx_audit_action ON audit_log(action);
CREATE INDEX idx_audit_created_at ON audit_log(created_at);
-- =====================================================
-- Functions & Triggers
-- =====================================================
-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Triggers for updated_at
CREATE TRIGGER update_customers_updated_at BEFORE UPDATE ON customers
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_instances_updated_at BEFORE UPDATE ON instances
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_subscriptions_updated_at BEFORE UPDATE ON subscriptions
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Function to calculate trial end date
CREATE OR REPLACE FUNCTION set_trial_end_date()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.trial_end_date IS NULL THEN
NEW.trial_end_date = NEW.created_at + INTERVAL '7 days';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
-- Trigger for trial end date
CREATE TRIGGER set_customer_trial_end_date BEFORE INSERT ON customers
FOR EACH ROW EXECUTE FUNCTION set_trial_end_date();
-- =====================================================
-- Views
-- =====================================================
-- View: Active trials expiring soon
CREATE OR REPLACE VIEW trials_expiring_soon AS
SELECT
c.id,
c.email,
c.first_name,
c.last_name,
c.created_at,
c.trial_end_date,
EXTRACT(DAY FROM (c.trial_end_date - NOW())) as days_remaining,
i.ctid,
i.hostname,
i.fqdn
FROM customers c
JOIN instances i ON c.id = i.customer_id
WHERE c.status = 'trial'
AND i.status = 'active'
AND c.trial_end_date > NOW()
AND c.trial_end_date <= NOW() + INTERVAL '3 days';
-- View: Customer overview with instance info
CREATE OR REPLACE VIEW customer_overview AS
SELECT
c.id,
c.email,
c.first_name,
c.last_name,
c.company,
c.status,
c.created_at,
c.trial_end_date,
i.ctid,
i.hostname,
i.fqdn,
i.ip,
i.status as instance_status,
s.plan_name,
s.plan_price,
s.status as subscription_status
FROM customers c
LEFT JOIN instances i ON c.id = i.customer_id AND i.status = 'active'
LEFT JOIN subscriptions s ON c.id = s.customer_id AND s.status = 'active';
-- View: Revenue metrics
CREATE OR REPLACE VIEW revenue_metrics AS
SELECT
DATE_TRUNC('month', paid_at) as month,
COUNT(*) as payment_count,
SUM(amount) as total_revenue,
AVG(amount) as average_payment,
COUNT(DISTINCT customer_id) as unique_customers
FROM payments
WHERE status = 'succeeded'
AND paid_at IS NOT NULL
GROUP BY DATE_TRUNC('month', paid_at)
ORDER BY month DESC;
-- =====================================================
-- Row Level Security (RLS) Policies
-- =====================================================
-- Enable RLS on tables
ALTER TABLE customers ENABLE ROW LEVEL SECURITY;
ALTER TABLE instances ENABLE ROW LEVEL SECURITY;
ALTER TABLE subscriptions ENABLE ROW LEVEL SECURITY;
ALTER TABLE payments ENABLE ROW LEVEL SECURITY;
-- Policy: Customers can only see their own data
CREATE POLICY customers_select_own ON customers
FOR SELECT
USING (auth.uid()::text = id::text);
CREATE POLICY instances_select_own ON instances
FOR SELECT
USING (customer_id::text = auth.uid()::text);
CREATE POLICY subscriptions_select_own ON subscriptions
FOR SELECT
USING (customer_id::text = auth.uid()::text);
CREATE POLICY payments_select_own ON payments
FOR SELECT
USING (customer_id::text = auth.uid()::text);
-- =====================================================
-- Sample Data (for testing)
-- =====================================================
-- Insert sample customer (commented out for production)
-- INSERT INTO customers (email, first_name, last_name, company, status)
-- VALUES ('test@example.com', 'Max', 'Mustermann', 'Test GmbH', 'trial');
-- =====================================================
-- Grants
-- =====================================================
-- Grant permissions to authenticated users
GRANT SELECT, INSERT, UPDATE ON customers TO authenticated;
GRANT SELECT ON instances TO authenticated;
GRANT SELECT ON subscriptions TO authenticated;
GRANT SELECT ON payments TO authenticated;
GRANT SELECT ON usage_stats TO authenticated;
-- Grant all permissions to service role (for n8n workflows)
GRANT ALL ON ALL TABLES IN SCHEMA public TO service_role;
GRANT ALL ON ALL SEQUENCES IN SCHEMA public TO service_role;
-- =====================================================
-- Comments
-- =====================================================
COMMENT ON TABLE customers IS 'Stores customer information and trial status';
COMMENT ON TABLE instances IS 'Stores LXC instance information for each customer';
COMMENT ON TABLE emails_sent IS 'Tracks all emails sent to customers';
COMMENT ON TABLE subscriptions IS 'Stores subscription and payment information';
COMMENT ON TABLE payments IS 'Stores payment transaction history';
COMMENT ON TABLE usage_stats IS 'Tracks usage statistics for each instance';
COMMENT ON TABLE audit_log IS 'Audit trail for important actions';
-- =====================================================
-- End of Schema
-- =====================================================