This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
2026-03-23 14:51:55 +00:00

210 lines
8.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# ============================================================
# Timmy Tower API — One-shot production setup for Ubuntu 22.04+
#
# Run as root on the VPS (same hermes droplet as the Lightning node):
# bash setup-api.sh
#
# Prerequisites:
# - setup.sh (Bitcoin node bootstrap) has already run
# - Tailscale is authenticated
# ============================================================
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; CYAN='\033[0;36m'; NC='\033[0m'
info() { echo -e "${CYAN}[setup-api]${NC} $*"; }
ok() { echo -e "${GREEN}[ok]${NC} $*"; }
warn() { echo -e "${YELLOW}[warn]${NC} $*"; }
die() { echo -e "${RED}[error]${NC} $*"; exit 1; }
[[ $EUID -ne 0 ]] && die "Run as root: sudo bash setup-api.sh"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DEPLOY_DIR="/opt/timmy-tower"
DB_NAME="timmy_tower"
DB_USER="timmy"
info "Starting Timmy Tower API setup..."
echo ""
# ── 1. Node.js 24 via NodeSource ─────────────────────────────
info "Installing Node.js 24..."
if ! command -v node &>/dev/null || [[ "$(node -v | cut -d. -f1 | tr -d v)" -lt 24 ]]; then
curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
apt-get install -y -qq nodejs
fi
node -v
ok "Node.js $(node -v) ready"
# ── 2. PostgreSQL ────────────────────────────────────────────
info "Installing PostgreSQL..."
if ! command -v psql &>/dev/null; then
apt-get install -y -qq postgresql postgresql-contrib
systemctl enable postgresql
systemctl start postgresql
fi
ok "PostgreSQL ready"
# Create database and user (idempotent)
info "Provisioning database..."
DB_PASS=$(openssl rand -hex 16)
sudo -u postgres psql -tc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'" | grep -q 1 || \
sudo -u postgres psql -c "CREATE USER $DB_USER WITH PASSWORD '$DB_PASS';"
sudo -u postgres psql -tc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'" | grep -q 1 || \
sudo -u postgres psql -c "CREATE DATABASE $DB_NAME OWNER $DB_USER;"
sudo -u postgres psql -c "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" 2>/dev/null || true
ok "Database '$DB_NAME' provisioned"
# ── 3. Caddy (reverse proxy with automatic HTTPS) ───────────
info "Installing Caddy..."
if ! command -v caddy &>/dev/null; then
apt-get install -y -qq debian-keyring debian-archive-keyring apt-transport-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | tee /etc/apt/sources.list.d/caddy-stable.list
apt-get update -qq
apt-get install -y -qq caddy
fi
ok "Caddy ready"
# ── 4. System user ───────────────────────────────────────────
info "Creating system user..."
if ! id "$DB_USER" &>/dev/null; then
useradd --system --create-home --shell /usr/sbin/nologin "$DB_USER"
fi
ok "User '$DB_USER' ready"
# ── 5. Deploy directory ──────────────────────────────────────
info "Setting up deploy directory..."
mkdir -p "$DEPLOY_DIR"
chown "$DB_USER:$DB_USER" "$DEPLOY_DIR"
# Install externalized npm packages (not bundled by esbuild)
if [[ ! -d "$DEPLOY_DIR/node_modules" ]]; then
info "Installing external npm packages..."
cd "$DEPLOY_DIR"
cat > package.json <<'PKG'
{
"private": true,
"dependencies": {
"nostr-tools": "^2.23.3",
"cookie-parser": "^1.4.7"
}
}
PKG
npm install --production
chown -R "$DB_USER:$DB_USER" "$DEPLOY_DIR"
fi
ok "Deploy directory ready"
# ── 6. Environment file ─────────────────────────────────────
info "Writing environment file..."
if [[ ! -f "$DEPLOY_DIR/.env" ]]; then
cat > "$DEPLOY_DIR/.env" <<ENV
# Timmy Tower API — Production environment
# Generated: $(date -u)
NODE_ENV=production
PORT=8080
# Database
DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}
# LNbits — running on same host via Docker (see docker-compose.yml)
LNBITS_URL=http://localhost:5000
LNBITS_API_KEY=<set-after-lnbits-wallet-creation>
# AI backend — set one of these
# Option A: Direct Anthropic
# ANTHROPIC_API_KEY=sk-ant-...
# Option B: OpenRouter (Anthropic SDK compat layer)
# AI_INTEGRATIONS_ANTHROPIC_BASE_URL=https://openrouter.ai/api/v1
# AI_INTEGRATIONS_ANTHROPIC_API_KEY=sk-or-...
# Nostr identity (generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))")
# TIMMY_NOSTR_NSEC=<hex-secret-key>
# Relay policy shared secret (must match relay-policy container)
# RELAY_POLICY_SECRET=<shared-secret>
ENV
chmod 600 "$DEPLOY_DIR/.env"
chown "$DB_USER:$DB_USER" "$DEPLOY_DIR/.env"
warn "Edit $DEPLOY_DIR/.env to set API keys before starting the service"
else
# Update DATABASE_URL if DB was just created
if ! grep -q "DATABASE_URL" "$DEPLOY_DIR/.env"; then
echo "DATABASE_URL=postgres://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}" >> "$DEPLOY_DIR/.env"
fi
warn ".env already exists — not overwriting. DB password: $DB_PASS"
fi
# ── 7. Systemd service ──────────────────────────────────────
info "Installing systemd service..."
cp "$SCRIPT_DIR/timmy-tower.service" /etc/systemd/system/timmy-tower.service
systemctl daemon-reload
systemctl enable timmy-tower
ok "Systemd service installed (timmy-tower)"
# ── 8. Caddy config ─────────────────────────────────────────
info "Installing Caddy config..."
mkdir -p /var/log/caddy
cp "$SCRIPT_DIR/Caddyfile" /etc/caddy/Caddyfile
ok "Caddyfile installed"
echo ""
echo -e "${YELLOW}NOTE: Set TIMMY_DOMAIN in /etc/caddy/Caddyfile or as env var before starting Caddy.${NC}"
echo -e "${YELLOW} For HTTPS, point your DNS A record to this server's IP first.${NC}"
echo ""
# ── 9. Logrotate ─────────────────────────────────────────────
info "Installing logrotate config..."
mkdir -p /var/log/timmy-tower
chown "$DB_USER:$DB_USER" /var/log/timmy-tower
cp "$SCRIPT_DIR/logrotate.conf" /etc/logrotate.d/timmy-tower
ok "Logrotate configured"
# ── 10. Firewall — open HTTP/HTTPS ───────────────────────────
info "Opening firewall ports for HTTP/HTTPS..."
ufw allow 80/tcp comment "HTTP (Caddy)" 2>/dev/null || true
ufw allow 443/tcp comment "HTTPS (Caddy)" 2>/dev/null || true
ok "Firewall updated"
# ── 11. Health check cron ────────────────────────────────────
info "Installing health check cron..."
HEALTHCHECK_SCRIPT="$DEPLOY_DIR/healthcheck.sh"
cp "$SCRIPT_DIR/healthcheck.sh" "$HEALTHCHECK_SCRIPT"
chmod +x "$HEALTHCHECK_SCRIPT"
chown "$DB_USER:$DB_USER" "$HEALTHCHECK_SCRIPT"
# Add cron job (every 5 minutes)
crontab -l 2>/dev/null | grep -v "timmy-tower.*healthcheck" | crontab - || true
(crontab -l 2>/dev/null; echo "*/5 * * * * bash $HEALTHCHECK_SCRIPT >> /var/log/timmy-tower/healthcheck.log 2>&1 # timmy-tower healthcheck") | crontab -
ok "Health check cron installed (every 5 min)"
# ── Done ─────────────────────────────────────────────────────
echo ""
echo -e "${GREEN}════════════════════════════════════════${NC}"
echo -e "${GREEN} API setup complete — next steps:${NC}"
echo -e "${GREEN}════════════════════════════════════════${NC}"
echo ""
echo -e " 1. ${CYAN}Edit environment variables:${NC}"
echo -e " nano $DEPLOY_DIR/.env"
echo -e " (set LNBITS_API_KEY, AI keys, Nostr identity)"
echo ""
echo -e " 2. ${CYAN}Deploy the API bundle:${NC}"
echo -e " bash $SCRIPT_DIR/deploy.sh"
echo -e " (or from dev machine: bash infrastructure/api-server/deploy.sh)"
echo ""
echo -e " 3. ${CYAN}Set your domain and start Caddy:${NC}"
echo -e " export TIMMY_DOMAIN=yourdomain.com"
echo -e " systemctl restart caddy"
echo ""
echo -e " 4. ${CYAN}Start the API:${NC}"
echo -e " systemctl start timmy-tower"
echo -e " journalctl -u timmy-tower -f"
echo ""
echo -e " 5. ${CYAN}Verify health:${NC}"
echo -e " curl -s http://localhost:8080/api/health | jq ."
echo ""
echo -e " DB credentials: ${YELLOW}postgres://${DB_USER}:${DB_PASS}@localhost:5432/${DB_NAME}${NC}"
echo ""