| 1 |
-- Shared schema for both PHP and Go versions
|
| 2 |
|
| 3 |
CREATE TABLE IF NOT EXISTS products (
|
| 4 |
id SERIAL PRIMARY KEY,
|
| 5 |
slug VARCHAR(255) UNIQUE NOT NULL,
|
| 6 |
name VARCHAR(255) NOT NULL,
|
| 7 |
description TEXT,
|
| 8 |
price INTEGER NOT NULL, -- cents
|
| 9 |
image_url VARCHAR(500),
|
| 10 |
active BOOLEAN DEFAULT true,
|
| 11 |
printful_product_id VARCHAR(100),
|
| 12 |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
| 13 |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
| 14 |
);
|
| 15 |
|
| 16 |
CREATE TABLE IF NOT EXISTS variants (
|
| 17 |
id SERIAL PRIMARY KEY,
|
| 18 |
product_id INTEGER REFERENCES products(id) ON DELETE CASCADE,
|
| 19 |
size VARCHAR(50) NOT NULL,
|
| 20 |
printful_variant_id VARCHAR(100),
|
| 21 |
stock INTEGER DEFAULT 0,
|
| 22 |
sort_order INTEGER DEFAULT 0
|
| 23 |
);
|
| 24 |
|
| 25 |
CREATE TABLE IF NOT EXISTS orders (
|
| 26 |
id SERIAL PRIMARY KEY,
|
| 27 |
stripe_session_id VARCHAR(255) UNIQUE,
|
| 28 |
stripe_payment_intent VARCHAR(255),
|
| 29 |
email VARCHAR(255),
|
| 30 |
status VARCHAR(50) DEFAULT 'pending', -- pending, paid, fulfilled, shipped
|
| 31 |
total INTEGER NOT NULL, -- cents
|
| 32 |
shipping_name VARCHAR(255),
|
| 33 |
shipping_address TEXT,
|
| 34 |
printful_order_id VARCHAR(100),
|
| 35 |
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
| 36 |
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
| 37 |
);
|
| 38 |
|
| 39 |
CREATE TABLE IF NOT EXISTS order_items (
|
| 40 |
id SERIAL PRIMARY KEY,
|
| 41 |
order_id INTEGER REFERENCES orders(id) ON DELETE CASCADE,
|
| 42 |
product_id INTEGER REFERENCES products(id),
|
| 43 |
variant_id INTEGER REFERENCES variants(id),
|
| 44 |
quantity INTEGER NOT NULL,
|
| 45 |
price INTEGER NOT NULL -- cents at time of purchase
|
| 46 |
);
|
| 47 |
|
| 48 |
CREATE TABLE IF NOT EXISTS product_images (
|
| 49 |
id SERIAL PRIMARY KEY,
|
| 50 |
product_id INTEGER REFERENCES products(id) ON DELETE CASCADE,
|
| 51 |
image_url VARCHAR(500) NOT NULL,
|
| 52 |
sort_order INTEGER DEFAULT 0
|
| 53 |
);
|
| 54 |
|
| 55 |
-- Sample data for testing
|
| 56 |
INSERT INTO products (slug, name, description, price, image_url) VALUES
|
| 57 |
('linux-tux-tee', 'Linux Tux Tee', 'Classic penguin on a comfy tee. 100% cotton.', 2500, 'https://placehold.co/600x600/1a1a1a/3b82f6?text=Linux+Tux+Tee'),
|
| 58 |
('btw-i-use-arch-hoodie', 'BTW I Use Arch Hoodie', 'Let everyone know. Heavyweight fleece.', 4500, 'https://placehold.co/600x600/1a1a1a/3b82f6?text=Arch+Hoodie'),
|
| 59 |
('foss-freedom-cap', 'FOSS Freedom Cap', 'Embroidered dad hat for free software enjoyers.', 2000, 'https://placehold.co/600x600/1a1a1a/3b82f6?text=FOSS+Cap')
|
| 60 |
ON CONFLICT (slug) DO UPDATE SET image_url = EXCLUDED.image_url;
|
| 61 |
|
| 62 |
INSERT INTO variants (product_id, size, sort_order) VALUES
|
| 63 |
(1, 'S', 1), (1, 'M', 2), (1, 'L', 3), (1, 'XL', 4),
|
| 64 |
(2, 'S', 1), (2, 'M', 2), (2, 'L', 3), (2, 'XL', 4), (2, 'XXL', 5),
|
| 65 |
(3, 'One Size', 1)
|
| 66 |
ON CONFLICT DO NOTHING;
|
| 67 |
|
| 68 |
CREATE INDEX IF NOT EXISTS idx_products_slug ON products(slug);
|
| 69 |
CREATE INDEX IF NOT EXISTS idx_products_active ON products(active);
|
| 70 |
CREATE INDEX IF NOT EXISTS idx_variants_product ON variants(product_id);
|
| 71 |
CREATE INDEX IF NOT EXISTS idx_orders_stripe ON orders(stripe_session_id);
|