template-saas/apps/database/scripts/create-database.sh
rckrdmrd 68d3c54023 docs(ai): Update documentation and fix DDL scripts
Documentation:
- Update SAAS-006-ai-integration.md with full implementation details
- Update _MAP.md with AI schema (30 tables, 11 schemas)
- Update PROJECT-STATUS.md (67% completion)

Database fixes:
- Add update_updated_at_column() function to 03-functions.sql
- Add trigger creation for ai.configs in 03-functions.sql
- Fix partial index in 02-ai-usage.sql (remove CURRENT_DATE)
- Add AI schema grants to create-database.sh
- Add AI to SCHEMA_ORDER array

Validated: Database recreation successful with all AI objects

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-07 07:21:56 -06:00

239 lines
9.0 KiB
Bash
Executable File

#!/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;
GRANT USAGE ON SCHEMA ai 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"
"ai"
)
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 TABLES IN SCHEMA ai TO $DB_USER;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA billing TO $DB_USER;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA ai 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;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA ai 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}"