-- E-Commerce Database Schema -- Run this file to create database, user, tables and populate initial data -- Usage: psql -U postgres -h localhost -f schema.sql -- ============================================ -- CREATE DATABASE AND USER -- ============================================ -- Drop existing database and user if they exist (for clean setup) DROP DATABASE IF EXISTS ecommerce_db; DROP USER IF EXISTS ecommerce_user; -- Create database (Windows compatible - uses system defaults) CREATE DATABASE ecommerce_db WITH OWNER = postgres ENCODING = 'UTF8' TABLESPACE = pg_default CONNECTION LIMIT = -1; -- Create user with password CREATE USER ecommerce_user WITH PASSWORD 'password'; -- Grant all privileges on database to user GRANT ALL PRIVILEGES ON DATABASE ecommerce_db TO ecommerce_user; -- Connect to the ecommerce_db database \c ecommerce_db -- Grant schema privileges including CREATE permission GRANT ALL ON SCHEMA public TO ecommerce_user; GRANT CREATE ON SCHEMA public TO ecommerce_user; -- Make ecommerce_user owner of public schema to avoid permission issues ALTER SCHEMA public OWNER TO ecommerce_user; -- Grant privileges on all current and future tables GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ecommerce_user; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ecommerce_user; GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO ecommerce_user; -- Grant privileges on future objects ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ecommerce_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ecommerce_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO ecommerce_user; -- ============================================ -- CREATE TABLES -- ============================================ -- Category Table CREATE TABLE category ( id SERIAL PRIMARY KEY, name VARCHAR(100) UNIQUE NOT NULL, slug VARCHAR(100) UNIQUE NOT NULL, description TEXT, image VARCHAR(500) ); -- Model Table (e.g., New Balance 9060) CREATE TABLE model ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, category_id INTEGER NOT NULL, brand VARCHAR(100) NOT NULL, base_price DECIMAL(10, 2), sizes JSONB, description TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE CASCADE, UNIQUE (name, category_id, brand) ); CREATE INDEX idx_model_category ON model(category_id); CREATE INDEX idx_model_brand ON model(brand); -- User Table CREATE TABLE "user" ( id SERIAL PRIMARY KEY, email VARCHAR(255) UNIQUE NOT NULL, hashed_password VARCHAR(255) NOT NULL, full_name VARCHAR(255), phone VARCHAR(20), address TEXT, city VARCHAR(100), postal_code VARCHAR(20), country VARCHAR(100), is_active BOOLEAN DEFAULT TRUE, is_admin BOOLEAN DEFAULT FALSE, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- Product Table CREATE TABLE product ( id SERIAL PRIMARY KEY, name VARCHAR(255) NOT NULL, slug VARCHAR(255) UNIQUE, description TEXT, price DECIMAL(10, 2) NOT NULL, discount_price DECIMAL(10, 2), category_id INTEGER NOT NULL, model_id INTEGER, gender VARCHAR(50), brand VARCHAR(100), sizes JSONB, colors JSONB, stock INTEGER DEFAULT 0, images JSONB, is_featured BOOLEAN DEFAULT FALSE, is_on_sale BOOLEAN DEFAULT FALSE, override_price DECIMAL(10, 2), override_sizes JSONB, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES category(id) ON DELETE CASCADE, FOREIGN KEY (model_id) REFERENCES model(id) ON DELETE SET NULL ); CREATE INDEX idx_product_slug ON product(slug); CREATE INDEX idx_product_model ON product(model_id); -- Cart Table CREATE TABLE cart ( id SERIAL PRIMARY KEY, user_id INTEGER UNIQUE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE ); -- Cart Item Table CREATE TABLE cart_item ( id SERIAL PRIMARY KEY, cart_id INTEGER NOT NULL, product_id INTEGER NOT NULL, quantity INTEGER DEFAULT 1, size VARCHAR(50), color VARCHAR(50), FOREIGN KEY (cart_id) REFERENCES cart(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES product(id) ON DELETE CASCADE ); -- Order Table CREATE TABLE "order" ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL, order_number VARCHAR(100) UNIQUE NOT NULL, status VARCHAR(50) DEFAULT 'pending', total_amount DECIMAL(10, 2) NOT NULL, shipping_address TEXT, shipping_city VARCHAR(100), shipping_postal_code VARCHAR(20), shipping_country VARCHAR(100), created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE ); -- Order Item Table CREATE TABLE order_item ( id SERIAL PRIMARY KEY, order_id INTEGER NOT NULL, product_id INTEGER NOT NULL, quantity INTEGER NOT NULL, price DECIMAL(10, 2) NOT NULL, size VARCHAR(50), color VARCHAR(50), FOREIGN KEY (order_id) REFERENCES "order"(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES product(id) ON DELETE CASCADE ); -- Wishlist (User-Product Association) CREATE TABLE user_wishlist ( user_id INTEGER NOT NULL, product_id INTEGER NOT NULL, PRIMARY KEY (user_id, product_id), FOREIGN KEY (user_id) REFERENCES "user"(id) ON DELETE CASCADE, FOREIGN KEY (product_id) REFERENCES product(id) ON DELETE CASCADE ); -- Contact Message Table CREATE TABLE contact_message ( id SERIAL PRIMARY KEY, name VARCHAR(255), email VARCHAR(255), subject VARCHAR(255), message TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); -- ============================================ -- CREATE INDEXES -- ============================================ CREATE INDEX idx_user_email ON "user"(email); CREATE INDEX idx_product_name ON product(name); CREATE INDEX idx_product_category ON product(category_id); CREATE INDEX idx_product_is_featured ON product(is_featured); CREATE INDEX idx_product_is_on_sale ON product(is_on_sale); CREATE INDEX idx_order_user ON "order"(user_id); CREATE INDEX idx_order_created_at ON "order"(created_at); CREATE INDEX idx_cart_item_cart ON cart_item(cart_id); CREATE INDEX idx_order_item_order ON order_item(order_id); -- ============================================ -- POPULATE INITIAL DATA -- ============================================ -- Insert Categories INSERT INTO category (name, slug, description) VALUES ('Shoes', 'shoes', 'Footwear for all occasions'), ('Shirts', 'shirts', 'T-shirts and casual tops'), ('Pants', 'pants', 'Jeans and trousers'), ('Hats', 'hats', 'Caps and beanies'), ('Accessories', 'accessories', 'Bags, belts, and more'); -- Insert Sample Products (Shoes - Featured) INSERT INTO product (name, description, price, discount_price, category_id, gender, brand, sizes, colors, stock, images, is_featured, is_on_sale) VALUES ( 'Premium Running Shoes', 'High-performance running shoes with advanced cushioning technology', 129.99, 99.99, 1, 'men', 'Nike', '["7", "8", "9", "10", "11", "12", "13"]', '["Black", "White", "Blue"]', 50, '["https://via.placeholder.com/300x300?text=Nike+Running"]', TRUE, TRUE ), ( 'Women Athletic Sneakers', 'Comfortable athletic sneakers for everyday wear', 99.99, NULL, 1, 'women', 'Adidas', '["5", "6", "7", "8", "9", "10"]', '["Pink", "White", "Purple"]', 45, '["https://via.placeholder.com/300x300?text=Adidas+Sneakers"]', TRUE, FALSE ), ( 'Basketball High Tops', 'Professional basketball shoes with ankle support', 149.99, NULL, 1, 'men', 'Jordan', '["8", "9", "10", "11", "12", "13"]', '["Red", "Black", "Gold"]', 30, '["https://via.placeholder.com/300x300?text=Jordan+High"]', TRUE, FALSE ), ( 'Casual Leather Loafers', 'Classic leather loafers for formal occasions', 139.99, 109.99, 1, 'men', 'Cole Haan', '["7", "8", "9", "10", "11", "12"]', '["Brown", "Black"]', 25, '["https://via.placeholder.com/300x300?text=Cole+Haan+Loafers"]', TRUE, TRUE ), ( 'Hiking Boot Pro', 'Durable hiking boots with waterproof technology', 179.99, 149.99, 1, 'men', 'Salomon', '["8", "9", "10", "11", "12", "13"]', '["Brown", "Gray", "Black"]', 35, '["https://via.placeholder.com/300x300?text=Salomon+Hiking"]', TRUE, TRUE ); -- Insert Sample Products (Clothing) INSERT INTO product (name, description, price, discount_price, category_id, gender, brand, sizes, colors, stock, images, is_featured, is_on_sale) VALUES ( 'Classic Cotton T-Shirt', 'Comfortable everyday cotton t-shirt', 29.99, NULL, 2, 'men', 'Gap', '["S", "M", "L", "XL", "XXL"]', '["Red", "Blue", "White", "Black"]', 100, '["https://via.placeholder.com/300x300?text=Cotton+Tee"]', FALSE, FALSE ), ( 'Silk Blouse', 'Elegant silk blouse for professional settings', 89.99, 69.99, 2, 'women', 'Hugo Boss', '["XS", "S", "M", "L", "XL"]', '["White", "Black", "Navy"]', 40, '["https://via.placeholder.com/300x300?text=Silk+Blouse"]', FALSE, TRUE ), ( 'Slim Fit Jeans', 'Modern slim fit jeans with stretch fabric', 79.99, 59.99, 3, 'men', 'Levi''s', '["28", "30", "32", "34", "36", "38"]', '["Dark Blue", "Light Blue", "Black"]', 60, '["https://via.placeholder.com/300x300?text=Slim+Jeans"]', FALSE, TRUE ), ( 'Yoga Leggings', 'High-waist yoga leggings with moisture-wicking', 89.99, NULL, 3, 'women', 'Lululemon', '["XS", "S", "M", "L", "XL"]', '["Black", "Navy", "Purple", "Gray"]', 55, '["https://via.placeholder.com/300x300?text=Yoga+Leggings"]', TRUE, FALSE ); -- Insert Sample Users (for testing) INSERT INTO "user" (email, hashed_password, full_name, phone, address, city, postal_code, country, is_active, is_admin) VALUES ( 'admin@example.com', '$2b$12$jNWZdZNMbKEkOjA8Gq3ZUOTml23zfqhFDPJ8AlZQ51WhUyi04AH7C', -- password: password123 'Admin User', '1234567890', '123 Admin Street', 'New York', '10001', 'USA', TRUE, TRUE ), ( 'user@example.com', '$2b$12$jNWZdZNMbKEkOjA8Gq3ZUOTml23zfqhFDPJ8AlZQ51WhUyi04AH7C', -- password: password123 'John Doe', '1234567890', '123 Main Street', 'New York', '10001', 'USA', TRUE, FALSE ), ( 'jane@example.com', '$2b$12$jNWZdZNMbKEkOjA8Gq3ZUOTml23zfqhFDPJ8AlZQ51WhUyi04AH7C', -- password: password123 'Jane Smith', '9876543210', '456 Oak Avenue', 'Los Angeles', '90001', 'USA', TRUE, FALSE ); -- Create carts for users INSERT INTO cart (user_id) VALUES (1), (2), (3); -- ============================================ -- SET PERMISSIONS (After all tables are created) -- ============================================ -- Grant all privileges on all tables, sequences, and functions to the user GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ecommerce_user; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ecommerce_user; GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA public TO ecommerce_user; -- Ensure future objects also get permissions ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ecommerce_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ecommerce_user; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON FUNCTIONS TO ecommerce_user; -- ============================================ -- COMPLETE -- ============================================ -- Schema created successfully! -- -- Database: ecommerce_db -- User: ecommerce_user -- Password: password -- Host: localhost:5432 -- -- Connection string for .env file: -- DATABASE_URL=postgresql://ecommerce_user:password@localhost:5432/ecommerce_db -- -- Tables created: 9 -- - category, user, product, cart, cart_item -- - order, order_item, user_wishlist, contact_message -- -- Demo accounts: -- - ADMIN: admin@example.com / password123 (is_admin: true) -- - USER: user@example.com / password123 -- - USER: jane@example.com / password123 -- -- Sample data: 5 categories, 9 products, 3 users -- -- Next steps: -- 1. Update backend/.env with: DATABASE_URL=postgresql://ecommerce_user:password@localhost:5432/ecommerce_db -- 2. Run: python backend/app/main.py -- 3. Login as admin to create/edit/delete products