- Executive summary.
|
|
@ -1,146 +0,0 @@
|
||||||
# BotServer Implementation Status
|
|
||||||
|
|
||||||
## Current State
|
|
||||||
|
|
||||||
The BotServer system is fully operational with a clean separation between user interfaces and backend services.
|
|
||||||
|
|
||||||
## User Interfaces
|
|
||||||
|
|
||||||
### Suite Interface (`ui/suite/`)
|
|
||||||
Complete productivity workspace with integrated applications:
|
|
||||||
- Chat - AI conversation interface
|
|
||||||
- Drive - File storage and management
|
|
||||||
- Mail - Email client integration
|
|
||||||
- Meet - Video conferencing
|
|
||||||
- Tasks - Task management system
|
|
||||||
- Account - User settings and preferences
|
|
||||||
|
|
||||||
All functionality implemented using server-side rendering with minimal client-side JavaScript (~300 lines).
|
|
||||||
|
|
||||||
### Minimal Interface (`ui/minimal/`)
|
|
||||||
Single-page chat interface for simple deployments:
|
|
||||||
- Clean chat-only experience
|
|
||||||
- Voice input support
|
|
||||||
- File attachments
|
|
||||||
- Markdown rendering
|
|
||||||
- No additional applications
|
|
||||||
|
|
||||||
## Security Implementation
|
|
||||||
|
|
||||||
### Authentication
|
|
||||||
- Session-based authentication with secure cookies
|
|
||||||
- Directory service integration (Zitadel) for enterprise SSO
|
|
||||||
- Development mode for testing environments
|
|
||||||
- Automatic session management and refresh
|
|
||||||
|
|
||||||
### Data Protection
|
|
||||||
- TLS encryption for all connections
|
|
||||||
- Certificate generation during bootstrap
|
|
||||||
- Service-to-service mTLS communication
|
|
||||||
- Encrypted storage for sensitive data
|
|
||||||
|
|
||||||
## Bootstrap Components
|
|
||||||
|
|
||||||
The system automatically installs and manages these services:
|
|
||||||
- `tables` - PostgreSQL database
|
|
||||||
- `cache` - Redis caching layer
|
|
||||||
- `drive` - MinIO object storage
|
|
||||||
- `llm` - Language model runtime
|
|
||||||
- `email` - Mail service
|
|
||||||
- `proxy` - Reverse proxy
|
|
||||||
- `directory` - Zitadel authentication
|
|
||||||
- `alm` - Application lifecycle management
|
|
||||||
- `alm_ci` - Continuous integration
|
|
||||||
- `dns` - DNS service
|
|
||||||
- `meeting` - LiveKit video service
|
|
||||||
- `desktop` - Tauri desktop runtime
|
|
||||||
- `vector_db` - Qdrant vector database
|
|
||||||
- `host` - Host management
|
|
||||||
|
|
||||||
## Directory Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
botserver/
|
|
||||||
├── ui/
|
|
||||||
│ ├── suite/ # Full workspace interface
|
|
||||||
│ │ ├── index.html
|
|
||||||
│ │ ├── chat.html
|
|
||||||
│ │ ├── drive.html
|
|
||||||
│ │ ├── mail.html
|
|
||||||
│ │ ├── meet.html
|
|
||||||
│ │ ├── tasks.html
|
|
||||||
│ │ ├── account.html
|
|
||||||
│ │ └── js/
|
|
||||||
│ │ ├── htmx-app.js # Minimal initialization (300 lines)
|
|
||||||
│ │ └── theme-manager.js
|
|
||||||
│ └── minimal/ # Simple chat interface
|
|
||||||
│ ├── index.html
|
|
||||||
│ └── style.css
|
|
||||||
├── botserver-stack/ # Auto-installed components
|
|
||||||
│ ├── bin/ # Service binaries
|
|
||||||
│ ├── conf/ # Configuration files
|
|
||||||
│ ├── data/ # Service data
|
|
||||||
│ └── logs/ # Service logs
|
|
||||||
└── work/ # Bot packages deployment
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
The system uses directory-based configuration stored in Zitadel:
|
|
||||||
- Service credentials managed centrally
|
|
||||||
- No `.env` files in application directories
|
|
||||||
- Auto-generated secure credentials during bootstrap
|
|
||||||
- Certificate management for all services
|
|
||||||
|
|
||||||
## Documentation Structure
|
|
||||||
|
|
||||||
User-focused documentation organized by use case:
|
|
||||||
- **Chapter 1-3**: Getting started and concepts
|
|
||||||
- **Chapter 4**: User interface guide
|
|
||||||
- **Chapter 5**: Theme customization
|
|
||||||
- **Chapter 6**: Dialog scripting
|
|
||||||
- **Chapter 7**: Technical architecture (for developers)
|
|
||||||
- **Chapter 8-11**: Configuration and features
|
|
||||||
- **Chapter 12**: Security for end users
|
|
||||||
- **Chapter 13-14**: Community and migration
|
|
||||||
|
|
||||||
## Key Design Decisions
|
|
||||||
|
|
||||||
1. **Server-side rendering over client-side frameworks**
|
|
||||||
- Reduced complexity
|
|
||||||
- Better performance
|
|
||||||
- Simplified state management
|
|
||||||
|
|
||||||
2. **Directory service for configuration**
|
|
||||||
- Centralized credential management
|
|
||||||
- No scattered configuration files
|
|
||||||
- Enterprise-ready from the start
|
|
||||||
|
|
||||||
3. **Minimal JavaScript philosophy**
|
|
||||||
- 95% reduction in client-side code
|
|
||||||
- Essential functionality only
|
|
||||||
- Improved maintainability
|
|
||||||
|
|
||||||
4. **User-focused documentation**
|
|
||||||
- How to use, not how it works
|
|
||||||
- Technical details in developer sections
|
|
||||||
- Clear separation of concerns
|
|
||||||
|
|
||||||
## Production Readiness
|
|
||||||
|
|
||||||
### Complete
|
|
||||||
- User interfaces (suite and minimal)
|
|
||||||
- Authentication and security
|
|
||||||
- Service orchestration
|
|
||||||
- Documentation for users
|
|
||||||
- Bootstrap automation
|
|
||||||
|
|
||||||
### Deployment
|
|
||||||
- Single binary deployment
|
|
||||||
- Auto-installation of dependencies
|
|
||||||
- Self-contained operation
|
|
||||||
- No external configuration required
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
BotServer provides a complete, secure, and user-friendly platform for AI-powered productivity. The system emphasizes simplicity for users while maintaining enterprise-grade security and reliability. All components work together seamlessly with minimal configuration required.
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# Summary
|
# Summary
|
||||||
|
|
||||||
|
[🚀 Executive Vision](./executive-vision.md)
|
||||||
[Introduction](./introduction.md)
|
[Introduction](./introduction.md)
|
||||||
|
|
||||||
# Part I - Getting Started
|
# Part I - Getting Started
|
||||||
|
|
|
||||||
224
docs/src/executive-vision.md
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
# EXECUTIVE VISION: THE GENERAL BOTS REVOLUTION
|
||||||
|
|
||||||
|
## **OPEN SOURCE! OPEN SOURCE! OPEN SOURCE!**
|
||||||
|
|
||||||
|
Welcome to the future of enterprise automation. Welcome to **General Bots 6.1** - where you OWN your data, RUN your own cloud, and LAUGH at subscription fees!
|
||||||
|
|
||||||
|
## THE POWER TABLE: What General Bots Delivers TODAY
|
||||||
|
|
||||||
|
| **CAPABILITY** | **WHAT IT DOES** | **BUSINESS IMPACT** | **TIME TO VALUE** |
|
||||||
|
|----------------|------------------|---------------------|-------------------|
|
||||||
|
| **AI-POWERED CONVERSATIONS** | Multi-channel bot orchestration with LLM integration (GPT-4, Claude, Llama) | **90% reduction** in customer service costs | **< 1 hour** |
|
||||||
|
| **KNOWLEDGE BASES** | Vector-indexed document collections with semantic search (Qdrant/FAISS) | **10x faster** information retrieval | **15 minutes** |
|
||||||
|
| **EMAIL AUTOMATION** | Full IMAP/SMTP integration with intelligent routing | **Zero inbox** achieved automatically | **5 minutes** |
|
||||||
|
| **LLM-ASSISTED BASIC** | Plain English programming - LLM helps you write the code! | **NO programming skills needed** | **Immediate** |
|
||||||
|
| **DRIVE INTEGRATION** | S3-compatible storage with automatic document processing | **Unlimited scalability** | **2 minutes** |
|
||||||
|
| **ENTERPRISE SECURITY** | Argon2 hashing, JWT tokens, TLS everywhere | **Bank-grade security** out of the box | **Built-in** |
|
||||||
|
| **INSTANT THEMING** | CSS-based UI customization | **Your brand, your way** | **< 30 seconds** |
|
||||||
|
| **COMPLIANCE READY** | Built-in attendance, audit logs, GDPR support | **Zero compliance debt** | **Pre-configured** |
|
||||||
|
| **NVIDIA GPU SUPPORT** | CUDA acceleration for LLM operations | **50x faster** AI responses | **When available** |
|
||||||
|
| **OMNICHANNEL** | WhatsApp, Teams, Instagram, Web - ONE codebase | **Be everywhere** your customers are | **Single deploy** |
|
||||||
|
|
||||||
|
## GOODBYE OFFICE 365! GOODBYE GOOGLE WORKSPACE!
|
||||||
|
|
||||||
|
### **THE GREAT REPLACEMENT TABLE**
|
||||||
|
|
||||||
|
| **THEIR PRODUCT** | **THEIR COST** | **GENERAL BOTS REPLACEMENT** | **YOUR COST** |
|
||||||
|
|-------------------|----------------|-------------------------------|---------------|
|
||||||
|
| **Outlook/Gmail** | $12/user/month | **Email Module + LLM** | **$0 FOREVER** |
|
||||||
|
| **Teams/Meet** | $15/user/month | **Meet Module + WebRTC** | **$0 FOREVER** |
|
||||||
|
| **SharePoint/Drive** | $20/user/month | **Drive Module + S3** | **$0 FOREVER** |
|
||||||
|
| **Power Automate** | $40/user/month | **BASIC Scripts (LLM writes them!)** | **$0 FOREVER** |
|
||||||
|
| **Copilot/Gemini** | $30/user/month | **Local LLM (Llama/Mistral)** | **$0 FOREVER** |
|
||||||
|
| **Exchange Server** | Thousands | **Built-in Email Server** | **$0 FOREVER** |
|
||||||
|
| **Active Directory** | Thousands | **Directory Module** | **$0 FOREVER** |
|
||||||
|
| **OneDrive** | $10/user/month | **Personal Drives** | **$0 FOREVER** |
|
||||||
|
|
||||||
|
### **ANNUAL SAVINGS PER USER: Over $1,500**
|
||||||
|
**100 employees? That's over $150,000 EVERY YEAR back in YOUR pocket!**
|
||||||
|
|
||||||
|
## RUN IT ANYWHERE - ACCESS IT EVERYWHERE
|
||||||
|
|
||||||
|
### **Your Infrastructure Options**
|
||||||
|
|
||||||
|
**Option 1: Pragmatismo Hosts It**
|
||||||
|
- We handle everything
|
||||||
|
- Access from: YourCompany.pragmatismo.com.br
|
||||||
|
- Full management and support
|
||||||
|
- Your data remains YOURS
|
||||||
|
|
||||||
|
**Option 2: Your Own Hardware**
|
||||||
|
- That old RTX 3060? Perfect LLM accelerator!
|
||||||
|
- Your desktop with 16GB RAM handles 1000+ users
|
||||||
|
- A Raspberry Pi can run a satellite office
|
||||||
|
- Access from YOUR domain: bot.yourcompany.com
|
||||||
|
|
||||||
|
**Option 3: Hybrid Approach**
|
||||||
|
- Run locally, backup to secure cloud
|
||||||
|
- Export everything as ZIP anytime
|
||||||
|
- Move between hosting options freely
|
||||||
|
- No vendor lock-in, ever
|
||||||
|
|
||||||
|
### **Compare the Requirements:**
|
||||||
|
- **Microsoft 365:** Requires Azure subscription and ongoing fees
|
||||||
|
- **Google Workspace:** Requires Google Cloud and monthly payments
|
||||||
|
- **General Bots:** Requires... a computer that turns on
|
||||||
|
|
||||||
|
## THE ARCHITECTURE: Built for FREEDOM
|
||||||
|
|
||||||
|
| **COMPONENT** | **TECHNOLOGY** | **PERFORMANCE** |
|
||||||
|
|---------------|----------------|-----------------|
|
||||||
|
| **Core Runtime** | Rust + Tokio | **Millions** of concurrent connections |
|
||||||
|
| **Database** | PostgreSQL + Diesel | **Sub-millisecond** queries |
|
||||||
|
| **Vector Search** | Qdrant/FAISS | **100M+** documents indexed |
|
||||||
|
| **Caching** | Redis + Semantic Cache | **95% cache hit** ratio |
|
||||||
|
| **Message Queue** | Built-in async channels | **Zero latency** routing |
|
||||||
|
| **File Processing** | Parallel PDF/DOC extraction | **1000 docs/minute** |
|
||||||
|
| **Security Layer** | TLS 1.3 + Argon2 | **Quantum-resistant** ready |
|
||||||
|
|
||||||
|
## THE PHILOSOPHY: Why We WIN
|
||||||
|
|
||||||
|
### **1964 BASIC → 2024 FREEDOM**
|
||||||
|
At Dartmouth, they democratized computing. Today, we democratize EVERYTHING.
|
||||||
|
|
||||||
|
### **NO PROGRAMMING REQUIRED - LLM DOES IT FOR YOU**
|
||||||
|
|
||||||
|
The revolutionary truth about General Bots BASIC:
|
||||||
|
- Tell the LLM what you want in plain English
|
||||||
|
- LLM generates the BASIC code for you
|
||||||
|
- You review, adjust if needed (still in plain English!)
|
||||||
|
- Deploy immediately
|
||||||
|
|
||||||
|
Example conversation:
|
||||||
|
```
|
||||||
|
You: "I need to check emails every morning and summarize them"
|
||||||
|
LLM: "Here's your BASIC script:"
|
||||||
|
|
||||||
|
TALK "Checking morning emails..."
|
||||||
|
emails = GET EMAILS FROM "inbox"
|
||||||
|
FOR EACH email IN emails
|
||||||
|
summary = HEAR "Summarize this: " + email.content
|
||||||
|
TALK summary
|
||||||
|
NEXT
|
||||||
|
```
|
||||||
|
|
||||||
|
### **NO SUBSCRIPTIONS. NO SURVEILLANCE. NO SURRENDER.**
|
||||||
|
- No monthly fees bleeding you dry
|
||||||
|
- No data mining by big tech
|
||||||
|
- No "sorry, you've exceeded your API quota"
|
||||||
|
- No "please upgrade to Premium for this feature"
|
||||||
|
- **YOUR DATA. YOUR SERVERS. YOUR RULES.**
|
||||||
|
|
||||||
|
### **THE FREEDOM MANIFESTO**
|
||||||
|
```
|
||||||
|
AUTOMATION WITHOUT SUBSCRIPTION!
|
||||||
|
INTELLIGENCE WITHOUT SURVEILLANCE!
|
||||||
|
ENTERPRISE WITHOUT EXTORTION!
|
||||||
|
```
|
||||||
|
|
||||||
|
## REAL-WORLD DEPLOYMENTS
|
||||||
|
|
||||||
|
### **Pragmatismo Powers Organizations Globally**
|
||||||
|
|
||||||
|
From single-person businesses to thousand-worker enterprises, General Bots scales with you:
|
||||||
|
|
||||||
|
| **Organization Type** | **What They Replaced** | **Result** |
|
||||||
|
|----------------------|------------------------|------------|
|
||||||
|
| **Solo Consultant** | Notion + Gmail + Calendly | Complete business automation |
|
||||||
|
| **50-Person Law Firm** | Office 365 Enterprise | Full data sovereignty |
|
||||||
|
| **200-Bed Hospital** | Multiple vendor systems | Unified patient communication |
|
||||||
|
| **500-Student School** | Google Workspace Education | Complete digital campus |
|
||||||
|
| **1000-Worker Factory** | SAP + Exchange + Teams | Integrated operations platform |
|
||||||
|
|
||||||
|
## THE BOTTOM LINE
|
||||||
|
|
||||||
|
### **With General Bots, You Get:**
|
||||||
|
1. **EVERYTHING** Office 365 offers (and more!)
|
||||||
|
2. **ZERO** monthly fees - FOREVER
|
||||||
|
3. **100%** data ownership
|
||||||
|
4. **UNLIMITED** users, storage, compute
|
||||||
|
5. **YOUR** hostname.pragmatismo.com.br or your own domain
|
||||||
|
6. **LLM writes your automation** - no coding skills needed
|
||||||
|
|
||||||
|
### **Without General Bots, You're Stuck With:**
|
||||||
|
- Bleeding money monthly to Microsoft/Google
|
||||||
|
- Your data held hostage in their cloud
|
||||||
|
- Prices that only go UP, never down
|
||||||
|
- "Sorry, that feature requires Enterprise licensing"
|
||||||
|
- Every email, document, and chat being analyzed for "product improvement"
|
||||||
|
|
||||||
|
## START NOW - OWN YOUR FUTURE
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# THIS IS ALL IT TAKES:
|
||||||
|
cargo install general-bots
|
||||||
|
general-bots --init my-company
|
||||||
|
|
||||||
|
# Choose your hosting:
|
||||||
|
# Option 1: Use Pragmatismo's infrastructure
|
||||||
|
# Access: https://mycompany.pragmatismo.com.br
|
||||||
|
|
||||||
|
# Option 2: Self-host anywhere
|
||||||
|
# Access: https://bot.yourcompany.com
|
||||||
|
|
||||||
|
# Email server? Watch this:
|
||||||
|
general-bots --email-server --enable
|
||||||
|
# Full email with LLM filtering at ZERO COST
|
||||||
|
```
|
||||||
|
|
||||||
|
## THE SUBSCRIPTION COMPARISON
|
||||||
|
|
||||||
|
| **Organization Size** | **Big Tech Monthly Cost** | **10-Year Total** | **General Bots Cost** |
|
||||||
|
|----------------------|---------------------------|-------------------|-----------------------|
|
||||||
|
| **Small (10 users)** | ~$1,200/month | ~$144,000 | **$0** |
|
||||||
|
| **Medium (100 users)** | ~$12,000/month | ~$1,440,000 | **$0** |
|
||||||
|
| **Large (1000 users)** | ~$120,000/month | ~$14,400,000 | **$0** |
|
||||||
|
|
||||||
|
That's not just savings. That's LIBERATION.
|
||||||
|
|
||||||
|
## THE MOMENT OF TRUTH
|
||||||
|
|
||||||
|
### **THREE PATHS:**
|
||||||
|
|
||||||
|
1. **Keep paying** Microsoft/Google indefinitely
|
||||||
|
2. **Keep begging** for IT budget increases
|
||||||
|
3. **INSTALL GENERAL BOTS** and invest that money in growth
|
||||||
|
|
||||||
|
### **YOUR HARDWARE IS READY**
|
||||||
|
That RTX 3060 from three years ago? It runs enterprise LLM inference beautifully.
|
||||||
|
That decommissioned server? Perfect for 500 users.
|
||||||
|
Your laptop? Can handle development and testing.
|
||||||
|
|
||||||
|
## THE OPEN SOURCE ADVANTAGE
|
||||||
|
|
||||||
|
### **Complete Transparency:**
|
||||||
|
- Audit the code yourself
|
||||||
|
- Modify anything you need
|
||||||
|
- Contribute improvements back
|
||||||
|
- Join a global community
|
||||||
|
|
||||||
|
### **Deployment Flexibility:**
|
||||||
|
- Run on YOUR hardware
|
||||||
|
- Use Pragmatismo's infrastructure
|
||||||
|
- Deploy to any cloud provider
|
||||||
|
- Switch anytime - it's YOUR choice
|
||||||
|
|
||||||
|
### **Data Sovereignty:**
|
||||||
|
- Export everything as ZIP
|
||||||
|
- Backup anywhere you want
|
||||||
|
- Move between providers freely
|
||||||
|
- Delete when YOU decide
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**In 1964, BASIC freed programming from the priesthood. In 2024, General Bots frees enterprises from subscription slavery. With LLM assistance, ANYONE can build automation - no programming degree required. This isn't just software - it's INDEPENDENCE DAY!**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## READY? [Chapter 01: Run and Talk →](./chapter-01/README.md)
|
||||||
|
|
||||||
|
**10 minutes from now, you'll be running YOUR OWN enterprise stack. Office 365? Google Workspace? They'll be looking to YOU for innovation!**
|
||||||
|
|
||||||
|
**OPEN SOURCE! OWNERSHIP! SOVEREIGNTY!**
|
||||||
|
|
||||||
|
**P.S. - Take that money you're saving and invest it in your people, your products, or your future. It's YOUR money now!**
|
||||||
|
|
@ -1,519 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# TLS/HTTPS Setup Script for BotServer
|
|
||||||
# This script sets up a complete TLS infrastructure with internal CA and certificates for all services
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Colors for output
|
|
||||||
RED='\033[0;31m'
|
|
||||||
GREEN='\033[0;32m'
|
|
||||||
YELLOW='\033[1;33m'
|
|
||||||
BLUE='\033[0;34m'
|
|
||||||
NC='\033[0m' # No Color
|
|
||||||
|
|
||||||
# Configuration
|
|
||||||
CERT_DIR="./certs"
|
|
||||||
CA_DIR="$CERT_DIR/ca"
|
|
||||||
VALIDITY_DAYS=365
|
|
||||||
COUNTRY="BR"
|
|
||||||
STATE="SP"
|
|
||||||
LOCALITY="São Paulo"
|
|
||||||
ORGANIZATION="BotServer"
|
|
||||||
COMMON_NAME_SUFFIX="botserver.local"
|
|
||||||
|
|
||||||
# Services that need certificates
|
|
||||||
SERVICES=(
|
|
||||||
"api:8443:localhost,api.botserver.local,127.0.0.1"
|
|
||||||
"llm:8444:localhost,llm.botserver.local,127.0.0.1"
|
|
||||||
"embedding:8445:localhost,embedding.botserver.local,127.0.0.1"
|
|
||||||
"qdrant:6334:localhost,qdrant.botserver.local,127.0.0.1"
|
|
||||||
"redis:6380:localhost,redis.botserver.local,127.0.0.1"
|
|
||||||
"postgres:5433:localhost,postgres.botserver.local,127.0.0.1"
|
|
||||||
"minio:9001:localhost,minio.botserver.local,127.0.0.1"
|
|
||||||
"directory:8446:localhost,directory.botserver.local,127.0.0.1"
|
|
||||||
"email:465:localhost,email.botserver.local,127.0.0.1"
|
|
||||||
"meet:7881:localhost,meet.botserver.local,127.0.0.1"
|
|
||||||
)
|
|
||||||
|
|
||||||
echo -e "${BLUE}========================================${NC}"
|
|
||||||
echo -e "${BLUE}BotServer TLS/HTTPS Setup${NC}"
|
|
||||||
echo -e "${BLUE}========================================${NC}"
|
|
||||||
|
|
||||||
# Function to check if OpenSSL is installed
|
|
||||||
check_openssl() {
|
|
||||||
if ! command -v openssl &> /dev/null; then
|
|
||||||
echo -e "${RED}OpenSSL is not installed. Please install it first.${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
echo -e "${GREEN}✓ OpenSSL found${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to create directory structure
|
|
||||||
create_directories() {
|
|
||||||
echo -e "${YELLOW}Creating certificate directories...${NC}"
|
|
||||||
|
|
||||||
mkdir -p "$CA_DIR"
|
|
||||||
mkdir -p "$CA_DIR/private"
|
|
||||||
mkdir -p "$CA_DIR/certs"
|
|
||||||
mkdir -p "$CA_DIR/crl"
|
|
||||||
mkdir -p "$CA_DIR/newcerts"
|
|
||||||
|
|
||||||
# Create directories for each service
|
|
||||||
for service_config in "${SERVICES[@]}"; do
|
|
||||||
IFS=':' read -r service port sans <<< "$service_config"
|
|
||||||
mkdir -p "$CERT_DIR/$service"
|
|
||||||
done
|
|
||||||
|
|
||||||
# Create CA database files
|
|
||||||
touch "$CA_DIR/index.txt"
|
|
||||||
echo "1000" > "$CA_DIR/serial"
|
|
||||||
echo "1000" > "$CA_DIR/crlnumber"
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ Directories created${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to create CA configuration
|
|
||||||
create_ca_config() {
|
|
||||||
echo -e "${YELLOW}Creating CA configuration...${NC}"
|
|
||||||
|
|
||||||
cat > "$CA_DIR/ca.conf" << EOF
|
|
||||||
[ ca ]
|
|
||||||
default_ca = CA_default
|
|
||||||
|
|
||||||
[ CA_default ]
|
|
||||||
dir = $CA_DIR
|
|
||||||
certs = \$dir/certs
|
|
||||||
crl_dir = \$dir/crl
|
|
||||||
new_certs_dir = \$dir/newcerts
|
|
||||||
database = \$dir/index.txt
|
|
||||||
serial = \$dir/serial
|
|
||||||
crlnumber = \$dir/crlnumber
|
|
||||||
crl = \$dir/crl.pem
|
|
||||||
certificate = \$dir/ca.crt
|
|
||||||
private_key = \$dir/private/ca.key
|
|
||||||
RANDFILE = \$dir/private/.rand
|
|
||||||
x509_extensions = usr_cert
|
|
||||||
name_opt = ca_default
|
|
||||||
cert_opt = ca_default
|
|
||||||
default_days = $VALIDITY_DAYS
|
|
||||||
default_crl_days = 30
|
|
||||||
default_md = sha256
|
|
||||||
preserve = no
|
|
||||||
policy = policy_loose
|
|
||||||
|
|
||||||
[ policy_loose ]
|
|
||||||
countryName = optional
|
|
||||||
stateOrProvinceName = optional
|
|
||||||
localityName = optional
|
|
||||||
organizationName = optional
|
|
||||||
organizationalUnitName = optional
|
|
||||||
commonName = supplied
|
|
||||||
emailAddress = optional
|
|
||||||
|
|
||||||
[ req ]
|
|
||||||
default_bits = 4096
|
|
||||||
default_keyfile = privkey.pem
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
attributes = req_attributes
|
|
||||||
x509_extensions = v3_ca
|
|
||||||
string_mask = utf8only
|
|
||||||
default_md = sha256
|
|
||||||
|
|
||||||
[ req_distinguished_name ]
|
|
||||||
countryName = Country Name (2 letter code)
|
|
||||||
countryName_default = $COUNTRY
|
|
||||||
stateOrProvinceName = State or Province Name (full name)
|
|
||||||
stateOrProvinceName_default = $STATE
|
|
||||||
localityName = Locality Name (eg, city)
|
|
||||||
localityName_default = $LOCALITY
|
|
||||||
organizationName = Organization Name (eg, company)
|
|
||||||
organizationName_default = $ORGANIZATION
|
|
||||||
organizationalUnitName = Organizational Unit Name (eg, section)
|
|
||||||
commonName = Common Name (e.g. server FQDN or YOUR name)
|
|
||||||
emailAddress = Email Address
|
|
||||||
|
|
||||||
[ req_attributes ]
|
|
||||||
challengePassword = A challenge password
|
|
||||||
challengePassword_min = 4
|
|
||||||
challengePassword_max = 20
|
|
||||||
unstructuredName = An optional company name
|
|
||||||
|
|
||||||
[ v3_ca ]
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
authorityKeyIdentifier = keyid:always,issuer
|
|
||||||
basicConstraints = critical,CA:true
|
|
||||||
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
|
||||||
|
|
||||||
[ v3_intermediate_ca ]
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
authorityKeyIdentifier = keyid:always,issuer
|
|
||||||
basicConstraints = critical, CA:true, pathlen:0
|
|
||||||
keyUsage = critical, digitalSignature, cRLSign, keyCertSign
|
|
||||||
|
|
||||||
[ usr_cert ]
|
|
||||||
basicConstraints = CA:FALSE
|
|
||||||
nsCertType = client, email
|
|
||||||
nsComment = "OpenSSL Generated Client Certificate"
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
authorityKeyIdentifier = keyid,issuer
|
|
||||||
keyUsage = critical, nonRepudiation, digitalSignature, keyEncipherment
|
|
||||||
extendedKeyUsage = clientAuth, emailProtection
|
|
||||||
|
|
||||||
[ server_cert ]
|
|
||||||
basicConstraints = CA:FALSE
|
|
||||||
nsCertType = server
|
|
||||||
nsComment = "OpenSSL Generated Server Certificate"
|
|
||||||
subjectKeyIdentifier = hash
|
|
||||||
authorityKeyIdentifier = keyid,issuer:always
|
|
||||||
keyUsage = critical, digitalSignature, keyEncipherment
|
|
||||||
extendedKeyUsage = serverAuth
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ CA configuration created${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to generate Root CA
|
|
||||||
generate_root_ca() {
|
|
||||||
echo -e "${YELLOW}Generating Root CA...${NC}"
|
|
||||||
|
|
||||||
if [ -f "$CA_DIR/ca.crt" ] && [ -f "$CA_DIR/private/ca.key" ]; then
|
|
||||||
echo -e "${YELLOW}Root CA already exists, skipping...${NC}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate Root CA private key
|
|
||||||
openssl genrsa -out "$CA_DIR/private/ca.key" 4096
|
|
||||||
chmod 400 "$CA_DIR/private/ca.key"
|
|
||||||
|
|
||||||
# Generate Root CA certificate
|
|
||||||
openssl req -config "$CA_DIR/ca.conf" \
|
|
||||||
-key "$CA_DIR/private/ca.key" \
|
|
||||||
-new -x509 -days 7300 -sha256 -extensions v3_ca \
|
|
||||||
-out "$CA_DIR/ca.crt" \
|
|
||||||
-subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/CN=BotServer Root CA"
|
|
||||||
|
|
||||||
# Copy CA cert to main cert directory for easy access
|
|
||||||
cp "$CA_DIR/ca.crt" "$CERT_DIR/ca.crt"
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ Root CA generated${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to generate Intermediate CA
|
|
||||||
generate_intermediate_ca() {
|
|
||||||
echo -e "${YELLOW}Generating Intermediate CA...${NC}"
|
|
||||||
|
|
||||||
if [ -f "$CA_DIR/intermediate.crt" ] && [ -f "$CA_DIR/private/intermediate.key" ]; then
|
|
||||||
echo -e "${YELLOW}Intermediate CA already exists, skipping...${NC}"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Generate Intermediate CA private key
|
|
||||||
openssl genrsa -out "$CA_DIR/private/intermediate.key" 4096
|
|
||||||
chmod 400 "$CA_DIR/private/intermediate.key"
|
|
||||||
|
|
||||||
# Generate Intermediate CA CSR
|
|
||||||
openssl req -config "$CA_DIR/ca.conf" \
|
|
||||||
-new -sha256 \
|
|
||||||
-key "$CA_DIR/private/intermediate.key" \
|
|
||||||
-out "$CA_DIR/intermediate.csr" \
|
|
||||||
-subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/CN=BotServer Intermediate CA"
|
|
||||||
|
|
||||||
# Sign Intermediate CA certificate with Root CA
|
|
||||||
openssl ca -config "$CA_DIR/ca.conf" \
|
|
||||||
-extensions v3_intermediate_ca \
|
|
||||||
-days 3650 -notext -md sha256 \
|
|
||||||
-in "$CA_DIR/intermediate.csr" \
|
|
||||||
-out "$CA_DIR/intermediate.crt" \
|
|
||||||
-batch
|
|
||||||
|
|
||||||
chmod 444 "$CA_DIR/intermediate.crt"
|
|
||||||
|
|
||||||
# Create certificate chain
|
|
||||||
cat "$CA_DIR/intermediate.crt" "$CA_DIR/ca.crt" > "$CA_DIR/ca-chain.crt"
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ Intermediate CA generated${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to generate service certificate
|
|
||||||
generate_service_cert() {
|
|
||||||
local service=$1
|
|
||||||
local port=$2
|
|
||||||
local sans=$3
|
|
||||||
|
|
||||||
echo -e "${YELLOW}Generating certificates for $service...${NC}"
|
|
||||||
|
|
||||||
local cert_dir="$CERT_DIR/$service"
|
|
||||||
|
|
||||||
# Create SAN configuration
|
|
||||||
cat > "$cert_dir/san.conf" << EOF
|
|
||||||
[req]
|
|
||||||
distinguished_name = req_distinguished_name
|
|
||||||
req_extensions = v3_req
|
|
||||||
|
|
||||||
[req_distinguished_name]
|
|
||||||
|
|
||||||
[v3_req]
|
|
||||||
basicConstraints = CA:FALSE
|
|
||||||
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
|
|
||||||
subjectAltName = @alt_names
|
|
||||||
|
|
||||||
[alt_names]
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Add SANs
|
|
||||||
IFS=',' read -ra SAN_ARRAY <<< "$sans"
|
|
||||||
local san_index=1
|
|
||||||
for san in "${SAN_ARRAY[@]}"; do
|
|
||||||
if [[ $san =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
|
||||||
echo "IP.$san_index = $san" >> "$cert_dir/san.conf"
|
|
||||||
else
|
|
||||||
echo "DNS.$san_index = $san" >> "$cert_dir/san.conf"
|
|
||||||
fi
|
|
||||||
((san_index++))
|
|
||||||
done
|
|
||||||
|
|
||||||
# Generate server private key
|
|
||||||
openssl genrsa -out "$cert_dir/server.key" 2048
|
|
||||||
chmod 400 "$cert_dir/server.key"
|
|
||||||
|
|
||||||
# Generate server CSR
|
|
||||||
openssl req -new -sha256 \
|
|
||||||
-key "$cert_dir/server.key" \
|
|
||||||
-out "$cert_dir/server.csr" \
|
|
||||||
-config "$cert_dir/san.conf" \
|
|
||||||
-subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/CN=$service.$COMMON_NAME_SUFFIX"
|
|
||||||
|
|
||||||
# Sign server certificate with CA
|
|
||||||
openssl x509 -req -in "$cert_dir/server.csr" \
|
|
||||||
-CA "$CA_DIR/ca.crt" \
|
|
||||||
-CAkey "$CA_DIR/private/ca.key" \
|
|
||||||
-CAcreateserial \
|
|
||||||
-out "$cert_dir/server.crt" \
|
|
||||||
-days $VALIDITY_DAYS \
|
|
||||||
-sha256 \
|
|
||||||
-extensions v3_req \
|
|
||||||
-extfile "$cert_dir/san.conf"
|
|
||||||
|
|
||||||
# Generate client certificate for mTLS
|
|
||||||
openssl genrsa -out "$cert_dir/client.key" 2048
|
|
||||||
chmod 400 "$cert_dir/client.key"
|
|
||||||
|
|
||||||
openssl req -new -sha256 \
|
|
||||||
-key "$cert_dir/client.key" \
|
|
||||||
-out "$cert_dir/client.csr" \
|
|
||||||
-subj "/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORGANIZATION/CN=$service-client.$COMMON_NAME_SUFFIX"
|
|
||||||
|
|
||||||
openssl x509 -req -in "$cert_dir/client.csr" \
|
|
||||||
-CA "$CA_DIR/ca.crt" \
|
|
||||||
-CAkey "$CA_DIR/private/ca.key" \
|
|
||||||
-CAcreateserial \
|
|
||||||
-out "$cert_dir/client.crt" \
|
|
||||||
-days $VALIDITY_DAYS \
|
|
||||||
-sha256
|
|
||||||
|
|
||||||
# Copy CA certificate to service directory
|
|
||||||
cp "$CA_DIR/ca.crt" "$cert_dir/ca.crt"
|
|
||||||
|
|
||||||
# Create full chain certificate
|
|
||||||
cat "$cert_dir/server.crt" "$CA_DIR/ca.crt" > "$cert_dir/fullchain.crt"
|
|
||||||
|
|
||||||
# Clean up CSR files
|
|
||||||
rm -f "$cert_dir/server.csr" "$cert_dir/client.csr" "$cert_dir/san.conf"
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ Certificates generated for $service (port $port)${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to generate all service certificates
|
|
||||||
generate_all_service_certs() {
|
|
||||||
echo -e "${BLUE}Generating certificates for all services...${NC}"
|
|
||||||
|
|
||||||
for service_config in "${SERVICES[@]}"; do
|
|
||||||
IFS=':' read -r service port sans <<< "$service_config"
|
|
||||||
generate_service_cert "$service" "$port" "$sans"
|
|
||||||
done
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ All service certificates generated${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to create TLS configuration file
|
|
||||||
create_tls_config() {
|
|
||||||
echo -e "${YELLOW}Creating TLS configuration file...${NC}"
|
|
||||||
|
|
||||||
cat > "$CERT_DIR/tls-config.toml" << EOF
|
|
||||||
# TLS Configuration for BotServer
|
|
||||||
# Generated on $(date)
|
|
||||||
|
|
||||||
[tls]
|
|
||||||
enabled = true
|
|
||||||
mtls_enabled = true
|
|
||||||
auto_generate_certs = true
|
|
||||||
renewal_threshold_days = 30
|
|
||||||
|
|
||||||
[ca]
|
|
||||||
ca_cert_path = "$CA_DIR/ca.crt"
|
|
||||||
ca_key_path = "$CA_DIR/private/ca.key"
|
|
||||||
intermediate_cert_path = "$CA_DIR/intermediate.crt"
|
|
||||||
intermediate_key_path = "$CA_DIR/private/intermediate.key"
|
|
||||||
validity_days = $VALIDITY_DAYS
|
|
||||||
organization = "$ORGANIZATION"
|
|
||||||
country = "$COUNTRY"
|
|
||||||
state = "$STATE"
|
|
||||||
locality = "$LOCALITY"
|
|
||||||
|
|
||||||
# Service configurations
|
|
||||||
EOF
|
|
||||||
|
|
||||||
for service_config in "${SERVICES[@]}"; do
|
|
||||||
IFS=':' read -r service port sans <<< "$service_config"
|
|
||||||
cat >> "$CERT_DIR/tls-config.toml" << EOF
|
|
||||||
|
|
||||||
[[services]]
|
|
||||||
name = "$service"
|
|
||||||
port = $port
|
|
||||||
cert_path = "$CERT_DIR/$service/server.crt"
|
|
||||||
key_path = "$CERT_DIR/$service/server.key"
|
|
||||||
client_cert_path = "$CERT_DIR/$service/client.crt"
|
|
||||||
client_key_path = "$CERT_DIR/$service/client.key"
|
|
||||||
ca_cert_path = "$CERT_DIR/$service/ca.crt"
|
|
||||||
sans = "$sans"
|
|
||||||
EOF
|
|
||||||
done
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ TLS configuration file created${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to display certificate information
|
|
||||||
display_cert_info() {
|
|
||||||
echo -e "${BLUE}========================================${NC}"
|
|
||||||
echo -e "${BLUE}Certificate Information${NC}"
|
|
||||||
echo -e "${BLUE}========================================${NC}"
|
|
||||||
|
|
||||||
echo -e "${YELLOW}Root CA:${NC}"
|
|
||||||
openssl x509 -in "$CA_DIR/ca.crt" -noout -subject -dates
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}Service Certificates:${NC}"
|
|
||||||
for service_config in "${SERVICES[@]}"; do
|
|
||||||
IFS=':' read -r service port sans <<< "$service_config"
|
|
||||||
echo -e "${GREEN}$service (port $port):${NC}"
|
|
||||||
openssl x509 -in "$CERT_DIR/$service/server.crt" -noout -subject -dates
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to create environment variables file
|
|
||||||
create_env_vars() {
|
|
||||||
echo -e "${YELLOW}Creating environment variables file...${NC}"
|
|
||||||
|
|
||||||
cat > "$CERT_DIR/tls.env" << EOF
|
|
||||||
# TLS Environment Variables for BotServer
|
|
||||||
# Source this file to set TLS environment variables
|
|
||||||
|
|
||||||
export TLS_ENABLED=true
|
|
||||||
export MTLS_ENABLED=true
|
|
||||||
export CA_CERT_PATH="$CA_DIR/ca.crt"
|
|
||||||
export CA_KEY_PATH="$CA_DIR/private/ca.key"
|
|
||||||
|
|
||||||
# Service-specific TLS settings
|
|
||||||
export API_TLS_PORT=8443
|
|
||||||
export API_CERT_PATH="$CERT_DIR/api/server.crt"
|
|
||||||
export API_KEY_PATH="$CERT_DIR/api/server.key"
|
|
||||||
|
|
||||||
export LLM_TLS_PORT=8444
|
|
||||||
export LLM_CERT_PATH="$CERT_DIR/llm/server.crt"
|
|
||||||
export LLM_KEY_PATH="$CERT_DIR/llm/server.key"
|
|
||||||
|
|
||||||
export EMBEDDING_TLS_PORT=8445
|
|
||||||
export EMBEDDING_CERT_PATH="$CERT_DIR/embedding/server.crt"
|
|
||||||
export EMBEDDING_KEY_PATH="$CERT_DIR/embedding/server.key"
|
|
||||||
|
|
||||||
export QDRANT_TLS_PORT=6334
|
|
||||||
export QDRANT_CERT_PATH="$CERT_DIR/qdrant/server.crt"
|
|
||||||
export QDRANT_KEY_PATH="$CERT_DIR/qdrant/server.key"
|
|
||||||
|
|
||||||
export REDIS_TLS_PORT=6380
|
|
||||||
export REDIS_CERT_PATH="$CERT_DIR/redis/server.crt"
|
|
||||||
export REDIS_KEY_PATH="$CERT_DIR/redis/server.key"
|
|
||||||
|
|
||||||
export POSTGRES_TLS_PORT=5433
|
|
||||||
export POSTGRES_CERT_PATH="$CERT_DIR/postgres/server.crt"
|
|
||||||
export POSTGRES_KEY_PATH="$CERT_DIR/postgres/server.key"
|
|
||||||
|
|
||||||
export MINIO_TLS_PORT=9001
|
|
||||||
export MINIO_CERT_PATH="$CERT_DIR/minio/server.crt"
|
|
||||||
export MINIO_KEY_PATH="$CERT_DIR/minio/server.key"
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo -e "${GREEN}✓ Environment variables file created${NC}"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Function to test certificate validity
|
|
||||||
test_certificates() {
|
|
||||||
echo -e "${BLUE}Testing certificate validity...${NC}"
|
|
||||||
|
|
||||||
local all_valid=true
|
|
||||||
|
|
||||||
for service_config in "${SERVICES[@]}"; do
|
|
||||||
IFS=':' read -r service port sans <<< "$service_config"
|
|
||||||
|
|
||||||
# Verify certificate chain
|
|
||||||
if openssl verify -CAfile "$CA_DIR/ca.crt" "$CERT_DIR/$service/server.crt" > /dev/null 2>&1; then
|
|
||||||
echo -e "${GREEN}✓ $service server certificate is valid${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${RED}✗ $service server certificate is invalid${NC}"
|
|
||||||
all_valid=false
|
|
||||||
fi
|
|
||||||
|
|
||||||
if openssl verify -CAfile "$CA_DIR/ca.crt" "$CERT_DIR/$service/client.crt" > /dev/null 2>&1; then
|
|
||||||
echo -e "${GREEN}✓ $service client certificate is valid${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${RED}✗ $service client certificate is invalid${NC}"
|
|
||||||
all_valid=false
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
|
|
||||||
if $all_valid; then
|
|
||||||
echo -e "${GREEN}✓ All certificates are valid${NC}"
|
|
||||||
else
|
|
||||||
echo -e "${RED}Some certificates failed validation${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Main execution
|
|
||||||
main() {
|
|
||||||
check_openssl
|
|
||||||
create_directories
|
|
||||||
create_ca_config
|
|
||||||
generate_root_ca
|
|
||||||
# Intermediate CA is optional but recommended
|
|
||||||
# generate_intermediate_ca
|
|
||||||
generate_all_service_certs
|
|
||||||
create_tls_config
|
|
||||||
create_env_vars
|
|
||||||
test_certificates
|
|
||||||
display_cert_info
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo -e "${GREEN}========================================${NC}"
|
|
||||||
echo -e "${GREEN}TLS Setup Complete!${NC}"
|
|
||||||
echo -e "${GREEN}========================================${NC}"
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}Next steps:${NC}"
|
|
||||||
echo "1. Source the environment variables: source $CERT_DIR/tls.env"
|
|
||||||
echo "2. Update your service configurations to use HTTPS/TLS"
|
|
||||||
echo "3. Restart all services with TLS enabled"
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}Important files:${NC}"
|
|
||||||
echo " CA Certificate: $CA_DIR/ca.crt"
|
|
||||||
echo " TLS Config: $CERT_DIR/tls-config.toml"
|
|
||||||
echo " Environment Variables: $CERT_DIR/tls.env"
|
|
||||||
echo ""
|
|
||||||
echo -e "${YELLOW}To trust the CA certificate on your system:${NC}"
|
|
||||||
echo " Ubuntu/Debian: sudo cp $CA_DIR/ca.crt /usr/local/share/ca-certificates/botserver-ca.crt && sudo update-ca-certificates"
|
|
||||||
echo " RHEL/CentOS: sudo cp $CA_DIR/ca.crt /etc/pki/ca-trust/source/anchors/ && sudo update-ca-trust"
|
|
||||||
echo " macOS: sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain $CA_DIR/ca.crt"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Run main function
|
|
||||||
main
|
|
||||||
|
|
@ -1,247 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>WebSocket Message Handler Test</title>
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: monospace;
|
|
||||||
padding: 20px;
|
|
||||||
max-width: 1200px;
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
.test-section {
|
|
||||||
margin: 20px 0;
|
|
||||||
padding: 15px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
.test-case {
|
|
||||||
margin: 10px 0;
|
|
||||||
padding: 10px;
|
|
||||||
background: #f5f5f5;
|
|
||||||
}
|
|
||||||
.success {
|
|
||||||
color: green;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.error {
|
|
||||||
color: red;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
|
||||||
.info {
|
|
||||||
color: blue;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
padding: 10px 20px;
|
|
||||||
margin: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
#output {
|
|
||||||
background: #000;
|
|
||||||
color: #0f0;
|
|
||||||
padding: 10px;
|
|
||||||
height: 300px;
|
|
||||||
overflow-y: auto;
|
|
||||||
margin-top: 20px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>WebSocket Message Handler Test Suite</h1>
|
|
||||||
|
|
||||||
<div class="test-section">
|
|
||||||
<h2>Test Cases</h2>
|
|
||||||
<button onclick="runAllTests()">Run All Tests</button>
|
|
||||||
<button onclick="clearOutput()">Clear Output</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="test-section">
|
|
||||||
<h2>Individual Tests</h2>
|
|
||||||
<button onclick="testEmptyMessage()">Test Empty Message</button>
|
|
||||||
<button onclick="testValidEventMessage()">Test Valid Event Message</button>
|
|
||||||
<button onclick="testRegularTextMessage()">Test Regular Text Message</button>
|
|
||||||
<button onclick="testMalformedJSON()">Test Malformed JSON</button>
|
|
||||||
<button onclick="testIncompleteJSON()">Test Incomplete JSON</button>
|
|
||||||
<button onclick="testContextMessage()">Test Context Message</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="output"></div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Simulate the message handler from the actual implementation
|
|
||||||
function handleEvent(eventType, eventData) {
|
|
||||||
log(`Event handled: ${eventType}`, 'info');
|
|
||||||
log(`Event data: ${JSON.stringify(eventData)}`, 'info');
|
|
||||||
}
|
|
||||||
|
|
||||||
function processMessageContent(message) {
|
|
||||||
log(`Processing message content: ${message.content}`, 'info');
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleWebSocketMessage(rawData) {
|
|
||||||
try {
|
|
||||||
if (!rawData || rawData.trim() === "") {
|
|
||||||
log("Empty WebSocket message received", 'error');
|
|
||||||
return { success: false, reason: "Empty message" };
|
|
||||||
}
|
|
||||||
|
|
||||||
const r = JSON.parse(rawData);
|
|
||||||
|
|
||||||
if (r.type === "connected") {
|
|
||||||
log("WebSocket welcome message", 'info');
|
|
||||||
return { success: true, type: "connected" };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r.message_type === 2) {
|
|
||||||
// Check if content looks like JSON (starts with { or [)
|
|
||||||
const contentTrimmed = r.content.trim();
|
|
||||||
if (contentTrimmed.startsWith("{") || contentTrimmed.startsWith("[")) {
|
|
||||||
try {
|
|
||||||
const d = JSON.parse(r.content);
|
|
||||||
if (d.event && d.data) {
|
|
||||||
// This is an event message
|
|
||||||
handleEvent(d.event, d.data);
|
|
||||||
return { success: true, type: "event", event: d.event };
|
|
||||||
}
|
|
||||||
} catch (parseErr) {
|
|
||||||
// Not a valid event message, treat as regular content
|
|
||||||
log("Content is not an event message, processing as regular message", 'info');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Process as regular message content
|
|
||||||
processMessageContent(r);
|
|
||||||
return { success: true, type: "regular_message" };
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r.message_type === 5) {
|
|
||||||
log("Context change message", 'info');
|
|
||||||
return { success: true, type: "context_change" };
|
|
||||||
}
|
|
||||||
|
|
||||||
processMessageContent(r);
|
|
||||||
return { success: true, type: "default_message" };
|
|
||||||
|
|
||||||
} catch (err) {
|
|
||||||
log(`WebSocket message parse error: ${err.message}`, 'error');
|
|
||||||
return { success: false, reason: err.message };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test cases
|
|
||||||
function testEmptyMessage() {
|
|
||||||
log("\n=== Testing Empty Message ===", 'info');
|
|
||||||
const result = handleWebSocketMessage("");
|
|
||||||
if (!result.success && result.reason === "Empty message") {
|
|
||||||
log("✓ Empty message handled correctly", 'success');
|
|
||||||
} else {
|
|
||||||
log("✗ Empty message not handled correctly", 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testValidEventMessage() {
|
|
||||||
log("\n=== Testing Valid Event Message ===", 'info');
|
|
||||||
const message = JSON.stringify({
|
|
||||||
message_type: 2,
|
|
||||||
content: JSON.stringify({
|
|
||||||
event: "thinking_start",
|
|
||||||
data: { message: "Processing..." }
|
|
||||||
})
|
|
||||||
});
|
|
||||||
const result = handleWebSocketMessage(message);
|
|
||||||
if (result.success && result.type === "event") {
|
|
||||||
log("✓ Valid event message handled correctly", 'success');
|
|
||||||
} else {
|
|
||||||
log("✗ Valid event message not handled correctly", 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testRegularTextMessage() {
|
|
||||||
log("\n=== Testing Regular Text Message (type 2) ===", 'info');
|
|
||||||
const message = JSON.stringify({
|
|
||||||
message_type: 2,
|
|
||||||
content: "This is a regular text message, not JSON"
|
|
||||||
});
|
|
||||||
const result = handleWebSocketMessage(message);
|
|
||||||
if (result.success && result.type === "regular_message") {
|
|
||||||
log("✓ Regular text message handled correctly", 'success');
|
|
||||||
} else {
|
|
||||||
log("✗ Regular text message not handled correctly", 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testMalformedJSON() {
|
|
||||||
log("\n=== Testing Malformed JSON in Content ===", 'info');
|
|
||||||
const message = JSON.stringify({
|
|
||||||
message_type: 2,
|
|
||||||
content: "{invalid json: true"
|
|
||||||
});
|
|
||||||
const result = handleWebSocketMessage(message);
|
|
||||||
if (result.success && result.type === "regular_message") {
|
|
||||||
log("✓ Malformed JSON handled gracefully", 'success');
|
|
||||||
} else {
|
|
||||||
log("✗ Malformed JSON not handled correctly", 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testIncompleteJSON() {
|
|
||||||
log("\n=== Testing Incomplete JSON ===", 'info');
|
|
||||||
const message = JSON.stringify({
|
|
||||||
message_type: 2,
|
|
||||||
content: '{"event": "test", "data":' // Incomplete JSON
|
|
||||||
});
|
|
||||||
const result = handleWebSocketMessage(message);
|
|
||||||
if (result.success && result.type === "regular_message") {
|
|
||||||
log("✓ Incomplete JSON handled gracefully", 'success');
|
|
||||||
} else {
|
|
||||||
log("✗ Incomplete JSON not handled correctly", 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function testContextMessage() {
|
|
||||||
log("\n=== Testing Context Message (type 5) ===", 'info');
|
|
||||||
const message = JSON.stringify({
|
|
||||||
message_type: 5,
|
|
||||||
context_name: "test_context",
|
|
||||||
content: "Context content"
|
|
||||||
});
|
|
||||||
const result = handleWebSocketMessage(message);
|
|
||||||
if (result.success && result.type === "context_change") {
|
|
||||||
log("✓ Context message handled correctly", 'success');
|
|
||||||
} else {
|
|
||||||
log("✗ Context message not handled correctly", 'error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function runAllTests() {
|
|
||||||
log("Starting all tests...\n", 'info');
|
|
||||||
testEmptyMessage();
|
|
||||||
testValidEventMessage();
|
|
||||||
testRegularTextMessage();
|
|
||||||
testMalformedJSON();
|
|
||||||
testIncompleteJSON();
|
|
||||||
testContextMessage();
|
|
||||||
log("\n=== All tests completed ===", 'info');
|
|
||||||
}
|
|
||||||
|
|
||||||
function log(message, type = 'info') {
|
|
||||||
const output = document.getElementById('output');
|
|
||||||
const timestamp = new Date().toISOString().split('T')[1].split('.')[0];
|
|
||||||
const className = type === 'error' ? 'error' : type === 'success' ? 'success' : 'info';
|
|
||||||
output.innerHTML += `<span class="${className}">[${timestamp}] ${message}</span>\n`;
|
|
||||||
output.scrollTop = output.scrollHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearOutput() {
|
|
||||||
document.getElementById('output').innerHTML = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run tests on page load
|
|
||||||
window.onload = function() {
|
|
||||||
log("WebSocket Message Handler Test Suite Ready", 'info');
|
|
||||||
log("Click 'Run All Tests' to begin\n", 'info');
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 9 KiB After Width: | Height: | Size: 9 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 2 KiB After Width: | Height: | Size: 2 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 7.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |