-- Dating App Database Schema -- PostgreSQL 15+ Compatible -- Run this script to create the database and all tables -- ============================================================================ -- DATABASE CREATION -- ============================================================================ -- Create the database user (if doesn't exist) DO $do$ BEGIN CREATE ROLE dating_app_user WITH LOGIN PASSWORD 'Aa123456'; EXCEPTION WHEN DUPLICATE_OBJECT THEN RAISE NOTICE 'Role dating_app_user already exists'; END $do$; -- Grant all privileges on database to dating_app_user GRANT ALL PRIVILEGES ON DATABASE dating_app TO dating_app_user; -- Grant all privileges on all tables in public schema to dating_app_user GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO dating_app_user; -- Grant all privileges on all sequences in public schema to dating_app_user GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO dating_app_user; -- Grant all privileges on schema itself GRANT ALL PRIVILEGES ON SCHEMA public TO dating_app_user; -- Set default privileges for future tables ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO dating_app_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO dating_app_user; -- ============================================================================ -- TABLE: USERS -- ============================================================================ -- Stores user account information CREATE TABLE IF NOT EXISTS users ( id SERIAL PRIMARY KEY, username VARCHAR(255) UNIQUE NOT NULL, email VARCHAR(255) UNIQUE NOT NULL, hashed_password VARCHAR(255) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_users_email ON users(email); CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); -- ============================================================================ -- TABLE: PROFILES -- ============================================================================ -- Stores user profile information CREATE TABLE IF NOT EXISTS profiles ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL UNIQUE, display_name VARCHAR(255) NOT NULL, age INTEGER NOT NULL, gender VARCHAR(50) NOT NULL, location VARCHAR(255) NOT NULL, bio TEXT, interests JSONB DEFAULT '[]', created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_profiles_user_id ON profiles(user_id); -- ============================================================================ -- TABLE: PHOTOS -- ============================================================================ -- Stores user profile photos CREATE TABLE IF NOT EXISTS photos ( id SERIAL PRIMARY KEY, profile_id INTEGER NOT NULL, file_path VARCHAR(255) NOT NULL, display_order INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (profile_id) REFERENCES profiles(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_photos_profile_id ON photos(profile_id); -- ============================================================================ -- TABLE: LIKES -- ============================================================================ -- Tracks which users like which other users CREATE TABLE IF NOT EXISTS likes ( id SERIAL PRIMARY KEY, liker_id INTEGER NOT NULL, liked_id INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(liker_id, liked_id), FOREIGN KEY (liker_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (liked_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_likes_liker_id ON likes(liker_id); CREATE INDEX IF NOT EXISTS idx_likes_liked_id ON likes(liked_id); -- ============================================================================ -- TABLE: CONVERSATIONS -- ============================================================================ -- Stores 1:1 chat conversations between users CREATE TABLE IF NOT EXISTS conversations ( id SERIAL PRIMARY KEY, user_id_1 INTEGER NOT NULL, user_id_2 INTEGER NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, UNIQUE(user_id_1, user_id_2), FOREIGN KEY (user_id_1) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (user_id_2) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_conversations_users ON conversations(user_id_1, user_id_2); -- ============================================================================ -- TABLE: MESSAGES -- ============================================================================ -- Stores individual messages in conversations CREATE TABLE IF NOT EXISTS messages ( id SERIAL PRIMARY KEY, conversation_id INTEGER NOT NULL, sender_id INTEGER NOT NULL, content TEXT NOT NULL, read_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (conversation_id) REFERENCES conversations(id) ON DELETE CASCADE, FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_messages_conversation_id ON messages(conversation_id); CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at); -- ============================================================================ -- SAMPLE DATA (Optional - Uncomment to insert test users) -- ============================================================================ -- Test user 1: Alice (Password hash for 'password123') INSERT INTO users (username, email, hashed_password) VALUES ('alice', 'alice@example.com', '$2b$12$u9bdKguk7ROP404cOx9FIuyKZoc3DK6el3y5muR8ayUnHG1bJewh2') ON CONFLICT (email) DO NOTHING; -- Test user 2: Bob INSERT INTO users (username, email, hashed_password) VALUES ('bob', 'bob@example.com', '$2b$12$u9bdKguk7ROP404cOx9FIuyKZoc3DK6el3y5muR8ayUnHG1bJewh2') ON CONFLICT (email) DO NOTHING; -- Test user 3: Charlie INSERT INTO users (username, email, hashed_password) VALUES ('charlie', 'charlie@example.com', '$2b$12$u9bdKguk7ROP404cOx9FIuyKZoc3DK6el3y5muR8ayUnHG1bJewh2') ON CONFLICT (email) DO NOTHING; -- Test user 4: Diana INSERT INTO users (username, email, hashed_password) VALUES ('diana', 'diana@example.com', '$2b$12$u9bdKguk7ROP404cOx9FIuyKZoc3DK6el3y5muR8ayUnHG1bJewh2') ON CONFLICT (email) DO NOTHING; -- ============================================================================ -- SAMPLE PROFILES (Optional - Uncomment to create test profiles) -- ============================================================================ -- Alice's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'alice@example.com'), 'Alice', 28, 'Female', 'San Francisco, CA', 'Love hiking and coffee. Looking for genuine connection.', '["hiking", "coffee", "reading", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- Bob's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'bob@example.com'), 'Bob', 30, 'Male', 'San Francisco, CA', 'Software engineer who enjoys cooking and photography.', '["cooking", "photography", "gaming", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- Charlie's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'charlie@example.com'), 'Charlie', 27, 'Male', 'Los Angeles, CA', 'Designer and musician. Love live music and good conversation.', '["music", "design", "art", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- Diana's profile INSERT INTO profiles (user_id, display_name, age, gender, location, bio, interests) VALUES ( (SELECT id FROM users WHERE email = 'diana@example.com'), 'Diana', 26, 'Female', 'Los Angeles, CA', 'Yoga instructor and nature lover. Adventure seeker!', '["yoga", "hiking", "nature", "travel"]' ) ON CONFLICT (user_id) DO NOTHING; -- ============================================================================ -- VERIFICATION QUERIES -- ============================================================================ -- Run these queries to verify everything was created correctly: -- SELECT COUNT(*) as user_count FROM users; -- SELECT COUNT(*) as profile_count FROM profiles; -- SELECT COUNT(*) as photo_count FROM photos; -- SELECT COUNT(*) as like_count FROM likes; -- SELECT COUNT(*) as conversation_count FROM conversations; -- SELECT COUNT(*) as message_count FROM messages; -- ============================================================================ -- NOTES -- ============================================================================ -- -- Password hashes used in sample data: -- - Hash: $2b$12$u9bdKguk7ROP404cOx9FIuyKZoc3DK6el3y5muR8ayUnHG1bJewh2 -- - Password: 'password123' -- -- To generate your own bcrypt hash, use Python: -- from passlib.context import CryptContext -- pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto") -- hash = pwd_context.hash("your_password_here") -- -- IMPORTANT BEFORE PRODUCTION: -- 1. Change all password hashes to actual user passwords -- 2. Update email addresses to real users -- 3. Consider using proper user import/registration instead of direct inserts -- 4. Remove sample data if not needed -- -- DATABASE CONNECTION INFO: -- Database: dating_app -- Host: localhost (or your PostgreSQL host) -- Port: 5432 (default) -- User: postgres (or your database user) -- Password: (set when installing PostgreSQL) -- -- ============================================================================ -- ============================================================================ -- MIGRATIONS (Run these if tables already exist and need updates) -- ============================================================================ -- Uncomment and run the following lines to update existing database: -- Add username column to users table if it doesn't already exist: -- ALTER TABLE users ADD COLUMN username VARCHAR(255) UNIQUE; -- Add index on username for faster lookups: -- CREATE INDEX IF NOT EXISTS idx_users_username ON users(username); -- Update dating_app_user permissions: -- GRANT ALL PRIVILEGES ON DATABASE dating_app TO dating_app_user; -- GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO dating_app_user; -- GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO dating_app_user; -- GRANT ALL PRIVILEGES ON SCHEMA public TO dating_app_user;