Update skills: add website-creator, mql-developer, ecommerce-astro

Changes:
- Add FAL_KEY and GEMINI_API_KEY to .env.example
- Update picture-it to use ~/.config/opencode/.env (unified creds)
- Remove shodh-memory skill (no longer used)
- Remove alphaear-* skills (deprecated)
- Remove thai-frontend-dev skill (replaced by website-creator)
- Remove theme-factory skill
- Add mql-developer skill (MQL5 trading)
- Add ecommerce-astro skill (Astro e-commerce)
- Add website-creator skill (Next.js + Payload CMS)
- Update install script for new skills
This commit is contained in:
2026-04-16 17:40:27 +07:00
parent 5053ccdba2
commit b26c8199a5
562 changed files with 59030 additions and 37600 deletions

View File

@@ -0,0 +1,421 @@
-- =====================================================
-- E-commerce Database Schema for Supabase
-- Astro 6 + React + PaySo Multi-vendor Marketplace
-- =====================================================
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- =====================================================
-- USERS & AUTHENTICATION
-- =====================================================
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL,
name TEXT,
phone TEXT,
role TEXT DEFAULT 'customer' CHECK (role IN ('customer', 'vendor', 'admin')),
avatar_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- VENDOR PROFILES (Multi-vendor support)
-- =====================================================
CREATE TABLE vendor_profiles (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
store_name TEXT NOT NULL,
store_slug TEXT UNIQUE NOT NULL,
store_description TEXT,
store_logo TEXT,
bank_account TEXT,
bank_name TEXT,
payout_status TEXT DEFAULT 'pending' CHECK (payout_status IN ('pending', 'approved', 'rejected')),
total_earnings DECIMAL(12,2) DEFAULT 0,
approved_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- CATEGORIES (Hierarchical)
-- =====================================================
CREATE TABLE categories (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
description TEXT,
image_url TEXT,
parent_id UUID REFERENCES categories(id),
sort_order INT DEFAULT 0,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- PRODUCTS
-- =====================================================
CREATE TABLE products (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
vendor_id UUID REFERENCES vendor_profiles(id) ON DELETE CASCADE,
category_id UUID REFERENCES categories(id),
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
description TEXT,
price DECIMAL(12,2) NOT NULL,
compare_at_price DECIMAL(12,2),
cost_price DECIMAL(12,2),
sku TEXT UNIQUE,
barcode TEXT,
inventory INT DEFAULT 0,
low_stock_threshold INT DEFAULT 5,
track_inventory BOOLEAN DEFAULT TRUE,
allow_backorder BOOLEAN DEFAULT FALSE,
weight DECIMAL(8,2),
images JSONB DEFAULT '[]',
metadata JSONB DEFAULT '{}',
status TEXT DEFAULT 'draft' CHECK (status IN ('draft', 'active', 'archived')),
featured BOOLEAN DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- PRODUCT VARIANTS
-- =====================================================
CREATE TABLE product_variants (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
product_id UUID REFERENCES products(id) ON DELETE CASCADE,
name TEXT NOT NULL,
sku TEXT UNIQUE,
price DECIMAL(12,2),
inventory INT DEFAULT 0,
attributes JSONB DEFAULT '{}',
image_url TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- REVIEWS
-- =====================================================
CREATE TABLE reviews (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
product_id UUID REFERENCES products(id) ON DELETE CASCADE,
user_id UUID REFERENCES users(id),
order_id UUID,
rating INT CHECK (rating >= 1 AND rating <= 5),
title TEXT,
comment TEXT,
images JSONB DEFAULT '[]',
verified_purchase BOOLEAN DEFAULT FALSE,
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'approved', 'rejected')),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- ORDERS
-- =====================================================
CREATE TABLE orders (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
order_number TEXT UNIQUE NOT NULL,
user_id UUID REFERENCES users(id),
vendor_id UUID REFERENCES vendor_profiles(id),
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'confirmed', 'processing', 'shipped', 'delivered', 'cancelled', 'refunded')),
payment_status TEXT DEFAULT 'unpaid' CHECK (payment_status IN ('unpaid', 'paid', 'failed', 'refunded')),
subtotal DECIMAL(12,2) NOT NULL,
tax DECIMAL(12,2) DEFAULT 0,
shipping_cost DECIMAL(12,2) DEFAULT 0,
total DECIMAL(12,2) NOT NULL,
currency TEXT DEFAULT 'THB',
payment_method TEXT,
payment_provider TEXT,
payment_ref TEXT,
shipping_name TEXT,
shipping_phone TEXT,
shipping_address TEXT,
shipping_city TEXT,
shipping_postal TEXT,
shipping_country TEXT DEFAULT 'Thailand',
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- ORDER ITEMS
-- =====================================================
CREATE TABLE order_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
order_id UUID REFERENCES orders(id) ON DELETE CASCADE,
product_id UUID REFERENCES products(id),
variant_id UUID REFERENCES product_variants(id),
vendor_id UUID REFERENCES vendor_profiles(id),
quantity INT NOT NULL,
unit_price DECIMAL(12,2) NOT NULL,
total_price DECIMAL(12,2) NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- PAYMENTS
-- =====================================================
CREATE TABLE payments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
order_id UUID REFERENCES orders(id),
user_id UUID REFERENCES users(id),
provider TEXT NOT NULL,
provider_ref TEXT,
amount DECIMAL(12,2) NOT NULL,
currency TEXT DEFAULT 'THB',
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'completed', 'failed', 'refunded')),
payment_data JSONB,
paid_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- VENDOR PAYOUTS
-- =====================================================
CREATE TABLE vendor_payouts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
vendor_id UUID REFERENCES vendor_profiles(id),
amount DECIMAL(12,2) NOT NULL,
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'processing', 'completed', 'failed')),
bank_account TEXT,
bank_name TEXT,
notes TEXT,
processed_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================================================
-- SHOPPING CARTS
-- =====================================================
CREATE TABLE carts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
user_id UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE cart_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
cart_id UUID REFERENCES carts(id) ON DELETE CASCADE,
product_id UUID REFERENCES products(id),
variant_id UUID REFERENCES product_variants(id),
quantity INT DEFAULT 1,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(cart_id, product_id, variant_id)
);
-- =====================================================
-- INDEXES FOR PERFORMANCE
-- =====================================================
-- Products
CREATE INDEX idx_products_vendor ON products(vendor_id);
CREATE INDEX idx_products_category ON products(category_id);
CREATE INDEX idx_products_slug ON products(slug);
CREATE INDEX idx_products_status ON products(status);
CREATE INDEX idx_products_featured ON products(featured) WHERE featured = TRUE;
CREATE INDEX idx_products_inventory ON products(inventory);
-- Orders
CREATE INDEX idx_orders_user ON orders(user_id);
CREATE INDEX idx_orders_vendor ON orders(vendor_id);
CREATE INDEX idx_orders_number ON orders(order_number);
CREATE INDEX idx_orders_status ON orders(status);
CREATE INDEX idx_orders_payment_status ON orders(payment_status);
CREATE INDEX idx_orders_created ON orders(created_at DESC);
-- Order Items
CREATE INDEX idx_order_items_order ON order_items(order_id);
CREATE INDEX idx_order_items_product ON order_items(product_id);
CREATE INDEX idx_order_items_vendor ON order_items(vendor_id);
-- Reviews
CREATE INDEX idx_reviews_product ON reviews(product_id);
CREATE INDEX idx_reviews_user ON reviews(user_id);
CREATE INDEX idx_reviews_product_rating ON reviews(product_id, rating);
-- Cart
CREATE INDEX idx_cart_items_cart ON cart_items(cart_id);
-- Payments
CREATE INDEX idx_payments_order ON payments(order_id);
CREATE INDEX idx_payments_status ON payments(status);
-- Vendor
CREATE INDEX idx_vendor_profiles_user ON vendor_profiles(user_id);
CREATE INDEX idx_vendor_profiles_slug ON vendor_profiles(store_slug);
-- Categories
CREATE INDEX idx_categories_parent ON categories(parent_id);
-- =====================================================
-- ROW LEVEL SECURITY (RLS)
-- =====================================================
-- Enable RLS on all tables
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE vendor_profiles ENABLE ROW LEVEL SECURITY;
ALTER TABLE categories ENABLE ROW LEVEL SECURITY;
ALTER TABLE products ENABLE ROW LEVEL SECURITY;
ALTER TABLE product_variants ENABLE ROW LEVEL SECURITY;
ALTER TABLE reviews ENABLE ROW LEVEL SECURITY;
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
ALTER TABLE order_items ENABLE ROW LEVEL SECURITY;
ALTER TABLE payments ENABLE ROW LEVEL SECURITY;
ALTER TABLE vendor_payouts ENABLE ROW LEVEL SECURITY;
ALTER TABLE carts ENABLE ROW LEVEL SECURITY;
ALTER TABLE cart_items ENABLE ROW LEVEL SECURITY;
-- Users: Users can read their own profile
CREATE POLICY "Users can view own profile" ON users
FOR SELECT USING (auth.uid() = id);
CREATE POLICY "Users can update own profile" ON users
FOR UPDATE USING (auth.uid() = id);
-- Products: Anyone can view active products
CREATE POLICY "Anyone can view active products" ON products
FOR SELECT USING (status = 'active');
CREATE POLICY "Vendors can manage own products" ON products
FOR ALL USING (
vendor_id IN (
SELECT id FROM vendor_profiles WHERE user_id = auth.uid()
)
);
-- Categories: Anyone can view categories
CREATE POLICY "Anyone can view categories" ON categories
FOR SELECT USING (true);
-- Reviews: Anyone can view approved reviews
CREATE POLICY "Anyone can view approved reviews" ON reviews
FOR SELECT USING (status = 'approved');
-- Orders: Users can view their own orders
CREATE POLICY "Users can view own orders" ON orders
FOR SELECT USING (auth.uid() = user_id);
CREATE POLICY "Vendors can view their orders" ON orders
FOR SELECT USING (
vendor_id IN (
SELECT id FROM vendor_profiles WHERE user_id = auth.uid()
)
);
-- Carts: Users can manage their own cart
CREATE POLICY "Users can manage own cart" ON carts
FOR ALL USING (user_id = auth.uid());
CREATE POLICY "Users can manage own cart items" ON cart_items
FOR ALL USING (
cart_id IN (SELECT id FROM carts WHERE user_id = auth.uid())
);
-- =====================================================
-- FUNCTIONS & TRIGGERS
-- =====================================================
-- Auto-update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Apply to tables with updated_at
CREATE TRIGGER update_users_updated_at
BEFORE UPDATE ON users
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_products_updated_at
BEFORE UPDATE ON products
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_orders_updated_at
BEFORE UPDATE ON orders
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Function to generate order number
CREATE OR REPLACE FUNCTION generate_order_number()
RETURNS TRIGGER AS $$
BEGIN
NEW.order_number = 'ORD-' || TO_CHAR(NOW(), 'YYYYMMDD') || '-' || UPPER(SUBSTRING(NEW.id::text, 1, 8));
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER generate_order_number_trigger
BEFORE INSERT ON orders
FOR EACH ROW EXECUTE FUNCTION generate_order_number();
-- Function to update product inventory on order
CREATE OR REPLACE FUNCTION update_inventory_on_order()
RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' THEN
UPDATE products
SET inventory = inventory - NEW.quantity
WHERE id = NEW.product_id AND track_inventory = TRUE;
ELSIF TG_OP = 'DELETE' THEN
UPDATE products
SET inventory = inventory + OLD.quantity
WHERE id = OLD.product_id AND track_inventory = TRUE;
END IF;
RETURN NULL;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_inventory_trigger
AFTER INSERT OR DELETE ON order_items
FOR EACH ROW EXECUTE FUNCTION update_inventory_on_order();
-- Function to update vendor earnings on payment
CREATE OR REPLACE FUNCTION update_vendor_earnings()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status = 'completed' THEN
UPDATE vendor_profiles
SET total_earnings = total_earnings + NEW.amount
WHERE vendor_id IN (
SELECT vendor_id FROM orders WHERE id = NEW.order_id
);
END IF;
RETURN NEW;
END;
$$ language 'plpgsql';
CREATE TRIGGER update_vendor_earnings_trigger
AFTER UPDATE OF status ON payments
FOR EACH ROW EXECUTE FUNCTION update_vendor_earnings();
-- =====================================================
-- SEED DATA (Optional sample categories)
-- =====================================================
-- Uncomment to add sample categories
/*
INSERT INTO categories (name, slug, description, sort_order) VALUES
('Electronics', 'electronics', 'Electronic devices and accessories', 1),
('Clothing', 'clothing', 'Fashion and apparel', 2),
('Home & Garden', 'home-garden', 'Home improvement and garden', 3),
('Sports', 'sports', 'Sports and outdoor equipment', 4),
('Books', 'books', 'Books and media', 5);
*/