diff --git a/README.md b/README.md index 448a4af..89b19d6 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ If you would like to contribute, please read [CONTRIBUTING.md](docs/CONTRIBUTING - **Theme Management:** next-themes for system preference detection ## Documentation + - [Changelog](docs/CHANGELOG.md) - Project changelog and release notes - [Standards & Guidelines](docs/CLAUDE.md) - Development standards and project guidelines -- [Test Documentation](test/README.md) - Testing principles and structure +- [Database Setup](docs/DATABASE_SETUP.md) - How to set up the local database for development diff --git a/docs/DATABASE_SETUP.md b/docs/DATABASE_SETUP.md new file mode 100644 index 0000000..75c9187 --- /dev/null +++ b/docs/DATABASE_SETUP.md @@ -0,0 +1,122 @@ +# Database Setup Guide for Local Development + +This guide explains how to set up a local PostgreSQL database for development using the provided `setup-db.sh` script. The script handles the installation of PostgreSQL (if needed), database creation, schema setup, and data seeding to provide you with a fully functional test environment. + +## Prerequisites + +Before running the setup script, ensure you have: + +1. **MacOS** - The script is designed for MacOS with Homebrew +2. **Homebrew** - The script uses Homebrew to install PostgreSQL if needed + - Install from [brew.sh](https://brew.sh) if not already installed +3. **Node.js** - Version 18 or higher is recommended + - The project uses Next.js and Prisma which require Node.js + +## Quick Start + +For developers who want to get started quickly: + +```bash +# Clone the repository (if you haven't already) +git clone https://github.com/yourusername/yapli.git +cd yapli + +# Make the setup script executable +chmod +x setup-db.sh + +# Run the setup script +./setup-db.sh +``` + +The script will guide you through the setup process with clear prompts and colorful status messages. + +## What the Setup Script Does + +The `setup-db.sh` script performs the following tasks: + +1. **Checks for PostgreSQL** - Verifies if PostgreSQL is installed and installs it via Homebrew if needed +2. **Ensures PostgreSQL is running** - Starts the PostgreSQL service if it's not already running +3. **Creates a database user** - Sets up the required Postgres superuser if needed +4. **Creates a database** - Creates the `yapli-local` database (or offers to recreate it if it exists) +5. **Sets up environment variables** - Creates or updates the `.env` file with the database connection string +6. **Applies database schema** - Runs Prisma migrations and ensures all tables are created +7. **Seeds the database** - Populates the database with test data for development + +## Test Data + +After running the setup script, your database will be populated with: + +- **Test User**: + - Email: `testuser@example.com` + - Password: `password123` + +- **Chat Rooms**: + - General (`roomUrl`: `696fcd`) + - Technology (`roomUrl`: `2zjbmc`) + +- **Sample Messages**: + - Messages from users "Alice", "Bob", "Charlie", and "Dana" + - One message with a link preview + +## Using the Database in Your Application + +After setup, you can start the application with: + +```bash +npm run dev +``` + +The application will connect to the local database using the connection string: + +``` +postgresql://postgres@localhost:5432/yapli-local +``` + +This connection string is automatically added to your `.env` file during setup. + +## Common Issues and Troubleshooting + +### PostgreSQL Not in PATH + +If you see a warning about PostgreSQL not being in your PATH, consider adding it permanently: + +```bash +echo 'export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH"' >> ~/.zshrc +source ~/.zshrc +``` + +### Database In Use Error + +If you encounter "database removal failed: ERROR: database is being accessed by other users" when trying to recreate the database, the script will automatically attempt to terminate connections. If this fails: + +1. Close any applications that might be using the database (pgAdmin, other terminal sessions, etc.) +2. Run the setup script again + +### Schema Changes + +If you've made changes to the Prisma schema (`prisma/schema.prisma`), you may need to update the database schema: + +```bash +npx prisma generate # Update the Prisma client +npx prisma db push # Push schema changes to the database +``` + +## Manually Viewing the Database + +To view the database tables after setup: + +```bash +psql -d yapli-local -c '\dt' # List all tables +psql -d yapli-local -c 'SELECT * FROM chatrooms;' # View chat rooms +``` + +## Resetting the Database + +If you want to completely reset the database to a clean state: + +```bash +npx prisma db push --force-reset # Reset the database and apply schema +npm run db:seed # Re-seed with test data +``` + +Or simply run the setup script again and choose 'y' when asked if you want to recreate the database. \ No newline at end of file diff --git a/package.json b/package.json index 684a790..eed59e8 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "build": "next build", "start": "node -r dotenv/config server.js dotenv_config_path=.env.production", "lint": "next lint", - "test": "jest", - "test:watch": "jest --watch" + "db:seed": "tsx prisma/seed.ts" }, "dependencies": { "@auth/prisma-adapter": "^2.9.1", @@ -33,20 +32,14 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4", - "@testing-library/jest-dom": "^6.6.3", - "@testing-library/react": "^16.3.0", - "@types/jest": "^30.0.0", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", "eslint-config-next": "15.3.2", - "jest": "^30.0.4", - "jest-environment-jsdom": "^30.0.4", "prisma": "^6.8.2", "tailwind-merge": "^3.3.0", "tailwindcss": "^4", - "ts-node": "^10.9.2", "tsx": "^4.19.4", "typescript": "^5" } diff --git a/prisma/seed.ts b/prisma/seed.ts new file mode 100644 index 0000000..95b0c21 --- /dev/null +++ b/prisma/seed.ts @@ -0,0 +1,91 @@ +import { PrismaClient } from '@prisma/client'; +import { randomUUID } from 'crypto'; + +const prisma = new PrismaClient(); + +async function main() { + // Create a test user + const user = await prisma.user.create({ + data: { + name: 'Test User', + email: 'testuser@example.com', + password: 'password123', // In a real app, this would be hashed + createdAt: new Date(), + updatedAt: new Date(), + } + }); + + // Create test chatrooms + const generalRoom = await prisma.chatroom.create({ + data: { + title: 'General', + roomUrl: '696fcd', + userId: user.id, + }, + }); + + const techRoom = await prisma.chatroom.create({ + data: { + title: 'Technology', + roomUrl: '2zjbmc', + userId: user.id, + }, + }); + + // Create some test messages + // Create messages with link previews + await prisma.message.create({ + data: { + alias: 'Alice', + message: 'Hello everyone! Welcome to the General chat.', + chatroomId: generalRoom.id, + }, + }); + + const bobMessage = await prisma.message.create({ + data: { + alias: 'Bob', + message: 'Hey Alice! Check out this link https://yapli.chat', + chatroomId: generalRoom.id, + }, + }); + + // Add a link preview to Bob's message + await prisma.linkPreview.create({ + data: { + messageId: bobMessage.id, + url: 'https://yapli.chat', + title: 'Yapli Chat', + description: 'A low-friction chat application built with Next.js and Prisma', + domain: 'yapli.chat', + siteName: 'Yapli', + }, + }); + + await prisma.message.create({ + data: { + alias: 'Charlie', + message: 'Did you see the latest developments in AI?', + chatroomId: techRoom.id, + }, + }); + + await prisma.message.create({ + data: { + alias: 'Dana', + message: 'Yes! The new models are impressive.', + chatroomId: techRoom.id, + }, + }); + + console.log('Database seeded with test data'); +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); \ No newline at end of file diff --git a/setup-db.sh b/setup-db.sh new file mode 100755 index 0000000..e031556 --- /dev/null +++ b/setup-db.sh @@ -0,0 +1,241 @@ +#!/bin/bash + +# Colors for output +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[0;33m' +BLUE='\033[0;34m' +NC='\033[0m' # No Color + +# Database name and user +DB_NAME="yapli-local" +DB_USER="postgres" + +echo -e "${BLUE}============================================${NC}" +echo -e "${BLUE} Yapli Database Setup Script ${NC}" +echo -e "${BLUE}============================================${NC}" + +# Function to check if command exists +command_exists() { + command -v "$1" >/dev/null 2>&1 +} + +echo -e "\n${BLUE}Step 1:${NC} Checking if PostgreSQL is installed..." + +# Check for Homebrew +if ! command_exists brew; then + echo -e "${RED}Homebrew is not installed. Please install Homebrew first.${NC}" + echo "Visit https://brew.sh for installation instructions." + exit 1 +fi + +# Check for PostgreSQL client +if ! command_exists psql; then + echo -e "${YELLOW}PostgreSQL client not found in PATH. Checking Homebrew installation...${NC}" + + # Check if PostgreSQL is installed via Homebrew + if brew list postgresql@16 &>/dev/null || brew list postgresql &>/dev/null; then + echo -e "${YELLOW}PostgreSQL is installed via Homebrew but not in PATH.${NC}" + + # Check which version is installed + if brew list postgresql@16 &>/dev/null; then + PG_PATH="/opt/homebrew/opt/postgresql@16/bin" + else + PG_PATH="/opt/homebrew/opt/postgresql/bin" + fi + + echo -e "${YELLOW}Adding PostgreSQL to PATH for this session...${NC}" + export PATH="$PG_PATH:$PATH" + + echo -e "${YELLOW}Consider adding this to your shell profile:${NC}" + echo -e " echo 'export PATH=\"$PG_PATH:\$PATH\"' >> ~/.zshrc" + else + echo -e "${YELLOW}PostgreSQL not found. Installing PostgreSQL 16...${NC}" + brew install postgresql@16 + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to install PostgreSQL.${NC}" + exit 1 + fi + + export PATH="/opt/homebrew/opt/postgresql@16/bin:$PATH" + + echo -e "${YELLOW}Consider adding PostgreSQL to your PATH:${NC}" + echo -e " echo 'export PATH=\"/opt/homebrew/opt/postgresql@16/bin:\$PATH\"' >> ~/.zshrc" + fi +fi + +# Start PostgreSQL service +echo -e "\n${BLUE}Step 2:${NC} Ensuring PostgreSQL service is running..." +if brew services list | grep postgresql | grep started &>/dev/null; then + echo -e "${GREEN}PostgreSQL service is already running.${NC}" +else + echo "Starting PostgreSQL service..." + if brew list postgresql@16 &>/dev/null; then + brew services start postgresql@16 + else + brew services start postgresql + fi + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to start PostgreSQL service.${NC}" + exit 1 + else + echo -e "${GREEN}PostgreSQL service started successfully.${NC}" + fi + + # Give PostgreSQL a moment to start up + echo "Waiting for PostgreSQL to start..." + sleep 3 +fi + +echo -e "\n${BLUE}Step 3:${NC} Setting up database user..." + +# Create postgres superuser if it doesn't exist +psql -c "\du" postgres | grep postgres &>/dev/null +if [ $? -ne 0 ]; then + echo "Creating postgres superuser..." + createuser -s postgres + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to create postgres user.${NC}" + exit 1 + else + echo -e "${GREEN}Created postgres superuser.${NC}" + fi +else + echo -e "${GREEN}Postgres superuser already exists.${NC}" +fi + +echo -e "\n${BLUE}Step 4:${NC} Creating database..." + +# Check if database already exists +psql -lqt | cut -d \| -f 1 | grep -qw "$DB_NAME" +if [ $? -eq 0 ]; then + echo -e "${YELLOW}Database '$DB_NAME' already exists.${NC}" + read -p "Do you want to drop and recreate it? (y/N): " CONFIRM + if [[ $CONFIRM =~ ^[Yy]$ ]]; then + echo "Dropping database '$DB_NAME'..." + # Try to drop normally first + if ! dropdb "$DB_NAME" 2>/dev/null; then + echo -e "${YELLOW}Database is currently in use. Attempting to terminate connections...${NC}" + # Terminate all connections to the database + psql -c "SELECT pg_terminate_backend(pg_stat_activity.pid) FROM pg_stat_activity WHERE pg_stat_activity.datname = '$DB_NAME' AND pid <> pg_backend_pid();" postgres + sleep 1 + # Try dropping again after terminating connections + if ! dropdb "$DB_NAME" 2>/dev/null; then + echo -e "${RED}Failed to drop database. Please manually stop applications that might be using the database and try again.${NC}" + exit 1 + fi + fi + + echo "Creating database '$DB_NAME'..." + createdb "$DB_NAME" + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to recreate database.${NC}" + exit 1 + else + echo -e "${GREEN}Database recreated successfully.${NC}" + fi + else + echo "Keeping existing database." + fi +else + echo "Creating database '$DB_NAME'..." + createdb "$DB_NAME" + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to create database.${NC}" + exit 1 + else + echo -e "${GREEN}Database created successfully.${NC}" + fi +fi + +echo -e "\n${BLUE}Step 5:${NC} Creating .env file with database connection string..." + +# Check if .env exists and if not, create it based on .env.example +if [ ! -f .env ]; then + if [ -f .env.example ]; then + echo "Creating .env file from .env.example template..." + cp .env.example .env + sed -i '' "s|DATABASE_URL=.*|DATABASE_URL=\"postgresql://$DB_USER@localhost:5432/$DB_NAME\"|g" .env + echo -e "${GREEN}.env file created.${NC}" + else + echo "Creating new .env file..." + cat > .env << EOF +EOF + echo -e "${GREEN}.env file created.${NC}" + fi +else + echo -e "${YELLOW}.env file already exists.${NC}" + read -p "Do you want to update the DATABASE_URL in .env? (y/N): " UPDATE_ENV + if [[ $UPDATE_ENV =~ ^[Yy]$ ]]; then + sed -i '' "s|DATABASE_URL=.*|DATABASE_URL=\"postgresql://$DB_USER@localhost:5432/$DB_NAME\"|g" .env + echo -e "${GREEN}DATABASE_URL updated in .env file.${NC}" + fi +fi + +echo -e "\n${BLUE}Step 6:${NC} Running database migrations..." + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + echo -e "${YELLOW}Installing dependencies...${NC}" + npm install + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to install dependencies.${NC}" + exit 1 + else + echo -e "${GREEN}Dependencies installed successfully.${NC}" + fi +fi + +# Run migrations +echo "Running Prisma migrations..." + +# First, try normal migrations +npx prisma migrate deploy + +# Additionally, ensure schema is fully applied (creates all tables from schema) +echo "Pushing complete schema to database..." +npx prisma db push --accept-data-loss + +if [ $? -ne 0 ]; then + echo -e "${RED}Failed to apply schema.${NC}" + exit 1 +else + echo -e "${GREEN}Schema applied successfully.${NC}" +fi + +echo -e "\n${BLUE}Step 7:${NC} Seeding database with test data..." + +# Check if the seed script exists +if [ -f "prisma/seed.ts" ]; then + # Add db:seed script to package.json if it doesn't exist + if ! grep -q '"db:seed"' package.json; then + echo "Adding db:seed script to package.json..." + sed -i '' 's/"scripts": {/"scripts": {\n "db:seed": "tsx prisma\/seed.ts",/g' package.json + fi + + # Run seed script + echo "Running seed script..." + npm run db:seed + + if [ $? -ne 0 ]; then + echo -e "${RED}Failed to seed database.${NC}" + exit 1 + else + echo -e "${GREEN}Database seeded successfully.${NC}" + fi +else + echo -e "${YELLOW}No seed script found at prisma/seed.ts.${NC}" + echo "To add test data, create a seed script at prisma/seed.ts." +fi + +echo -e "\n${GREEN}==============================================${NC}" +echo -e "${GREEN} Database setup completed successfully! ${NC}" +echo -e "${GREEN}==============================================${NC}" +echo -e "\nYou can now start your application with: ${BLUE}npm run dev${NC}" +echo -e "Database connection: ${BLUE}postgresql://$DB_USER@localhost:5432/$DB_NAME${NC}" +echo -e "\nTo view database tables: ${BLUE}psql -d $DB_NAME -c '\\dt'${NC}" \ No newline at end of file