264 lines
9.6 KiB
Bash
264 lines
9.6 KiB
Bash
#!/bin/bash
|
|
# =============================================================================
|
|
# Life OS Infrastructure Setup Script
|
|
# Server: defiant-01 (46.225.166.142) - Ubuntu 24.04 LTS
|
|
# Run as: root
|
|
# Purpose: Repeatable setup of Life OS DEV and PROD environments on Hetzner VM
|
|
# =============================================================================
|
|
# USAGE:
|
|
# Full run: bash lifeos-setup.sh
|
|
# Single section: bash lifeos-setup.sh network
|
|
# bash lifeos-setup.sh database
|
|
# bash lifeos-setup.sh app
|
|
# bash lifeos-setup.sh nginx
|
|
# bash lifeos-setup.sh ssl
|
|
# =============================================================================
|
|
|
|
set -e # Exit on any error
|
|
|
|
# --- Configuration -----------------------------------------------------------
|
|
LIFEOS_NETWORK="lifeos_network"
|
|
DB_CONTAINER="lifeos-db"
|
|
DB_IMAGE="postgres:16-alpine"
|
|
DB_PROD="lifeos_prod"
|
|
DB_DEV="lifeos_dev"
|
|
APP_PROD_CONTAINER="lifeos-prod"
|
|
APP_DEV_CONTAINER="lifeos-dev"
|
|
APP_PROD_PORT="8002"
|
|
APP_DEV_PORT="8003"
|
|
DOMAIN_PROD="lifeos.invixiom.com"
|
|
DOMAIN_DEV="lifeos-dev.invixiom.com"
|
|
CERT_PATH="/etc/letsencrypt/live/kasm.invixiom.com"
|
|
LIFEOS_DIR="/opt/lifeos"
|
|
# DB passwords - change these before running
|
|
DB_PROD_PASSWORD="CHANGE_ME_PROD"
|
|
DB_DEV_PASSWORD="CHANGE_ME_DEV"
|
|
# -----------------------------------------------------------------------------
|
|
|
|
section() {
|
|
echo ""
|
|
echo "=============================================="
|
|
echo " $1"
|
|
echo "=============================================="
|
|
}
|
|
|
|
# =============================================================================
|
|
# SECTION 1: Docker Network
|
|
# =============================================================================
|
|
setup_network() {
|
|
section "SECTION 1: Docker Network"
|
|
|
|
if docker network ls | grep -q "$LIFEOS_NETWORK"; then
|
|
echo "Network $LIFEOS_NETWORK already exists, skipping."
|
|
else
|
|
docker network create "$LIFEOS_NETWORK"
|
|
echo "Created network: $LIFEOS_NETWORK"
|
|
fi
|
|
|
|
docker network ls | grep lifeos
|
|
}
|
|
|
|
# =============================================================================
|
|
# SECTION 2: PostgreSQL Container
|
|
# =============================================================================
|
|
setup_database() {
|
|
section "SECTION 2: PostgreSQL Container"
|
|
|
|
if docker ps -a | grep -q "$DB_CONTAINER"; then
|
|
echo "Container $DB_CONTAINER already exists, skipping creation."
|
|
else
|
|
docker run -d \
|
|
--name "$DB_CONTAINER" \
|
|
--network "$LIFEOS_NETWORK" \
|
|
--restart unless-stopped \
|
|
-e POSTGRES_PASSWORD="$DB_PROD_PASSWORD" \
|
|
-v lifeos_db_data:/var/lib/postgresql/data \
|
|
"$DB_IMAGE"
|
|
echo "Created container: $DB_CONTAINER"
|
|
|
|
echo "Waiting for Postgres to be ready..."
|
|
sleep 5
|
|
fi
|
|
|
|
# Create PROD database
|
|
docker exec "$DB_CONTAINER" psql -U postgres -tc \
|
|
"SELECT 1 FROM pg_database WHERE datname='$DB_PROD'" | grep -q 1 || \
|
|
docker exec "$DB_CONTAINER" psql -U postgres \
|
|
-c "CREATE DATABASE $DB_PROD;"
|
|
|
|
# Create DEV database
|
|
docker exec "$DB_CONTAINER" psql -U postgres -tc \
|
|
"SELECT 1 FROM pg_database WHERE datname='$DB_DEV'" | grep -q 1 || \
|
|
docker exec "$DB_CONTAINER" psql -U postgres \
|
|
-c "CREATE DATABASE $DB_DEV;"
|
|
|
|
# Create DEV user with separate password
|
|
docker exec "$DB_CONTAINER" psql -U postgres -tc \
|
|
"SELECT 1 FROM pg_roles WHERE rolname='lifeos_dev'" | grep -q 1 || \
|
|
docker exec "$DB_CONTAINER" psql -U postgres \
|
|
-c "CREATE USER lifeos_dev WITH PASSWORD '$DB_DEV_PASSWORD';"
|
|
|
|
docker exec "$DB_CONTAINER" psql -U postgres \
|
|
-c "GRANT ALL PRIVILEGES ON DATABASE $DB_DEV TO lifeos_dev;"
|
|
|
|
echo "Databases ready:"
|
|
docker exec "$DB_CONTAINER" psql -U postgres -c "\l" | grep lifeos
|
|
}
|
|
|
|
# =============================================================================
|
|
# SECTION 3: Application Directory Structure
|
|
# =============================================================================
|
|
setup_app_dirs() {
|
|
section "SECTION 3: Application Directory Structure"
|
|
|
|
mkdir -p "$LIFEOS_DIR/prod"
|
|
mkdir -p "$LIFEOS_DIR/dev"
|
|
mkdir -p "$LIFEOS_DIR/prod/files"
|
|
mkdir -p "$LIFEOS_DIR/dev/files"
|
|
|
|
echo "Created directory structure:"
|
|
ls -la "$LIFEOS_DIR"
|
|
}
|
|
|
|
# =============================================================================
|
|
# SECTION 4: Nginx Configuration
|
|
# (Run after app containers are up and SSL cert is expanded)
|
|
# =============================================================================
|
|
setup_nginx() {
|
|
section "SECTION 4: Nginx Virtual Hosts"
|
|
|
|
# Add Life OS PROD and DEV server blocks to existing invixiom config
|
|
# We append to the existing file - kasm/files/code blocks remain untouched
|
|
|
|
if grep -q "$DOMAIN_PROD" /etc/nginx/sites-available/invixiom; then
|
|
echo "Nginx config for $DOMAIN_PROD already exists, skipping."
|
|
else
|
|
cat >> /etc/nginx/sites-available/invixiom << EOF
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
server_name $DOMAIN_PROD;
|
|
ssl_certificate $CERT_PATH/fullchain.pem;
|
|
ssl_certificate_key $CERT_PATH/privkey.pem;
|
|
location / {
|
|
proxy_pass http://127.0.0.1:$APP_PROD_PORT;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
}
|
|
|
|
server {
|
|
listen 443 ssl;
|
|
server_name $DOMAIN_DEV;
|
|
ssl_certificate $CERT_PATH/fullchain.pem;
|
|
ssl_certificate_key $CERT_PATH/privkey.pem;
|
|
location / {
|
|
proxy_pass http://127.0.0.1:$APP_DEV_PORT;
|
|
proxy_http_version 1.1;
|
|
proxy_set_header Upgrade \$http_upgrade;
|
|
proxy_set_header Connection "upgrade";
|
|
proxy_set_header Host \$host;
|
|
proxy_set_header X-Real-IP \$remote_addr;
|
|
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto \$scheme;
|
|
}
|
|
}
|
|
EOF
|
|
echo "Added Nginx config for $DOMAIN_PROD and $DOMAIN_DEV"
|
|
fi
|
|
|
|
# Add new domains to the HTTP->HTTPS redirect block
|
|
# (manual step - see notes below)
|
|
echo ""
|
|
echo "NOTE: Also add $DOMAIN_PROD and $DOMAIN_DEV to the server_name line"
|
|
echo "in the HTTP redirect block at the top of /etc/nginx/sites-available/invixiom"
|
|
|
|
# Test and reload
|
|
nginx -t && systemctl reload nginx
|
|
echo "Nginx reloaded."
|
|
}
|
|
|
|
# =============================================================================
|
|
# SECTION 5: SSL Certificate Expansion
|
|
# (Expand Let's Encrypt cert to cover new subdomains)
|
|
# =============================================================================
|
|
setup_ssl() {
|
|
section "SECTION 5: SSL Certificate Expansion"
|
|
|
|
certbot certonly --nginx \
|
|
-d kasm.invixiom.com \
|
|
-d files.invixiom.com \
|
|
-d code.invixiom.com \
|
|
-d "$DOMAIN_PROD" \
|
|
-d "$DOMAIN_DEV" \
|
|
--expand
|
|
|
|
systemctl reload nginx
|
|
echo "SSL cert expanded and Nginx reloaded."
|
|
}
|
|
|
|
# =============================================================================
|
|
# MAIN
|
|
# =============================================================================
|
|
case "${1:-all}" in
|
|
network) setup_network ;;
|
|
database) setup_database ;;
|
|
dirs) setup_app_dirs ;;
|
|
nginx) setup_nginx ;;
|
|
ssl) setup_ssl ;;
|
|
all)
|
|
setup_network
|
|
setup_database
|
|
setup_app_dirs
|
|
# nginx and ssl run after app containers are built
|
|
echo ""
|
|
echo "=============================================="
|
|
echo " Sections 1-3 complete."
|
|
echo " Next: build Life OS Docker image, then run:"
|
|
echo " bash lifeos-setup.sh ssl"
|
|
echo " bash lifeos-setup.sh nginx"
|
|
echo "=============================================="
|
|
;;
|
|
*)
|
|
echo "Unknown section: $1"
|
|
echo "Usage: bash lifeos-setup.sh [network|database|dirs|nginx|ssl|all]"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# =============================================================================
|
|
# SECTION 6: Data Migration (reference - already completed)
|
|
# Documents the steps used to migrate Supabase prod data to lifeos_prod
|
|
# =============================================================================
|
|
setup_migration_notes() {
|
|
section "SECTION 6: Data Migration Notes"
|
|
echo "Migration completed 2026-02-27"
|
|
echo ""
|
|
echo "Steps used:"
|
|
echo " 1. Exported data from Supabase using Python supabase client (supabase_export.py)"
|
|
echo " 2. Applied schema: docker exec -i lifeos-db psql -U postgres -d lifeos_prod < lifeos_schema_r0.sql"
|
|
echo " 3. Imported data: docker exec -i lifeos-db psql -U postgres -d lifeos_prod < lifeos_export.sql"
|
|
echo ""
|
|
echo "Final row counts:"
|
|
docker exec lifeos-db psql -U postgres -d lifeos_prod -c "
|
|
SELECT 'domains' as table_name, count(*) FROM domains UNION ALL
|
|
SELECT 'areas', count(*) FROM areas UNION ALL
|
|
SELECT 'projects', count(*) FROM projects UNION ALL
|
|
SELECT 'tasks', count(*) FROM tasks UNION ALL
|
|
SELECT 'notes', count(*) FROM notes UNION ALL
|
|
SELECT 'links', count(*) FROM links UNION ALL
|
|
SELECT 'files', count(*) FROM files UNION ALL
|
|
SELECT 'daily_focus', count(*) FROM daily_focus UNION ALL
|
|
SELECT 'capture', count(*) FROM capture UNION ALL
|
|
SELECT 'context_types', count(*) FROM context_types;
|
|
"
|
|
echo ""
|
|
echo "Note: files table is empty - Supabase Storage paths are obsolete."
|
|
echo "File uploads start fresh in Release 1 using local storage."
|
|
}
|