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