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