this repo has no description
1#!/bin/bash 2set -euo pipefail 3RED='\033[0;31m' 4GREEN='\033[0;32m' 5YELLOW='\033[1;33m' 6BLUE='\033[0;34m' 7CYAN='\033[0;36m' 8NC='\033[0m' 9log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } 10log_success() { echo -e "${GREEN}[OK]${NC} $1"; } 11log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; } 12log_error() { echo -e "${RED}[ERROR]${NC} $1"; } 13if [[ $EUID -ne 0 ]]; then 14 log_error "This script must be run as root" 15 exit 1 16fi 17if ! grep -qi "debian" /etc/os-release 2>/dev/null; then 18 log_warn "This script is designed for Debian. Proceed with caution on other distros." 19fi 20nuke_installation() { 21 echo -e "${RED}" 22 echo "╔═══════════════════════════════════════════════════════════════════╗" 23 echo "║ NUKING EXISTING INSTALLATION ║" 24 echo "╚═══════════════════════════════════════════════════════════════════╝" 25 echo -e "${NC}" 26 log_info "Stopping services..." 27 systemctl stop bspds 2>/dev/null || true 28 systemctl disable bspds 2>/dev/null || true 29 log_info "Removing BSPDS files..." 30 rm -rf /opt/bspds 31 rm -rf /var/lib/bspds 32 rm -f /usr/local/bin/bspds 33 rm -f /usr/local/bin/bspds-sendmail 34 rm -f /usr/local/bin/bspds-mailq 35 rm -rf /var/spool/bspds-mail 36 rm -f /etc/systemd/system/bspds.service 37 systemctl daemon-reload 38 log_info "Removing BSPDS configuration..." 39 rm -rf /etc/bspds 40 log_info "Dropping postgres database and user..." 41 sudo -u postgres psql -c "DROP DATABASE IF EXISTS pds;" 2>/dev/null || true 42 sudo -u postgres psql -c "DROP USER IF EXISTS bspds;" 2>/dev/null || true 43 log_info "Removing minio bucket and resetting minio..." 44 if command -v mc &>/dev/null; then 45 mc rb local/pds-blobs --force 2>/dev/null || true 46 mc alias remove local 2>/dev/null || true 47 fi 48 systemctl stop minio 2>/dev/null || true 49 rm -rf /var/lib/minio/data/.minio.sys 2>/dev/null || true 50 rm -f /etc/default/minio 2>/dev/null || true 51 log_info "Removing nginx config..." 52 rm -f /etc/nginx/sites-enabled/bspds 53 rm -f /etc/nginx/sites-available/bspds 54 systemctl reload nginx 2>/dev/null || true 55 log_success "Previous installation nuked!" 56 echo "" 57} 58if [[ -f /etc/bspds/bspds.env ]] || [[ -d /opt/bspds ]] || [[ -f /usr/local/bin/bspds ]]; then 59 echo -e "${YELLOW}" 60 echo "╔═══════════════════════════════════════════════════════════════════╗" 61 echo "║ EXISTING INSTALLATION DETECTED ║" 62 echo "╚═══════════════════════════════════════════════════════════════════╝" 63 echo -e "${NC}" 64 echo "" 65 echo "Options:" 66 echo " 1) Nuke everything and start fresh (destroys database!)" 67 echo " 2) Continue with existing installation (idempotent update)" 68 echo " 3) Exit" 69 echo "" 70 read -p "Choose an option [1/2/3]: " INSTALL_CHOICE 71 case "$INSTALL_CHOICE" in 72 1) 73 echo "" 74 echo -e "${RED}WARNING: This will DELETE:${NC}" 75 echo " - PostgreSQL database 'pds' and all data" 76 echo " - All BSPDS configuration and credentials" 77 echo " - All source code in /opt/bspds" 78 echo " - MinIO bucket 'pds-blobs' and all blobs" 79 echo " - Mail queue contents" 80 echo "" 81 read -p "Type 'NUKE' to confirm destruction: " CONFIRM_NUKE 82 if [[ "$CONFIRM_NUKE" == "NUKE" ]]; then 83 nuke_installation 84 else 85 log_error "Nuke cancelled. Exiting." 86 exit 1 87 fi 88 ;; 89 2) 90 log_info "Continuing with existing installation..." 91 ;; 92 3) 93 log_info "Exiting." 94 exit 0 95 ;; 96 *) 97 log_error "Invalid option. Exiting." 98 exit 1 99 ;; 100 esac 101fi 102echo -e "${CYAN}" 103echo "╔═══════════════════════════════════════════════════════════════════╗" 104echo "║ BSPDS Installation Script for Debian ║" 105echo "║ AT Protocol Personal Data Server in Rust ║" 106echo "╚═══════════════════════════════════════════════════════════════════╝" 107echo -e "${NC}" 108get_public_ips() { 109 IPV4=$(curl -4 -s --max-time 5 ifconfig.me 2>/dev/null || curl -4 -s --max-time 5 icanhazip.com 2>/dev/null || echo "Could not detect") 110 IPV6=$(curl -6 -s --max-time 5 ifconfig.me 2>/dev/null || curl -6 -s --max-time 5 icanhazip.com 2>/dev/null || echo "Not available") 111} 112log_info "Detecting public IP addresses..." 113get_public_ips 114echo "" 115echo -e "${CYAN}Your server's public IPs:${NC}" 116echo -e " IPv4: ${GREEN}${IPV4}${NC}" 117echo -e " IPv6: ${GREEN}${IPV6}${NC}" 118echo "" 119read -p "Enter your PDS domain (e.g., pds.example.com): " PDS_DOMAIN 120if [[ -z "$PDS_DOMAIN" ]]; then 121 log_error "Domain cannot be empty" 122 exit 1 123fi 124read -p "Enter your email for Let's Encrypt notifications: " CERTBOT_EMAIL 125if [[ -z "$CERTBOT_EMAIL" ]]; then 126 log_error "Email cannot be empty" 127 exit 1 128fi 129echo "" 130echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 131echo -e "${YELLOW}DNS RECORDS REQUIRED${NC}" 132echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 133echo "" 134echo "Before continuing, create these DNS records at your registrar:" 135echo "" 136echo -e "${GREEN}A Record:${NC}" 137echo " Name: ${PDS_DOMAIN}" 138echo " Type: A" 139echo " Value: ${IPV4}" 140echo "" 141if [[ "$IPV6" != "Not available" ]]; then 142echo -e "${GREEN}AAAA Record:${NC}" 143echo " Name: ${PDS_DOMAIN}" 144echo " Type: AAAA" 145echo " Value: ${IPV6}" 146echo "" 147fi 148echo -e "${GREEN}Wildcard A Record (for user handles):${NC}" 149echo " Name: *.${PDS_DOMAIN}" 150echo " Type: A" 151echo " Value: ${IPV4}" 152echo "" 153if [[ "$IPV6" != "Not available" ]]; then 154echo -e "${GREEN}Wildcard AAAA Record (for user handles):${NC}" 155echo " Name: *.${PDS_DOMAIN}" 156echo " Type: AAAA" 157echo " Value: ${IPV6}" 158echo "" 159fi 160echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 161echo "" 162read -p "Have you created these DNS records? (y/N): " DNS_CONFIRMED 163if [[ ! "$DNS_CONFIRMED" =~ ^[Yy]$ ]]; then 164 log_warn "Please create the DNS records and run this script again." 165 exit 0 166fi 167CREDENTIALS_FILE="/etc/bspds/.credentials" 168if [[ -f "$CREDENTIALS_FILE" ]]; then 169 log_info "Loading existing credentials from previous installation..." 170 source "$CREDENTIALS_FILE" 171 log_success "Credentials loaded" 172else 173 log_info "Generating secure secrets..." 174 JWT_SECRET=$(openssl rand -base64 48) 175 DPOP_SECRET=$(openssl rand -base64 48) 176 MASTER_KEY=$(openssl rand -base64 48) 177 DB_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32) 178 MINIO_PASSWORD=$(openssl rand -base64 24 | tr -dc 'a-zA-Z0-9' | head -c 32) 179 mkdir -p /etc/bspds 180 cat > "$CREDENTIALS_FILE" << EOF 181JWT_SECRET="$JWT_SECRET" 182DPOP_SECRET="$DPOP_SECRET" 183MASTER_KEY="$MASTER_KEY" 184DB_PASSWORD="$DB_PASSWORD" 185MINIO_PASSWORD="$MINIO_PASSWORD" 186EOF 187 chmod 600 "$CREDENTIALS_FILE" 188 log_success "Secrets generated and saved" 189fi 190log_info "Checking swap space..." 191TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}') 192TOTAL_SWAP_KB=$(grep SwapTotal /proc/meminfo | awk '{print $2}') 193if [[ $TOTAL_SWAP_KB -lt 2000000 ]]; then 194 log_info "Adding swap space (needed for compilation)..." 195 if [[ ! -f /swapfile ]]; then 196 SWAP_SIZE="4G" 197 if [[ $TOTAL_MEM_KB -lt 2000000 ]]; then 198 SWAP_SIZE="4G" 199 elif [[ $TOTAL_MEM_KB -lt 4000000 ]]; then 200 SWAP_SIZE="2G" 201 fi 202 fallocate -l $SWAP_SIZE /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=4096 203 chmod 600 /swapfile 204 mkswap /swapfile 205 swapon /swapfile 206 grep -q '/swapfile' /etc/fstab || echo '/swapfile none swap sw 0 0' >> /etc/fstab 207 log_success "Swap space added ($SWAP_SIZE)" 208 else 209 swapon /swapfile 2>/dev/null || true 210 log_success "Existing swap enabled" 211 fi 212else 213 log_success "Sufficient swap already configured" 214fi 215log_info "Updating system packages..." 216apt update && apt upgrade -y 217log_success "System updated" 218log_info "Installing build dependencies..." 219apt install -y curl git build-essential pkg-config libssl-dev ca-certificates gnupg lsb-release unzip xxd 220log_success "Build dependencies installed" 221log_info "Installing postgres..." 222apt install -y postgresql postgresql-contrib 223systemctl enable postgresql 224systemctl start postgresql 225sudo -u postgres psql -c "CREATE USER bspds WITH PASSWORD '${DB_PASSWORD}';" 2>/dev/null || \ 226 sudo -u postgres psql -c "ALTER USER bspds WITH PASSWORD '${DB_PASSWORD}';" 227sudo -u postgres psql -c "CREATE DATABASE pds OWNER bspds;" 2>/dev/null || true 228sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE pds TO bspds;" 229log_success "postgres installed and configured" 230log_info "Installing valkey..." 231apt install -y valkey || { 232 log_warn "valkey not in repos, trying redis..." 233 apt install -y redis-server 234 systemctl enable redis-server 235 systemctl start redis-server 236} 237systemctl enable valkey-server 2>/dev/null || true 238systemctl start valkey-server 2>/dev/null || true 239log_success "valkey/redis installed" 240log_info "Installing minio..." 241if [[ ! -f /usr/local/bin/minio ]]; then 242 ARCH=$(dpkg --print-architecture) 243 if [[ "$ARCH" == "amd64" ]]; then 244 curl -fsSL -o /tmp/minio https://dl.min.io/server/minio/release/linux-amd64/minio 245 elif [[ "$ARCH" == "arm64" ]]; then 246 curl -fsSL -o /tmp/minio https://dl.min.io/server/minio/release/linux-arm64/minio 247 else 248 log_error "Unsupported architecture: $ARCH" 249 exit 1 250 fi 251 chmod +x /tmp/minio 252 mv /tmp/minio /usr/local/bin/ 253fi 254mkdir -p /var/lib/minio/data 255id -u minio-user &>/dev/null || useradd -r -s /sbin/nologin minio-user 256chown -R minio-user:minio-user /var/lib/minio 257cat > /etc/default/minio << EOF 258MINIO_ROOT_USER=minioadmin 259MINIO_ROOT_PASSWORD=${MINIO_PASSWORD} 260MINIO_VOLUMES="/var/lib/minio/data" 261MINIO_OPTS="--console-address :9001" 262EOF 263chmod 600 /etc/default/minio 264cat > /etc/systemd/system/minio.service << 'EOF' 265[Unit] 266Description=MinIO Object Storage 267After=network.target 268[Service] 269User=minio-user 270Group=minio-user 271EnvironmentFile=/etc/default/minio 272ExecStart=/usr/local/bin/minio server $MINIO_VOLUMES $MINIO_OPTS 273Restart=always 274LimitNOFILE=65536 275[Install] 276WantedBy=multi-user.target 277EOF 278systemctl daemon-reload 279systemctl enable minio 280systemctl start minio 281log_success "minio installed" 282log_info "Waiting for minio to start..." 283sleep 5 284log_info "Installing minio client and creating bucket..." 285if [[ ! -f /usr/local/bin/mc ]]; then 286 ARCH=$(dpkg --print-architecture) 287 if [[ "$ARCH" == "amd64" ]]; then 288 curl -fsSL -o /tmp/mc https://dl.min.io/client/mc/release/linux-amd64/mc 289 elif [[ "$ARCH" == "arm64" ]]; then 290 curl -fsSL -o /tmp/mc https://dl.min.io/client/mc/release/linux-arm64/mc 291 fi 292 chmod +x /tmp/mc 293 mv /tmp/mc /usr/local/bin/ 294fi 295mc alias remove local 2>/dev/null || true 296mc alias set local http://localhost:9000 minioadmin "${MINIO_PASSWORD}" --api S3v4 297mc mb local/pds-blobs --ignore-existing 298log_success "minio bucket created" 299log_info "Installing rust..." 300if [[ -f "$HOME/.cargo/env" ]]; then 301 source "$HOME/.cargo/env" 302fi 303if ! command -v rustc &>/dev/null; then 304 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 305 source "$HOME/.cargo/env" 306fi 307log_success "rust installed" 308log_info "Installing deno..." 309export PATH="$HOME/.deno/bin:$PATH" 310if ! command -v deno &>/dev/null && [[ ! -f "$HOME/.deno/bin/deno" ]]; then 311 curl -fsSL https://deno.land/install.sh | sh 312 grep -q 'deno/bin' ~/.bashrc 2>/dev/null || echo 'export PATH="$HOME/.deno/bin:$PATH"' >> ~/.bashrc 313fi 314log_success "deno installed" 315log_info "Cloning BSPDS..." 316if [[ ! -d /opt/bspds ]]; then 317 git clone https://tangled.org/lewis.moe/bspds-sandbox /opt/bspds 318else 319 log_warn "/opt/bspds already exists, pulling latest..." 320 cd /opt/bspds && git pull 321fi 322cd /opt/bspds 323log_success "BSPDS cloned" 324log_info "Building frontend..." 325cd /opt/bspds/frontend 326"$HOME/.deno/bin/deno" task build 327cd /opt/bspds 328log_success "Frontend built" 329log_info "Building BSPDS (this may take a while)..." 330source "$HOME/.cargo/env" 331NPROC=$(nproc) 332if [[ $TOTAL_MEM_KB -lt 4000000 ]]; then 333 log_info "Low memory detected, limiting parallel jobs..." 334 CARGO_BUILD_JOBS=1 cargo build --release 335else 336 cargo build --release 337fi 338log_success "BSPDS built" 339log_info "Installing sqlx-cli and running migrations..." 340cargo install sqlx-cli --no-default-features --features postgres 341export DATABASE_URL="postgres://bspds:${DB_PASSWORD}@localhost:5432/pds" 342"$HOME/.cargo/bin/sqlx" migrate run 343log_success "Migrations complete" 344log_info "Setting up mail trap for testing..." 345mkdir -p /var/spool/bspds-mail 346chown root:root /var/spool/bspds-mail 347chmod 1777 /var/spool/bspds-mail 348cat > /usr/local/bin/bspds-sendmail << 'SENDMAIL_EOF' 349#!/bin/bash 350MAIL_DIR="/var/spool/bspds-mail" 351TIMESTAMP=$(date +%Y%m%d-%H%M%S) 352RANDOM_ID=$(head -c 4 /dev/urandom | xxd -p) 353MAIL_FILE="${MAIL_DIR}/${TIMESTAMP}-${RANDOM_ID}.eml" 354mkdir -p "$MAIL_DIR" 355{ 356 echo "X-BSPDS-Received: $(date -Iseconds)" 357 echo "X-BSPDS-Args: $*" 358 echo "" 359 cat 360} > "$MAIL_FILE" 361chmod 644 "$MAIL_FILE" 362echo "Mail saved to: $MAIL_FILE" >&2 363exit 0 364SENDMAIL_EOF 365chmod +x /usr/local/bin/bspds-sendmail 366cat > /usr/local/bin/bspds-mailq << 'MAILQ_EOF' 367#!/bin/bash 368MAIL_DIR="/var/spool/bspds-mail" 369RED='\033[0;31m' 370GREEN='\033[0;32m' 371YELLOW='\033[1;33m' 372BLUE='\033[0;34m' 373CYAN='\033[0;36m' 374NC='\033[0m' 375show_help() { 376 echo "bspds-mailq - View captured emails from BSPDS mail trap" 377 echo "" 378 echo "Usage:" 379 echo " bspds-mailq List all captured emails" 380 echo " bspds-mailq <number> View email by number (from list)" 381 echo " bspds-mailq <filename> View email by filename" 382 echo " bspds-mailq latest View the most recent email" 383 echo " bspds-mailq clear Delete all captured emails" 384 echo " bspds-mailq watch Watch for new emails (tail -f style)" 385 echo " bspds-mailq count Show count of emails in queue" 386 echo "" 387} 388list_emails() { 389 if [[ ! -d "$MAIL_DIR" ]] || [[ -z "$(ls -A "$MAIL_DIR" 2>/dev/null)" ]]; then 390 echo -e "${YELLOW}No emails in queue.${NC}" 391 return 392 fi 393 echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 394 echo -e "${GREEN} BSPDS Mail Queue${NC}" 395 echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 396 echo "" 397 local i=1 398 for f in $(ls -t "$MAIL_DIR"/*.eml 2>/dev/null); do 399 local filename=$(basename "$f") 400 local received=$(grep "^X-BSPDS-Received:" "$f" 2>/dev/null | cut -d' ' -f2-) 401 local to=$(grep -i "^To:" "$f" 2>/dev/null | head -1 | cut -d' ' -f2-) 402 local subject=$(grep -i "^Subject:" "$f" 2>/dev/null | head -1 | sed 's/^Subject: *//') 403 echo -e "${BLUE}[$i]${NC} ${filename}" 404 echo -e " To: ${GREEN}${to:-unknown}${NC}" 405 echo -e " Subject: ${YELLOW}${subject:-<no subject>}${NC}" 406 echo -e " Received: ${received:-unknown}" 407 echo "" 408 ((i++)) 409 done 410 echo -e "${CYAN}Total: $((i-1)) email(s)${NC}" 411} 412view_email() { 413 local target="$1" 414 local file="" 415 if [[ "$target" == "latest" ]]; then 416 file=$(ls -t "$MAIL_DIR"/*.eml 2>/dev/null | head -1) 417 elif [[ "$target" =~ ^[0-9]+$ ]]; then 418 file=$(ls -t "$MAIL_DIR"/*.eml 2>/dev/null | sed -n "${target}p") 419 elif [[ -f "$MAIL_DIR/$target" ]]; then 420 file="$MAIL_DIR/$target" 421 elif [[ -f "$target" ]]; then 422 file="$target" 423 fi 424 if [[ -z "$file" ]] || [[ ! -f "$file" ]]; then 425 echo -e "${RED}Email not found: $target${NC}" 426 return 1 427 fi 428 echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 429 echo -e "${GREEN} $(basename "$file")${NC}" 430 echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 431 cat "$file" 432 echo "" 433 echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 434} 435clear_queue() { 436 local count=$(ls -1 "$MAIL_DIR"/*.eml 2>/dev/null | wc -l) 437 if [[ "$count" -eq 0 ]]; then 438 echo -e "${YELLOW}Queue is already empty.${NC}" 439 return 440 fi 441 rm -f "$MAIL_DIR"/*.eml 442 echo -e "${GREEN}Cleared $count email(s) from queue.${NC}" 443} 444watch_queue() { 445 echo -e "${CYAN}Watching for new emails... (Ctrl+C to stop)${NC}" 446 echo "" 447 local last_count=0 448 while true; do 449 local current_count=$(ls -1 "$MAIL_DIR"/*.eml 2>/dev/null | wc -l) 450 if [[ "$current_count" -gt "$last_count" ]]; then 451 echo -e "${GREEN}[$(date +%H:%M:%S)] New email received!${NC}" 452 view_email latest 453 last_count=$current_count 454 fi 455 sleep 1 456 done 457} 458count_queue() { 459 local count=$(ls -1 "$MAIL_DIR"/*.eml 2>/dev/null | wc -l) 460 echo "$count" 461} 462case "${1:-}" in 463 ""|list) 464 list_emails 465 ;; 466 latest|[0-9]*) 467 view_email "$1" 468 ;; 469 clear) 470 clear_queue 471 ;; 472 watch) 473 watch_queue 474 ;; 475 count) 476 count_queue 477 ;; 478 help|--help|-h) 479 show_help 480 ;; 481 *) 482 if [[ -f "$MAIL_DIR/$1" ]] || [[ -f "$1" ]]; then 483 view_email "$1" 484 else 485 echo -e "${RED}Unknown command: $1${NC}" 486 show_help 487 exit 1 488 fi 489 ;; 490esac 491MAILQ_EOF 492chmod +x /usr/local/bin/bspds-mailq 493log_success "Mail trap configured" 494log_info "Creating BSPDS configuration..." 495mkdir -p /etc/bspds 496cat > /etc/bspds/bspds.env << EOF 497SERVER_HOST=127.0.0.1 498SERVER_PORT=3000 499PDS_HOSTNAME=${PDS_DOMAIN} 500DATABASE_URL=postgres://bspds:${DB_PASSWORD}@localhost:5432/pds 501DATABASE_MAX_CONNECTIONS=100 502DATABASE_MIN_CONNECTIONS=10 503S3_ENDPOINT=http://localhost:9000 504AWS_REGION=us-east-1 505S3_BUCKET=pds-blobs 506AWS_ACCESS_KEY_ID=minioadmin 507AWS_SECRET_ACCESS_KEY=${MINIO_PASSWORD} 508VALKEY_URL=redis://localhost:6379 509JWT_SECRET=${JWT_SECRET} 510DPOP_SECRET=${DPOP_SECRET} 511MASTER_KEY=${MASTER_KEY} 512PLC_DIRECTORY_URL=https://plc.directory 513APPVIEW_URL=https://api.bsky.app 514CRAWLERS=https://bsky.network 515AVAILABLE_USER_DOMAINS=${PDS_DOMAIN} 516MAIL_FROM_ADDRESS=noreply@${PDS_DOMAIN} 517MAIL_FROM_NAME=BSPDS 518SENDMAIL_PATH=/usr/local/bin/bspds-sendmail 519EOF 520chmod 600 /etc/bspds/bspds.env 521log_success "Configuration created" 522log_info "Creating BSPDS service user..." 523id -u bspds &>/dev/null || useradd -r -s /sbin/nologin bspds 524cp /opt/bspds/target/release/bspds /usr/local/bin/ 525mkdir -p /var/lib/bspds 526cp -r /opt/bspds/frontend/dist /var/lib/bspds/frontend 527chown -R bspds:bspds /var/lib/bspds 528log_success "BSPDS binary installed" 529log_info "Creating systemd service..." 530cat > /etc/systemd/system/bspds.service << 'EOF' 531[Unit] 532Description=BSPDS - AT Protocol PDS 533After=network.target postgresql.service minio.service 534[Service] 535Type=simple 536User=bspds 537Group=bspds 538EnvironmentFile=/etc/bspds/bspds.env 539Environment=FRONTEND_DIR=/var/lib/bspds/frontend 540ExecStart=/usr/local/bin/bspds 541Restart=always 542RestartSec=5 543[Install] 544WantedBy=multi-user.target 545EOF 546systemctl daemon-reload 547systemctl enable bspds 548systemctl start bspds 549log_success "BSPDS service created and started" 550log_info "Installing nginx..." 551apt install -y nginx certbot python3-certbot-nginx 552log_success "nginx installed" 553log_info "Configuring nginx..." 554cat > /etc/nginx/sites-available/bspds << EOF 555server { 556 listen 80; 557 listen [::]:80; 558 server_name ${PDS_DOMAIN} *.${PDS_DOMAIN}; 559 location / { 560 proxy_pass http://127.0.0.1:3000; 561 proxy_http_version 1.1; 562 proxy_set_header Upgrade \$http_upgrade; 563 proxy_set_header Connection "upgrade"; 564 proxy_set_header Host \$host; 565 proxy_set_header X-Real-IP \$remote_addr; 566 proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; 567 proxy_set_header X-Forwarded-Proto \$scheme; 568 proxy_read_timeout 86400; 569 proxy_send_timeout 86400; 570 client_max_body_size 100M; 571 } 572} 573EOF 574ln -sf /etc/nginx/sites-available/bspds /etc/nginx/sites-enabled/ 575rm -f /etc/nginx/sites-enabled/default 576nginx -t 577systemctl reload nginx 578log_success "nginx configured" 579log_info "Configuring firewall (ufw)..." 580apt install -y ufw 581ufw --force reset 582ufw default deny incoming 583ufw default allow outgoing 584ufw allow ssh comment 'SSH' 585ufw allow 80/tcp comment 'HTTP' 586ufw allow 443/tcp comment 'HTTPS' 587ufw --force enable 588log_success "Firewall configured" 589log_info "Obtaining SSL certificate..." 590certbot --nginx -d "${PDS_DOMAIN}" -d "*.${PDS_DOMAIN}" --email "${CERTBOT_EMAIL}" --agree-tos --non-interactive || { 591 log_warn "Wildcard cert failed (requires DNS challenge). Trying single domain..." 592 certbot --nginx -d "${PDS_DOMAIN}" --email "${CERTBOT_EMAIL}" --agree-tos --non-interactive 593} 594log_success "SSL certificate obtained" 595log_info "Verifying installation..." 596sleep 3 597if curl -s "http://localhost:3000/xrpc/_health" | grep -q "version"; then 598 log_success "BSPDS is responding!" 599else 600 log_warn "BSPDS may still be starting up. Check: journalctl -u bspds -f" 601fi 602echo "" 603echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 604echo -e "${GREEN} INSTALLATION COMPLETE!${NC}" 605echo -e "${CYAN}═══════════════════════════════════════════════════════════════════${NC}" 606echo "" 607echo -e "Your PDS is now running at: ${GREEN}https://${PDS_DOMAIN}${NC}" 608echo "" 609echo -e "${YELLOW}IMPORTANT: Save these credentials securely!${NC}" 610echo "" 611echo "Database password: ${DB_PASSWORD}" 612echo "MinIO password: ${MINIO_PASSWORD}" 613echo "" 614echo "Configuration file: /etc/bspds/bspds.env" 615echo "" 616echo -e "${CYAN}Useful commands:${NC}" 617echo " journalctl -u bspds -f # View BSPDS logs" 618echo " systemctl status bspds # Check BSPDS status" 619echo " systemctl restart bspds # Restart BSPDS" 620echo " curl https://${PDS_DOMAIN}/xrpc/_health # Health check" 621echo "" 622echo -e "${CYAN}Mail queue (for testing):${NC}" 623echo " bspds-mailq # List all captured emails" 624echo " bspds-mailq latest # View most recent email" 625echo " bspds-mailq 1 # View email #1 from list" 626echo " bspds-mailq watch # Watch for new emails live" 627echo " bspds-mailq clear # Clear all captured emails" 628echo "" 629echo " Emails are saved to: /var/spool/bspds-mail/" 630echo "" 631echo -e "${CYAN}DNS Records Summary:${NC}" 632echo "" 633echo " ${PDS_DOMAIN} A ${IPV4}" 634if [[ "$IPV6" != "Not available" ]]; then 635echo " ${PDS_DOMAIN} AAAA ${IPV6}" 636fi 637echo " *.${PDS_DOMAIN} A ${IPV4}" 638if [[ "$IPV6" != "Not available" ]]; then 639echo " *.${PDS_DOMAIN} AAAA ${IPV6}" 640fi 641echo "" 642echo -e "${GREEN}Enjoy your new AT Protocol PDS!${NC}"