#!/bin/bash # ============================================ # TEMPLATE-SAAS: Create Database Script # Version: 1.0.0 # ============================================ set -e # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Default values DB_HOST="${DB_HOST:-localhost}" DB_PORT="${DB_PORT:-5432}" DB_NAME="${DB_NAME:-template_saas_dev}" DB_USER="${DB_USER:-template_saas_user}" DB_PASSWORD="${DB_PASSWORD:-template_saas_dev_2026}" DB_ADMIN_USER="${DB_ADMIN_USER:-postgres}" DB_ADMIN_PASSWORD="${DB_ADMIN_PASSWORD:-postgres}" # Script directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DDL_DIR="$SCRIPT_DIR/../ddl" SEEDS_DIR="$SCRIPT_DIR/../seeds" echo -e "${BLUE}============================================${NC}" echo -e "${BLUE} TEMPLATE-SAAS Database Creation${NC}" echo -e "${BLUE}============================================${NC}" echo "" echo -e "Host: ${YELLOW}$DB_HOST:$DB_PORT${NC}" echo -e "Database: ${YELLOW}$DB_NAME${NC}" echo -e "User: ${YELLOW}$DB_USER${NC}" echo "" # Function to run SQL as admin run_sql_admin() { PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d postgres -c "$1" } # Function to run SQL file as admin on target DB run_sql_file_admin() { PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d "$DB_NAME" -f "$1" } # Function to run SQL file as app user run_sql_file() { PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -f "$1" } # Check PostgreSQL connection echo -e "${BLUE}[1/7] Checking PostgreSQL connection...${NC}" if ! PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d postgres -c '\q' 2>/dev/null; then echo -e "${RED}ERROR: Cannot connect to PostgreSQL at $DB_HOST:$DB_PORT${NC}" exit 1 fi echo -e "${GREEN}✓ PostgreSQL connection successful${NC}" # Create user if not exists (skip if admin doesn't have CREATEROLE) echo -e "${BLUE}[2/7] Creating database user...${NC}" USER_EXISTS=$(PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d postgres -tAc "SELECT 1 FROM pg_roles WHERE rolname='$DB_USER'") if [ "$USER_EXISTS" != "1" ]; then # Check if admin has CREATEROLE privilege CAN_CREATE_ROLE=$(PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d postgres -tAc "SELECT rolcreaterole FROM pg_roles WHERE rolname='$DB_ADMIN_USER'") if [ "$CAN_CREATE_ROLE" = "t" ]; then run_sql_admin "CREATE USER $DB_USER WITH PASSWORD '$DB_PASSWORD';" echo -e "${GREEN}✓ User $DB_USER created${NC}" else echo -e "${YELLOW}→ Admin cannot create roles. Using admin user as DB owner.${NC}" DB_USER="$DB_ADMIN_USER" DB_PASSWORD="$DB_ADMIN_PASSWORD" fi else echo -e "${YELLOW}→ User $DB_USER already exists${NC}" fi # Create database if not exists echo -e "${BLUE}[3/7] Creating database...${NC}" DB_EXISTS=$(PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d postgres -tAc "SELECT 1 FROM pg_database WHERE datname='$DB_NAME'") if [ "$DB_EXISTS" != "1" ]; then run_sql_admin "CREATE DATABASE $DB_NAME OWNER $DB_USER;" echo -e "${GREEN}✓ Database $DB_NAME created${NC}" else echo -e "${YELLOW}→ Database $DB_NAME already exists${NC}" fi # Grant privileges run_sql_admin "GRANT ALL PRIVILEGES ON DATABASE $DB_NAME TO $DB_USER;" # Load extensions (requires admin) echo -e "${BLUE}[4/7] Loading extensions...${NC}" if [ -f "$DDL_DIR/00-extensions.sql" ]; then run_sql_file_admin "$DDL_DIR/00-extensions.sql" echo -e "${GREEN}✓ Extensions loaded${NC}" else echo -e "${RED}ERROR: 00-extensions.sql not found${NC}" exit 1 fi # Load base DDL files echo -e "${BLUE}[5/7] Loading DDL (schemas, enums, functions)...${NC}" # Schemas if [ -f "$DDL_DIR/01-schemas.sql" ]; then run_sql_file_admin "$DDL_DIR/01-schemas.sql" echo -e "${GREEN} ✓ Schemas created${NC}" fi # Enums if [ -f "$DDL_DIR/02-enums.sql" ]; then run_sql_file_admin "$DDL_DIR/02-enums.sql" echo -e "${GREEN} ✓ Enums created${NC}" fi # Grant schema privileges to user PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d "$DB_NAME" -c " GRANT USAGE ON SCHEMA auth TO $DB_USER; GRANT USAGE ON SCHEMA tenants TO $DB_USER; GRANT USAGE ON SCHEMA users TO $DB_USER; GRANT USAGE ON SCHEMA billing TO $DB_USER; GRANT USAGE ON SCHEMA plans TO $DB_USER; GRANT USAGE ON SCHEMA audit TO $DB_USER; GRANT USAGE ON SCHEMA notifications TO $DB_USER; GRANT USAGE ON SCHEMA feature_flags TO $DB_USER; GRANT USAGE ON SCHEMA storage TO $DB_USER; " # Load schema tables in order echo -e "${BLUE}[6/7] Loading tables...${NC}" # Order matters for foreign keys SCHEMA_ORDER=( "tenants" "plans" "users" "auth" "billing" "audit" "notifications" "feature_flags" ) for schema in "${SCHEMA_ORDER[@]}"; do SCHEMA_DIR="$DDL_DIR/schemas/$schema/tables" if [ -d "$SCHEMA_DIR" ]; then echo -e " Loading schema: ${YELLOW}$schema${NC}" for sql_file in $(ls "$SCHEMA_DIR"/*.sql 2>/dev/null | sort); do run_sql_file_admin "$sql_file" echo -e " ${GREEN}✓${NC} $(basename $sql_file)" done fi done # Load functions (after tables exist) if [ -f "$DDL_DIR/03-functions.sql" ]; then run_sql_file_admin "$DDL_DIR/03-functions.sql" echo -e "${GREEN} ✓ Functions created${NC}" fi # Grant table privileges PGPASSWORD="$DB_ADMIN_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_ADMIN_USER" -d "$DB_NAME" -c " GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA auth TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA tenants TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA users TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA billing TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA plans TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA audit TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA notifications TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA feature_flags TO $DB_USER; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA storage TO $DB_USER; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA billing TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA auth TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA tenants TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA users TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA billing TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA plans TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA audit TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA notifications TO $DB_USER; GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA feature_flags TO $DB_USER; " # Load seeds echo -e "${BLUE}[7/7] Loading seeds...${NC}" # Production seeds if [ -d "$SEEDS_DIR/prod" ]; then for sql_file in $(ls "$SEEDS_DIR/prod"/*.sql 2>/dev/null | sort); do run_sql_file_admin "$sql_file" echo -e " ${GREEN}✓${NC} $(basename $sql_file)" done fi # Development seeds (if DEV_SEEDS=1) if [ "$DEV_SEEDS" = "1" ] && [ -d "$SEEDS_DIR/dev" ]; then echo -e " Loading development seeds..." for sql_file in $(ls "$SEEDS_DIR/dev"/*.sql 2>/dev/null | sort); do run_sql_file_admin "$sql_file" echo -e " ${GREEN}✓${NC} $(basename $sql_file)" done fi # Summary echo "" echo -e "${GREEN}============================================${NC}" echo -e "${GREEN} Database creation completed!${NC}" echo -e "${GREEN}============================================${NC}" echo "" echo -e "Connection string:" echo -e "${YELLOW}postgresql://$DB_USER:$DB_PASSWORD@$DB_HOST:$DB_PORT/$DB_NAME${NC}" echo "" # Verify tables echo -e "${BLUE}Verification:${NC}" TABLE_COUNT=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema NOT IN ('pg_catalog', 'information_schema');") echo -e " Tables created: ${GREEN}$TABLE_COUNT${NC}" PLANS_COUNT=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT COUNT(*) FROM plans.plans;") echo -e " Plans seeded: ${GREEN}$PLANS_COUNT${NC}" PERMS_COUNT=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT COUNT(*) FROM users.permissions;") echo -e " Permissions seeded: ${GREEN}$PERMS_COUNT${NC}" FLAGS_COUNT=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT COUNT(*) FROM feature_flags.flags;") echo -e " Feature flags seeded: ${GREEN}$FLAGS_COUNT${NC}" TEMPLATES_COUNT=$(PGPASSWORD="$DB_PASSWORD" psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_USER" -d "$DB_NAME" -tAc "SELECT COUNT(*) FROM notifications.templates;") echo -e " Notification templates seeded: ${GREEN}$TEMPLATES_COUNT${NC}" echo "" echo -e "${GREEN}Done!${NC}"