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}"