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:
75
20250119_Logo_Botkozept.svg
Normal file
75
20250119_Logo_Botkozept.svg
Normal 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
434
BOTKONZEPT_README.md
Normal 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
|
||||
312
BotKonzept-Customer-Registration-Workflow.json
Normal file
312
BotKonzept-Customer-Registration-Workflow.json
Normal 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"
|
||||
}
|
||||
122
BotKonzept-Trial-Management-Workflow.json
Normal file
122
BotKonzept-Trial-Management-Workflow.json
Normal 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
363
DEPLOYMENT_CHECKLIST.md
Normal 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
326
Install-Workflow01.json
Normal 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": []
|
||||
}
|
||||
884
botkonzept-website/css/style.css
Normal file
884
botkonzept-website/css/style.css
Normal 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;
|
||||
}
|
||||
}
|
||||
417
botkonzept-website/index.html
Normal file
417
botkonzept-website/index.html
Normal 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>© 2025 BotKonzept. Alle Rechte vorbehalten.</p>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="js/main.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
388
botkonzept-website/js/main.js
Normal file
388
botkonzept-website/js/main.js
Normal 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
298
setup_botkonzept.sh
Executable 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
444
sql/botkonzept_schema.sql
Normal 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
|
||||
-- =====================================================
|
||||
Reference in New Issue
Block a user