Initial commit: OpenClaw CalDAV Installer Wizard
This commit is contained in:
commit
7e95b2f52e
102
README.md
Normal file
102
README.md
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
# 🦞 OpenClaw CalDAV Installer
|
||||||
|
|
||||||
|
One-command deployment of **Radicale CalDAV/CardDAV** on Ubuntu/Debian, pre-configured for [OpenClaw](https://openclaw.dev) integration.
|
||||||
|
|
||||||
|
## What It Does
|
||||||
|
|
||||||
|
- Installs **Docker**, **Nginx**, **Certbot**, and **apache2-utils** (if not present)
|
||||||
|
- Deploys **Radicale** via Docker Compose with bcrypt authentication
|
||||||
|
- Configures **Nginx reverse proxy** with SSL termination
|
||||||
|
- Obtains a free **Let's Encrypt SSL certificate**
|
||||||
|
- Binds to `127.0.0.1` for secure OpenClaw localhost access
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
| Requirement | Details |
|
||||||
|
|---|---|
|
||||||
|
| **OS** | Ubuntu 20.04+ / Debian 11+ |
|
||||||
|
| **Access** | Root (sudo) |
|
||||||
|
| **Ports** | 80 and 443 open to the internet |
|
||||||
|
| **DNS** | A record pointing your domain to this server's IP |
|
||||||
|
| **RAM** | 512 MB minimum |
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Download the installer
|
||||||
|
git clone https://github.com/openclaw/caldav-installer.git
|
||||||
|
cd caldav-installer
|
||||||
|
|
||||||
|
# Run the wizard
|
||||||
|
sudo bash install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The wizard will ask you for:
|
||||||
|
1. **Domain name** (e.g. `cal.example.com`)
|
||||||
|
2. **Email** (for Let's Encrypt SSL)
|
||||||
|
3. **CalDAV username & password**
|
||||||
|
4. **Port** (default: 5232)
|
||||||
|
5. **Install directory** (default: `~/caldav-docker`)
|
||||||
|
|
||||||
|
## Client Setup
|
||||||
|
|
||||||
|
### Apple Calendar (macOS / iOS)
|
||||||
|
1. Settings → Accounts → Add Account → Other → CalDAV
|
||||||
|
2. Server: `https://YOUR_DOMAIN`
|
||||||
|
3. Username & password as configured
|
||||||
|
|
||||||
|
### Thunderbird / GNOME Calendar
|
||||||
|
- URL: `https://YOUR_DOMAIN/USERNAME/calendar.ics/`
|
||||||
|
|
||||||
|
## OpenClaw Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw config set tools.calendar.provider "caldav"
|
||||||
|
openclaw config set tools.calendar.caldav.url "https://YOUR_DOMAIN"
|
||||||
|
openclaw config set tools.calendar.caldav.username "YOUR_USER"
|
||||||
|
openclaw config set tools.calendar.caldav.password "YOUR_PASSWORD"
|
||||||
|
```
|
||||||
|
|
||||||
|
OpenClaw connects internally via `http://localhost:5232`.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
Internet → Nginx (443/SSL) → 127.0.0.1:5232 → Radicale Container
|
||||||
|
↕
|
||||||
|
OpenClaw Gateway → localhost:5232 ──────────> /data/collections
|
||||||
|
```
|
||||||
|
|
||||||
|
## Management
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/caldav-docker
|
||||||
|
|
||||||
|
# Start / Stop / Restart
|
||||||
|
docker compose up -d
|
||||||
|
docker compose down
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
# Add a new user
|
||||||
|
sudo htpasswd -B ./config/users newuser
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
docker compose logs -f
|
||||||
|
|
||||||
|
# Check status
|
||||||
|
docker compose ps
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Issue | Fix |
|
||||||
|
|---|---|
|
||||||
|
| SSL cert failed | Verify DNS A record; check ports 80/443 are open |
|
||||||
|
| 401 Unauthorized | Check `config/users` file has the correct user entry |
|
||||||
|
| Container won't start | Check logs: `docker compose logs radicale` |
|
||||||
|
| Can't sync calendar | Ensure client URL ends with `/username/calendar.ics/` |
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT — use freely, contribute back. 🦞
|
||||||
434
install.sh
Executable file
434
install.sh
Executable file
@ -0,0 +1,434 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# ============================================================================
|
||||||
|
# OpenClaw CalDAV (Radicale) Installer
|
||||||
|
# Deploys Radicale via Docker with Nginx + Let's Encrypt SSL
|
||||||
|
# Compatible with OpenClaw gateway via localhost
|
||||||
|
#
|
||||||
|
# Usage: sudo bash install.sh
|
||||||
|
# GitHub: https://github.com/openclaw/caldav-installer
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# ── Colors ───────────────────────────────────────────────────────────────────
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
CYAN='\033[0;36m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
# ── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
|
||||||
|
success() { echo -e "${GREEN}[ OK]${NC} $*"; }
|
||||||
|
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
|
||||||
|
fail() { echo -e "${RED}[FAIL]${NC} $*"; exit 1; }
|
||||||
|
|
||||||
|
banner() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${CYAN}"
|
||||||
|
echo " ┌──────────────────────────────────────────────┐"
|
||||||
|
echo " │ 🦞 OpenClaw CalDAV Installer 🦞 │"
|
||||||
|
echo " │ Radicale + Docker + Nginx + SSL │"
|
||||||
|
echo " └──────────────────────────────────────────────┘"
|
||||||
|
echo -e "${NC}"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Pre-flight checks ───────────────────────────────────────────────────────
|
||||||
|
preflight() {
|
||||||
|
if [[ $EUID -ne 0 ]]; then
|
||||||
|
fail "This script must be run as root (sudo bash install.sh)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! grep -qiE 'ubuntu|debian' /etc/os-release 2>/dev/null; then
|
||||||
|
fail "This installer supports Ubuntu/Debian only."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ss -tlnp | grep -qE ':80\b' 2>/dev/null; then
|
||||||
|
warn "Port 80 is already in use. This is OK if Nginx is already running."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ss -tlnp | grep -qE ':443\b' 2>/dev/null; then
|
||||||
|
warn "Port 443 is already in use. This is OK if Nginx is already running."
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "Pre-flight checks passed."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Interactive prompts ──────────────────────────────────────────────────────
|
||||||
|
gather_input() {
|
||||||
|
echo -e "${BOLD}Step 1: Configuration${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
# Domain
|
||||||
|
while true; do
|
||||||
|
read -rp " Enter your CalDAV domain (e.g. cal.example.com): " CAL_DOMAIN
|
||||||
|
if [[ -z "$CAL_DOMAIN" ]]; then
|
||||||
|
warn "Domain cannot be empty."
|
||||||
|
elif [[ "$CAL_DOMAIN" == *" "* ]]; then
|
||||||
|
warn "Domain cannot contain spaces."
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Email for SSL
|
||||||
|
while true; do
|
||||||
|
read -rp " Enter your email (for Let's Encrypt SSL certificate): " CAL_EMAIL
|
||||||
|
if [[ -z "$CAL_EMAIL" ]]; then
|
||||||
|
warn "Email cannot be empty."
|
||||||
|
elif [[ "$CAL_EMAIL" != *"@"* ]]; then
|
||||||
|
warn "Please enter a valid email address."
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# CalDAV username
|
||||||
|
while true; do
|
||||||
|
read -rp " CalDAV username (e.g. openclaw): " CAL_USER
|
||||||
|
if [[ -z "$CAL_USER" ]]; then
|
||||||
|
warn "Username cannot be empty."
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# CalDAV password
|
||||||
|
while true; do
|
||||||
|
read -rsp " CalDAV password: " CAL_PASS
|
||||||
|
echo ""
|
||||||
|
if [[ -z "$CAL_PASS" ]]; then
|
||||||
|
warn "Password cannot be empty."
|
||||||
|
else
|
||||||
|
read -rsp " Confirm password: " CAL_PASS_CONFIRM
|
||||||
|
echo ""
|
||||||
|
if [[ "$CAL_PASS" != "$CAL_PASS_CONFIRM" ]]; then
|
||||||
|
warn "Passwords do not match."
|
||||||
|
else
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Internal port
|
||||||
|
read -rp " Internal proxy port [5232]: " CAL_PORT
|
||||||
|
CAL_PORT=${CAL_PORT:-5232}
|
||||||
|
|
||||||
|
# Install directory
|
||||||
|
read -rp " Installation directory [~/caldav-docker]: " INSTALL_DIR
|
||||||
|
INSTALL_DIR=${INSTALL_DIR:-"$HOME/caldav-docker"}
|
||||||
|
# Expand tilde
|
||||||
|
INSTALL_DIR="${INSTALL_DIR/#\~/$HOME}"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Summary:${NC}"
|
||||||
|
echo " Domain: https://${CAL_DOMAIN}"
|
||||||
|
echo " Email: ${CAL_EMAIL}"
|
||||||
|
echo " Username: ${CAL_USER}"
|
||||||
|
echo " Port: 127.0.0.1:${CAL_PORT} → :5232"
|
||||||
|
echo " Directory: ${INSTALL_DIR}"
|
||||||
|
echo ""
|
||||||
|
read -rp " Proceed? (Y/n): " confirm
|
||||||
|
[[ "$confirm" =~ ^[Nn]$ ]] && { info "Aborted."; exit 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Install dependencies ────────────────────────────────────────────────────
|
||||||
|
install_deps() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Step 2: Installing dependencies${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
info "Updating package index..."
|
||||||
|
apt-get update -qq
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
if command -v docker &>/dev/null; then
|
||||||
|
success "Docker already installed: $(docker --version)"
|
||||||
|
else
|
||||||
|
info "Installing Docker..."
|
||||||
|
apt-get install -y -qq ca-certificates curl gnupg lsb-release
|
||||||
|
install -m 0755 -d /etc/apt/keyrings
|
||||||
|
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg 2>/dev/null || true
|
||||||
|
chmod a+r /etc/apt/keyrings/docker.gpg
|
||||||
|
echo \
|
||||||
|
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
|
||||||
|
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
|
||||||
|
tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||||
|
apt-get update -qq
|
||||||
|
apt-get install -y -qq docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
|
||||||
|
success "Docker installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Docker Compose
|
||||||
|
if docker compose version &>/dev/null; then
|
||||||
|
COMPOSE_CMD="docker compose"
|
||||||
|
success "Docker Compose (v2 plugin) available."
|
||||||
|
elif command -v docker-compose &>/dev/null; then
|
||||||
|
COMPOSE_CMD="docker-compose"
|
||||||
|
success "Docker Compose (v1 standalone) available."
|
||||||
|
else
|
||||||
|
info "Installing Docker Compose plugin..."
|
||||||
|
apt-get install -y -qq docker-compose-plugin
|
||||||
|
COMPOSE_CMD="docker compose"
|
||||||
|
success "Docker Compose installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Nginx
|
||||||
|
if command -v nginx &>/dev/null; then
|
||||||
|
success "Nginx already installed."
|
||||||
|
else
|
||||||
|
info "Installing Nginx..."
|
||||||
|
apt-get install -y -qq nginx
|
||||||
|
systemctl enable nginx
|
||||||
|
systemctl start nginx
|
||||||
|
success "Nginx installed and started."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Certbot
|
||||||
|
if command -v certbot &>/dev/null; then
|
||||||
|
success "Certbot already installed."
|
||||||
|
else
|
||||||
|
info "Installing Certbot..."
|
||||||
|
apt-get install -y -qq certbot python3-certbot-nginx
|
||||||
|
success "Certbot installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# apache2-utils (for htpasswd / bcrypt)
|
||||||
|
if command -v htpasswd &>/dev/null; then
|
||||||
|
success "htpasswd (apache2-utils) already installed."
|
||||||
|
else
|
||||||
|
info "Installing apache2-utils for bcrypt password hashing..."
|
||||||
|
apt-get install -y -qq apache2-utils
|
||||||
|
success "apache2-utils installed."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# UFW (optional)
|
||||||
|
if command -v ufw &>/dev/null; then
|
||||||
|
info "Configuring firewall (UFW)..."
|
||||||
|
ufw allow 'Nginx Full' >/dev/null 2>&1 || true
|
||||||
|
ufw allow OpenSSH >/dev/null 2>&1 || true
|
||||||
|
success "Firewall rules updated."
|
||||||
|
fi
|
||||||
|
|
||||||
|
success "All dependencies ready."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Create Docker stack ──────────────────────────────────────────────────────
|
||||||
|
create_docker_stack() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Step 3: Creating Docker stack${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
mkdir -p "${INSTALL_DIR}/config"
|
||||||
|
mkdir -p "${INSTALL_DIR}/data"
|
||||||
|
|
||||||
|
# Docker Compose
|
||||||
|
cat > "${INSTALL_DIR}/docker-compose.yml" <<YAML
|
||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
radicale:
|
||||||
|
image: tomsquest/docker-radicale:latest
|
||||||
|
container_name: radicale
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:${CAL_PORT}:5232"
|
||||||
|
volumes:
|
||||||
|
- ./data:/data
|
||||||
|
- ./config:/config
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
YAML
|
||||||
|
|
||||||
|
# Radicale configuration
|
||||||
|
cat > "${INSTALL_DIR}/config/config" <<CONF
|
||||||
|
[server]
|
||||||
|
hosts = 0.0.0.0:5232
|
||||||
|
|
||||||
|
[auth]
|
||||||
|
type = htpasswd
|
||||||
|
htpasswd_filename = /config/users
|
||||||
|
htpasswd_encryption = bcrypt
|
||||||
|
|
||||||
|
[storage]
|
||||||
|
filesystem_folder = /data/collections
|
||||||
|
CONF
|
||||||
|
|
||||||
|
# Create htpasswd file with bcrypt
|
||||||
|
info "Generating bcrypt password hash..."
|
||||||
|
htpasswd -cbB "${INSTALL_DIR}/config/users" "${CAL_USER}" "${CAL_PASS}"
|
||||||
|
|
||||||
|
# Save credentials for reference
|
||||||
|
cat > "${INSTALL_DIR}/.env" <<ENV
|
||||||
|
# Auto-generated credentials — keep this file secure!
|
||||||
|
CAL_DOMAIN=${CAL_DOMAIN}
|
||||||
|
CAL_PORT=${CAL_PORT}
|
||||||
|
CAL_USER=${CAL_USER}
|
||||||
|
ENV
|
||||||
|
chmod 600 "${INSTALL_DIR}/.env"
|
||||||
|
|
||||||
|
success "Docker stack created at ${INSTALL_DIR}/"
|
||||||
|
success "Radicale config created at ${INSTALL_DIR}/config/config"
|
||||||
|
success "User credentials hashed at ${INSTALL_DIR}/config/users"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Configure Nginx ──────────────────────────────────────────────────────────
|
||||||
|
configure_nginx() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Step 4: Configuring Nginx reverse proxy${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
NGINX_CONF="/etc/nginx/sites-available/caldav-${CAL_DOMAIN}"
|
||||||
|
|
||||||
|
cat > "${NGINX_CONF}" <<NGINX
|
||||||
|
server {
|
||||||
|
server_name ${CAL_DOMAIN};
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://127.0.0.1:${CAL_PORT};
|
||||||
|
proxy_set_header Host \$http_host;
|
||||||
|
proxy_set_header X-Real-IP \$remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
listen 80;
|
||||||
|
}
|
||||||
|
NGINX
|
||||||
|
|
||||||
|
# Enable site
|
||||||
|
ln -sf "${NGINX_CONF}" /etc/nginx/sites-enabled/
|
||||||
|
|
||||||
|
nginx -t || fail "Nginx configuration test failed!"
|
||||||
|
systemctl reload nginx
|
||||||
|
|
||||||
|
success "Nginx proxy configured for ${CAL_DOMAIN}"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Obtain SSL certificate ───────────────────────────────────────────────────
|
||||||
|
obtain_ssl() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Step 5: Obtaining SSL certificate${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
info "Requesting Let's Encrypt certificate for ${CAL_DOMAIN}..."
|
||||||
|
info "Make sure your DNS A record points to this server's IP!"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
certbot --nginx \
|
||||||
|
-d "${CAL_DOMAIN}" \
|
||||||
|
--non-interactive \
|
||||||
|
--agree-tos \
|
||||||
|
--email "${CAL_EMAIL}" \
|
||||||
|
--redirect \
|
||||||
|
|| {
|
||||||
|
warn "Certbot failed. This usually means:"
|
||||||
|
warn " 1. DNS is not yet pointing to this server"
|
||||||
|
warn " 2. Ports 80/443 are blocked by a firewall"
|
||||||
|
warn ""
|
||||||
|
warn "You can retry later with:"
|
||||||
|
warn " sudo certbot --nginx -d ${CAL_DOMAIN}"
|
||||||
|
warn ""
|
||||||
|
warn "Continuing without SSL..."
|
||||||
|
}
|
||||||
|
|
||||||
|
success "SSL certificate obtained (or skipped)."
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Start the stack ──────────────────────────────────────────────────────────
|
||||||
|
start_stack() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}Step 6: Starting Radicale CalDAV${NC}"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
info "Pulling Docker images..."
|
||||||
|
cd "${INSTALL_DIR}"
|
||||||
|
$COMPOSE_CMD pull
|
||||||
|
|
||||||
|
info "Starting containers..."
|
||||||
|
$COMPOSE_CMD up -d
|
||||||
|
|
||||||
|
# Wait for Radicale to boot
|
||||||
|
info "Waiting for Radicale to start (up to 30s)..."
|
||||||
|
for i in $(seq 1 6); do
|
||||||
|
if curl -sI "http://127.0.0.1:${CAL_PORT}" >/dev/null 2>&1; then
|
||||||
|
success "Radicale is responding on port ${CAL_PORT}!"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
# Show container status
|
||||||
|
echo ""
|
||||||
|
$COMPOSE_CMD ps
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Print summary ───────────────────────────────────────────────────────────
|
||||||
|
print_summary() {
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${GREEN}"
|
||||||
|
echo " ┌──────────────────────────────────────────────┐"
|
||||||
|
echo " │ ✅ Installation Complete! ✅ │"
|
||||||
|
echo " └──────────────────────────────────────────────┘"
|
||||||
|
echo -e "${NC}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Access your CalDAV server:${NC}"
|
||||||
|
echo " 🌐 https://${CAL_DOMAIN}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Internal (localhost) access:${NC}"
|
||||||
|
echo " 📡 http://127.0.0.1:${CAL_PORT}"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Credentials:${NC}"
|
||||||
|
echo " 👤 Username: ${CAL_USER}"
|
||||||
|
echo " 📄 Env file: ${INSTALL_DIR}/.env"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Management commands:${NC}"
|
||||||
|
echo " Start: cd ${INSTALL_DIR} && ${COMPOSE_CMD} up -d"
|
||||||
|
echo " Stop: cd ${INSTALL_DIR} && ${COMPOSE_CMD} down"
|
||||||
|
echo " Logs: cd ${INSTALL_DIR} && ${COMPOSE_CMD} logs -f"
|
||||||
|
echo " Status: cd ${INSTALL_DIR} && ${COMPOSE_CMD} ps"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD} Add a new CalDAV user:${NC}"
|
||||||
|
echo " sudo htpasswd -B ${INSTALL_DIR}/config/users <username>"
|
||||||
|
echo " cd ${INSTALL_DIR} && ${COMPOSE_CMD} restart"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${CYAN} ── Client Setup ──${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Apple Calendar (macOS / iOS):"
|
||||||
|
echo " Settings → Accounts → Add Account → Other → CalDAV"
|
||||||
|
echo " Server: https://${CAL_DOMAIN}"
|
||||||
|
echo " Username: ${CAL_USER}"
|
||||||
|
echo " Password: (the password you entered)"
|
||||||
|
echo ""
|
||||||
|
echo " Thunderbird / GNOME Calendar:"
|
||||||
|
echo " Server URL: https://${CAL_DOMAIN}/${CAL_USER}/calendar.ics/"
|
||||||
|
echo ""
|
||||||
|
echo -e "${BOLD}${CYAN} ── OpenClaw Integration ──${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " Configure OpenClaw to sync calendars:"
|
||||||
|
echo -e " ${YELLOW}openclaw config set tools.calendar.provider \"caldav\"${NC}"
|
||||||
|
echo -e " ${YELLOW}openclaw config set tools.calendar.caldav.url \"https://${CAL_DOMAIN}\"${NC}"
|
||||||
|
echo -e " ${YELLOW}openclaw config set tools.calendar.caldav.username \"${CAL_USER}\"${NC}"
|
||||||
|
echo -e " ${YELLOW}openclaw config set tools.calendar.caldav.password \"<YOUR_PASSWORD>\"${NC}"
|
||||||
|
echo ""
|
||||||
|
echo " OpenClaw will connect internally via localhost:${CAL_PORT} 🦞"
|
||||||
|
echo ""
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── Main ─────────────────────────────────────────────────────────────────────
|
||||||
|
main() {
|
||||||
|
banner
|
||||||
|
preflight
|
||||||
|
gather_input
|
||||||
|
install_deps
|
||||||
|
create_docker_stack
|
||||||
|
configure_nginx
|
||||||
|
obtain_ssl
|
||||||
|
start_stack
|
||||||
|
print_summary
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Loading…
Reference in New Issue
Block a user