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:
620
skills/ecommerce-astro/SKILL.md
Normal file
620
skills/ecommerce-astro/SKILL.md
Normal file
@@ -0,0 +1,620 @@
|
||||
---
|
||||
name: ecommerce-astro
|
||||
description: |
|
||||
Full-featured e-commerce site builder with Astro 6, React, Supabase backend.
|
||||
Creates online stores with optional multi-vendor marketplace, Thai language support,
|
||||
inventory tracking, and order management. Use when: building e-commerce sites,
|
||||
marketplaces, online stores, or Thai e-commerce stores.
|
||||
---
|
||||
|
||||
# E-commerce Astro - E-commerce Site Builder
|
||||
|
||||
**Category:** `fullstack`
|
||||
**Tech Stack:** Astro 6 + React + Supabase + Tailwind v4
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Purpose
|
||||
|
||||
Create complete e-commerce websites with these core features:
|
||||
|
||||
- ✅ **Product catalog** - Browse, filter, search products from Supabase
|
||||
- ✅ **Inventory management** - Stock tracking with low-stock alerts
|
||||
- ✅ **Order management** - Cart, checkout, order tracking, status updates
|
||||
- ✅ **Thai language support** - Bilingual Thai/English with i18n routing
|
||||
- ✅ **Review system** - Verified purchase reviews with ratings
|
||||
- ✅ **Responsive design** - Mobile-first with React components
|
||||
|
||||
### Optional Features (Enable/Disable)
|
||||
|
||||
| Feature | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `multi_vendor` | `false` | Multi-vendor marketplace with vendor dashboards |
|
||||
| `payso_payment` | `false` | PaySo Thai payment gateway (stub for now) |
|
||||
| `vendor_payouts` | `false` | Automated payout tracking (requires multi_vendor) |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
```bash
|
||||
# Generate e-commerce site (interactive mode)
|
||||
python3 skills/ecommerce-astro/scripts/create_ecommerce.py \
|
||||
--name "My Store" \
|
||||
--output "./my-store"
|
||||
|
||||
# With options
|
||||
python3 skills/ecommerce-astro/scripts/create_ecommerce.py \
|
||||
--name "My Store" \
|
||||
--output "./my-store" \
|
||||
--multi-vendor true \
|
||||
--languages "th"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Pre-Flight Questions
|
||||
|
||||
Before running the script, gather these details:
|
||||
|
||||
1. **Store Name:** (e.g., "Deal Plus Tech Store")
|
||||
2. **Store Slug:** (e.g., "deal-plus-tech-store")
|
||||
3. **Supabase Project URL:** From supabase.com dashboard
|
||||
4. **Supabase Anon Key:** Public key for client-side
|
||||
5. **Supabase Service Role Key:** For admin/server-side operations
|
||||
6. **Multi-Vendor Mode:** Enable/disable vendor system (true/false)
|
||||
7. **Languages:** Thai only (th), English only (en), or bilingual (th,en)
|
||||
|
||||
---
|
||||
|
||||
## 📁 Generated Project Structure (Base)
|
||||
|
||||
```
|
||||
store-name/
|
||||
├── astro.config.mjs
|
||||
├── package.json
|
||||
├── Dockerfile
|
||||
├── docker-compose.yml
|
||||
├── .env.example
|
||||
├── .gitignore
|
||||
│
|
||||
├── supabase/
|
||||
│ └── migrations/
|
||||
│ └── 001_initial_schema.sql
|
||||
│
|
||||
├── src/
|
||||
│ ├── components/
|
||||
│ │ ├── cart/
|
||||
│ │ │ ├── CartBadge.tsx # Floating cart button
|
||||
│ │ │ ├── CartButton.tsx # Header cart icon
|
||||
│ │ │ ├── CartDrawer.tsx # Slide-out cart panel
|
||||
│ │ │ ├── CartItems.tsx # Cart item list
|
||||
│ │ │ └── CartSummary.tsx # Price breakdown
|
||||
│ │ ├── checkout/
|
||||
│ │ │ └── CheckoutForm.tsx # Checkout form
|
||||
│ │ ├── product/
|
||||
│ │ │ ├── ProductCard.astro # Product grid card
|
||||
│ │ │ ├── ProductFilters.tsx # Category/price filters
|
||||
│ │ │ ├── ProductGallery.tsx # Image gallery
|
||||
│ │ │ ├── ProductVariants.tsx # Size/color variants
|
||||
│ │ │ └── StockBadge.tsx # Inventory status
|
||||
│ │ ├── review/
|
||||
│ │ │ ├── ReviewList.tsx # Product reviews
|
||||
│ │ │ └── StarRating.tsx # Star rating display
|
||||
│ │ └── layout/
|
||||
│ │ ├── Header.astro # Site header
|
||||
│ │ └── Footer.astro # Site footer
|
||||
│ │
|
||||
│ ├── layouts/
|
||||
│ │ └── Layout.astro # Base layout
|
||||
│ │
|
||||
│ ├── lib/
|
||||
│ │ ├── supabase.ts # Supabase client (SSR-safe)
|
||||
│ │ ├── auth.ts # JWT auth helpers
|
||||
│ │ ├── utils.ts # Utility functions
|
||||
│ │ └── types.ts # TypeScript types
|
||||
│ │
|
||||
│ ├── stores/
|
||||
│ │ ├── cart.ts # Zustand cart (SSR-safe)
|
||||
│ │ ├── auth.ts # Auth state
|
||||
│ │ └── vendor.ts # Vendor state (if multi_vendor)
|
||||
│ │
|
||||
│ ├── pages/
|
||||
│ │ ├── index.astro # Homepage
|
||||
│ │ ├── products/
|
||||
│ │ │ ├── index.astro # Product listing
|
||||
│ │ │ └── [slug].astro # Product detail
|
||||
│ │ ├── cart.astro # Full cart page
|
||||
│ │ ├── checkout.astro # Checkout page
|
||||
│ │ ├── search.astro # Search page
|
||||
│ │ ├── auth/
|
||||
│ │ │ ├── login.astro # Login page
|
||||
│ │ │ └── register.astro # Register page
|
||||
│ │ ├── account/
|
||||
│ │ │ ├── index.astro # Account dashboard
|
||||
│ │ │ └── orders/
|
||||
│ │ │ ├── index.astro # Order history
|
||||
│ │ │ └── [id].astro # Order detail
|
||||
│ │ ├── vendor/ # Only if multi_vendor=true
|
||||
│ │ │ ├── dashboard.astro # Vendor dashboard
|
||||
│ │ │ ├── products/
|
||||
│ │ │ ├── orders.astro
|
||||
│ │ │ └── settings.astro
|
||||
│ │ ├── admin/ # Only if multi_vendor=true
|
||||
│ │ │ ├── dashboard.astro
|
||||
│ │ │ ├── vendors.astro
|
||||
│ │ │ ├── users.astro
|
||||
│ │ │ ├── orders.astro
|
||||
│ │ │ └── categories.astro
|
||||
│ │ └── api/
|
||||
│ │ ├── auth/
|
||||
│ │ ├── products/
|
||||
│ │ ├── orders/
|
||||
│ │ └── payments/
|
||||
│ │
|
||||
│ ├── i18n/
|
||||
│ │ ├── th.json # Thai translations
|
||||
│ │ └── en.json # English translations
|
||||
│ │
|
||||
│ └── styles/
|
||||
│ └── global.css # Global styles
|
||||
│
|
||||
└── public/
|
||||
└── images/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ Database Schema (Supabase PostgreSQL)
|
||||
|
||||
The migration creates these tables. Schema can be customized per project.
|
||||
|
||||
### Core Tables (Always Included)
|
||||
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `users` | Customer/admin accounts (id, email, password_hash, name, role, avatar_url) |
|
||||
| `categories` | Product categories (hierarchical with parent_id) |
|
||||
| `products` | Product catalog (id, vendor_id, category_id, name, slug, description, price, images JSONB, inventory, status, track_inventory, featured) |
|
||||
| `reviews` | Product reviews (product_id, user_id, rating, comment, status) |
|
||||
| `orders` | Customer orders (id, order_number, user_id, status, payment_status, total, shipping_address JSONB) |
|
||||
| `order_items` | Line items per order (order_id, product_id, quantity, unit_price) |
|
||||
|
||||
### Multi-Vendor Tables (Only if multi_vendor=true)
|
||||
|
||||
| Table | Purpose |
|
||||
|-------|---------|
|
||||
| `vendor_profiles` | Store info (user_id, store_name, store_slug, store_description, status) |
|
||||
| `product_variants` | Size/color variants (product_id, name, sku, price, inventory) |
|
||||
|
||||
### Indexes
|
||||
|
||||
```sql
|
||||
-- Core indexes
|
||||
CREATE INDEX idx_products_category ON products(category_id);
|
||||
CREATE INDEX idx_products_vendor ON products(vendor_id);
|
||||
CREATE INDEX idx_products_slug ON products(slug);
|
||||
CREATE INDEX idx_products_status ON products(status);
|
||||
CREATE INDEX idx_orders_user ON orders(user_id);
|
||||
CREATE INDEX idx_orders_status ON orders(status);
|
||||
CREATE INDEX idx_reviews_product ON reviews(product_id);
|
||||
```
|
||||
|
||||
### Row Level Security (RLS)
|
||||
|
||||
```sql
|
||||
-- Products: Public read, vendor write own
|
||||
-- Orders: User read own, vendor read own orders
|
||||
-- Vendors: Admin manages vendor_profiles
|
||||
```
|
||||
|
||||
### Key Lessons Learned
|
||||
|
||||
1. **Images stored as JSONB** - Parse with `typeof images === 'string' ? JSON.parse(images) : images`
|
||||
2. **Use service_role key for SSR** - Anonymous key blocked by RLS during server-side rendering
|
||||
3. **Format:** `Authorization: Bearer {key}` header for Supabase REST API
|
||||
|
||||
---
|
||||
|
||||
## 💳 Payment Integration
|
||||
|
||||
Payment is stubbed by default. To enable real payments:
|
||||
|
||||
1. Add PaySo credentials to `.env`
|
||||
2. Create `/api/payments/create.ts` endpoint
|
||||
3. Create `/api/webhooks/payso.ts` handler
|
||||
4. Update `checkout.astro` to call payment API
|
||||
|
||||
### Stub Implementation (Default)
|
||||
|
||||
```typescript
|
||||
// lib/payso.ts (stub)
|
||||
export async function createPayment(order: Order) {
|
||||
// TODO: Implement PaySo integration
|
||||
console.log('Payment stub for order:', order.id);
|
||||
return { success: true, paymentUrl: '/checkout/success' };
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Authentication
|
||||
|
||||
### User Roles
|
||||
|
||||
| Role | Permissions |
|
||||
|------|-------------|
|
||||
| `customer` | Browse, cart, checkout, orders |
|
||||
| `vendor` | Products, orders (requires multi_vendor=true) |
|
||||
| `admin` | All management (requires multi_vendor=true) |
|
||||
|
||||
### Auth Flow
|
||||
|
||||
1. Register with email/password
|
||||
2. Login → JWT token stored in httpOnly cookie
|
||||
3. Protected routes check session cookie
|
||||
4. Role-based access for vendor/admin pages
|
||||
|
||||
### SSR Authentication
|
||||
|
||||
```typescript
|
||||
// Check auth in Astro pages
|
||||
const token = Astro.cookies.get('session')?.value;
|
||||
if (!token) return Astro.redirect('/login');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛒 Cart & Checkout
|
||||
|
||||
### Cart Features
|
||||
|
||||
- **Floating Cart Button** - Fixed position bottom-right, blue circular button
|
||||
- **Cart Drawer** - Slide-out panel with HeadlessUI
|
||||
- **Persistent** - Zustand with localStorage (SSR-safe with getStorage)
|
||||
- **Guest cart** - localStorage only
|
||||
- **Logged-in cart** - Synced to database (optional)
|
||||
|
||||
### Cart Store (SSR-Safe)
|
||||
|
||||
```typescript
|
||||
// stores/cart.ts
|
||||
export const useCartStore = create<CartStore>()(
|
||||
persist(
|
||||
(set, get) => ({ ... }),
|
||||
{
|
||||
name: 'cart-storage',
|
||||
partialize: (state) => ({ items: state.items }),
|
||||
getStorage: () => {
|
||||
if (typeof window === 'undefined') {
|
||||
return { getItem: () => null, setItem: () => {}, removeItem: () => {} };
|
||||
}
|
||||
return localStorage;
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
```
|
||||
|
||||
### Checkout Flow
|
||||
|
||||
1. Cart Review → 2. Shipping Info → 3. Payment → 4. Confirmation
|
||||
|
||||
---
|
||||
|
||||
## 📦 Vendor Dashboard (Only if multi_vendor=true)
|
||||
|
||||
When `multi_vendor=true`, these pages are generated:
|
||||
|
||||
### Vendor Features
|
||||
|
||||
- **Dashboard** - Stats (products, orders, sales)
|
||||
- **Products** - Add/edit/archive products
|
||||
- **Orders** - View and manage orders
|
||||
- **Settings** - Store profile
|
||||
|
||||
### Vendor Onboarding Flow
|
||||
|
||||
1. Register as customer
|
||||
2. Apply for vendor status (`/vendors/apply`)
|
||||
3. Admin approves → Vendor profile created
|
||||
4. Access `/vendor/dashboard`
|
||||
|
||||
### Hiding Vendor Pages
|
||||
|
||||
When `multi_vendor=false`:
|
||||
- No vendor registration link
|
||||
- No `/vendor/*` routes
|
||||
- No admin vendor management pages
|
||||
- Products belong to "store" (no vendor_id)
|
||||
|
||||
---
|
||||
|
||||
## 🌐 Internationalization
|
||||
|
||||
### Thai/English Support
|
||||
|
||||
- **URL Structure:** `/th/products`, `/en/products` (if bilingual)
|
||||
- **Fallback:** Missing translation → English
|
||||
- **Default:** Thai-only if `--languages th`
|
||||
|
||||
### Translation Keys
|
||||
|
||||
```json
|
||||
// i18n/th.json
|
||||
{
|
||||
"common": {
|
||||
"addToCart": "เพิ่มลงตะกร้า",
|
||||
"checkout": "ชำระเงิน",
|
||||
"login": "เข้าสู่ระบบ"
|
||||
},
|
||||
"product": {
|
||||
"outOfStock": "สินค้าหมด",
|
||||
"inStock": "มีสินค้า"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Environment Variables
|
||||
|
||||
```bash
|
||||
# Supabase (Required)
|
||||
SUPABASE_URL=https://xxx.supabase.co
|
||||
SUPABASE_ANON_KEY=eyJxxx # Public key (client-side)
|
||||
SUPABASE_SERVICE_ROLE_KEY=eyJxxx # Admin key (server-side only!)
|
||||
|
||||
# JWT (Required for auth)
|
||||
JWT_SECRET=your-super-secret-jwt-key-min-32-chars
|
||||
|
||||
# Site
|
||||
SITE_URL=https://yourdomain.com
|
||||
SITE_NAME=My Store
|
||||
|
||||
# PaySo (Optional - stub by default)
|
||||
PAYSOLO_MERCHANT_ID=your-merchant-id
|
||||
PAYSOLO_API_KEY=your-api-key
|
||||
PAYSOLO_SECRET_KEY=your-secret-key
|
||||
PAYSOLO_CALLBACK_URL=https://yourdomain.com/api/webhooks/payso
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🐳 Docker Deployment
|
||||
|
||||
### Dockerfile (Astro SSR Mode)
|
||||
|
||||
```dockerfile
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
|
||||
# Build-time env vars (needed for npm run build)
|
||||
ENV PUBLIC_SUPABASE_URL=https://xxx.supabase.co
|
||||
ENV PUBLIC_SUPABASE_ANON_KEY=eyJxxx
|
||||
ENV SUPABASE_SERVICE_ROLE_KEY=eyJxxx
|
||||
ENV SITE_URL=https://yourdomain.com
|
||||
ENV JWT_SECRET=your-32-char-min-secret-key
|
||||
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
EXPOSE 4321
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=4321
|
||||
|
||||
CMD ["npm", "run", "start"]
|
||||
```
|
||||
|
||||
### docker-compose.yml
|
||||
|
||||
```yaml
|
||||
services:
|
||||
web:
|
||||
build: .
|
||||
ports:
|
||||
- "4321:4321"
|
||||
env_file:
|
||||
- .env
|
||||
```
|
||||
|
||||
### Key Points
|
||||
|
||||
1. **Build-time env vars** - Set `ENV` before `npm run build` so Astro can access them
|
||||
2. **Service role key** - Only in Dockerfile ENV, not in client code
|
||||
3. **Port 4321** - Astro default, map to 80 or your preferred port
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment to Easypanel
|
||||
|
||||
```bash
|
||||
# 1. Generate site locally
|
||||
python3 skills/ecommerce-astro/scripts/create_ecommerce.py \
|
||||
--name "my-store" \
|
||||
--output "./my-store"
|
||||
|
||||
# 2. Push to Gitea
|
||||
cd my-store
|
||||
git init
|
||||
git add .
|
||||
git commit -m "Initial e-commerce site"
|
||||
git remote add origin https://git.moreminimore.com/user/my-store.git
|
||||
git push -u origin main
|
||||
|
||||
# 3. Deploy to Easypanel
|
||||
# Use easypanel-deploy skill or dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ Success Criteria
|
||||
|
||||
- [ ] Astro dev server runs without errors
|
||||
- [ ] Supabase tables created successfully
|
||||
- [ ] Products display from Supabase (images as JSONB)
|
||||
- [ ] Cart adds/removes items (SSR-safe Zustand)
|
||||
- [ ] Checkout creates order
|
||||
- [ ] User registration/login works
|
||||
- [ ] (If multi_vendor=true) Vendor dashboard accessible
|
||||
- [ ] (If bilingual) Language switching works
|
||||
- [ ] Docker build succeeds
|
||||
- [ ] Deploys to Easypanel
|
||||
|
||||
---
|
||||
|
||||
## 📚 Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"astro": "^6.1.4",
|
||||
"@astrojs/react": "^4.2.0",
|
||||
"@astrojs/node": "^9.1.0",
|
||||
"@astrojs/sitemap": "^3.2.0",
|
||||
"@supabase/supabase-js": "^2.47.0",
|
||||
"@supabase/ssr": "^0.6.1",
|
||||
"@tailwindcss/vite": "^4.2.1",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"zustand": "^5.0.0",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0",
|
||||
"jose": "^6.0.0",
|
||||
"@headlessui/react": "^2.0.0",
|
||||
"lucide-react": "^0.400.0"
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Astro 6 is required for CSRF protection and other security features.
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Related Skills
|
||||
|
||||
- **thai-frontend-dev** - Base Astro setup with PDPA compliance, cookie consent
|
||||
- **easypanel-deploy** - Deploy to Easypanel
|
||||
- **gitea-sync** - Sync code to Gitea
|
||||
|
||||
---
|
||||
|
||||
## 📝 Example Usage
|
||||
|
||||
### Single Vendor Store (Thai Only)
|
||||
|
||||
```bash
|
||||
python3 skills/ecommerce-astro/scripts/create_ecommerce.py \
|
||||
--name "ร้านค้าออนไลน์" \
|
||||
--slug "online-store" \
|
||||
--output "./thai-store"
|
||||
# multi_vendor=false by default
|
||||
```
|
||||
|
||||
### Multi-Vendor Marketplace (Bilingual)
|
||||
|
||||
```bash
|
||||
python3 skills/ecommerce-astro/scripts/create_ecommerce.py \
|
||||
--name "ThaiMart" \
|
||||
--slug "thaimart" \
|
||||
--multi-vendor true \
|
||||
--languages "th,en" \
|
||||
--output "./thaimart"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Note:** After generation:
|
||||
1. Run the Supabase migration in your dashboard
|
||||
2. Update `.env` with your Supabase credentials
|
||||
3. Add sample products to test
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### SSR Error: `Cannot read properties of undefined (reading 'value')`
|
||||
|
||||
**Cause:** Zustand persist middleware tries to access localStorage during SSR.
|
||||
|
||||
**Fix:** Add `getStorage` to handle server-side:
|
||||
|
||||
```typescript
|
||||
getStorage: () => {
|
||||
if (typeof window === 'undefined') {
|
||||
return { getItem: () => null, setItem: () => {}, removeItem: () => {} };
|
||||
}
|
||||
return localStorage;
|
||||
}
|
||||
```
|
||||
|
||||
### RLS Policy Blocks Read
|
||||
|
||||
**Cause:** Anon key doesn't bypass RLS during SSR.
|
||||
|
||||
**Fix:** Use service role key for server-side fetches:
|
||||
|
||||
```typescript
|
||||
headers: {
|
||||
'apikey': import.meta.env.SUPABASE_SERVICE_ROLE_KEY,
|
||||
'Authorization': `Bearer ${import.meta.env.SUPABASE_SERVICE_ROLE_KEY}`
|
||||
}
|
||||
```
|
||||
|
||||
### Images Show as `[` or Empty
|
||||
|
||||
**Cause:** Images stored as JSONB string, not array.
|
||||
|
||||
**Fix:** Parse before use:
|
||||
|
||||
```typescript
|
||||
const images = typeof product.images === 'string'
|
||||
? JSON.parse(product.images || '[]')
|
||||
: (product.images || []);
|
||||
```
|
||||
|
||||
### URLSearchParams Error
|
||||
|
||||
**Cause:** Spread operator with undefined in template literal.
|
||||
|
||||
**Fix:** Use string concatenation instead:
|
||||
|
||||
```typescript
|
||||
// Bad
|
||||
href={`/products?${new URLSearchParams({...category && {category}})}`}
|
||||
|
||||
// Good
|
||||
href={`/products?sort=${sort}`}
|
||||
```
|
||||
|
||||
### Cross-site POST form submissions are forbidden
|
||||
|
||||
**Cause:** Astro 6 has built-in CSRF protection that blocks native form POST from different origins.
|
||||
|
||||
**Fix:** Use client-side fetch instead of native form submission:
|
||||
|
||||
```typescript
|
||||
// In your .astro page
|
||||
<form id="registerForm">
|
||||
<input name="email" id="emailInput" />
|
||||
<input name="password" id="passwordInput" />
|
||||
<button type="submit">Register</button>
|
||||
</form>
|
||||
|
||||
<script>
|
||||
document.getElementById('registerForm').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const email = document.getElementById('emailInput').value;
|
||||
const password = document.getElementById('passwordInput').value;
|
||||
|
||||
const res = await fetch('/api/auth/register', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password })
|
||||
});
|
||||
|
||||
if (res.ok) {
|
||||
window.location.href = '/';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
```
|
||||
Reference in New Issue
Block a user