Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
383 lines
9.5 KiB
Markdown
383 lines
9.5 KiB
Markdown
# 🥞 Les Crêpes de Maîtres
|
|
|
|
A beautiful demo showcasing **[json-render](https://github.com/vercel-labs/json-render)** with French chef crêpe recipes. Features recipes from legendary chefs like Auguste Escoffier, Olivier Roellinger, Pierre Hermé, and Joël Robuchon.
|
|
|
|
## 🎯 What is json-render?
|
|
|
|
**json-render** is a library from Vercel Labs that enables AI-generated UIs with guardrailed components. It provides:
|
|
|
|
- **Guardrailed** — AI can only use components in your catalog
|
|
- **Predictable** — JSON output matches your schema, every time
|
|
- **Fast** — Stream and render progressively as the model responds
|
|
|
|
This demo uses json-render's `Renderer` component with a type-safe catalog to render recipe specs as React components.
|
|
|
|
## ✨ How It Works
|
|
|
|
1. **Define Component Catalog** (`lib/catalog.ts`):
|
|
```typescript
|
|
export const catalog = defineCatalog(schema, {
|
|
components: {
|
|
RecipeHeader: {
|
|
props: z.object({
|
|
title: z.string(),
|
|
chef: z.string(),
|
|
// ...
|
|
}),
|
|
description: "Header with recipe info"
|
|
},
|
|
// ...
|
|
}
|
|
});
|
|
```
|
|
|
|
2. **Map to React Components** (`lib/registry.tsx`):
|
|
```typescript
|
|
export const { registry } = defineRegistry(catalog, {
|
|
components: {
|
|
RecipeHeader: ({ props }) => (
|
|
<div>
|
|
<h1>{props.title}</h1>
|
|
{/* ... */}
|
|
</div>
|
|
),
|
|
// ...
|
|
}
|
|
});
|
|
```
|
|
|
|
3. **Create JSON Specs** (`lib/recipes.ts`):
|
|
```typescript
|
|
const recipe = {
|
|
root: "root",
|
|
elements: {
|
|
root: {
|
|
type: "RecipeCard",
|
|
props: { title: "Crêpes Suzette", /* ... */ },
|
|
children: ["chef", "header", "ingredients", /* ... */]
|
|
},
|
|
chef: {
|
|
type: "ChefInfo",
|
|
props: { name: "Auguste Escoffier", /* ... */ }
|
|
},
|
|
// ...
|
|
}
|
|
};
|
|
```
|
|
|
|
4. **Render with json-render** (`app/page.tsx`):
|
|
```typescript
|
|
<Provider>
|
|
<Renderer spec={recipe} registry={registry} />
|
|
</Provider>
|
|
```
|
|
|
|
**Result**: AI-generated JSON specs become beautiful, type-safe React UIs!
|
|
|
|
## 🚀 Quick Start (Local Development)
|
|
|
|
```bash
|
|
# Install dependencies
|
|
npm install
|
|
|
|
# Run development server
|
|
npm run dev
|
|
|
|
# Open http://localhost:3000
|
|
```
|
|
|
|
## 🐳 Docker Build
|
|
|
|
```bash
|
|
# Build the image
|
|
docker build -t crepes-demo:latest .
|
|
|
|
# Run locally
|
|
docker run -p 3000:3000 crepes-demo:latest
|
|
```
|
|
|
|
## ☸️ Kubernetes Deployment with Flux
|
|
|
|
### Prerequisites
|
|
|
|
- Kubernetes cluster with Flux installed
|
|
- Container registry (Docker Hub, GitHub Container Registry, etc.)
|
|
- Domain configured with Cloudflare
|
|
- Ingress controller (nginx, traefik, etc.)
|
|
- cert-manager for TLS certificates
|
|
- external-dns for automatic DNS records (optional)
|
|
|
|
### Step 1: Build and Push Image
|
|
|
|
```bash
|
|
# Tag your image
|
|
docker tag crepes-demo:latest YOUR_REGISTRY/crepes-demo:latest
|
|
|
|
# Push to registry
|
|
docker push YOUR_REGISTRY/crepes-demo:latest
|
|
```
|
|
|
|
### Step 2: Update Configuration
|
|
|
|
**k8s/deployment.yaml:**
|
|
- Update `image:` with your registry URL
|
|
|
|
**k8s/ingress.yaml:**
|
|
- Update all instances of `crepes.yourdomain.com` with your actual domain
|
|
- Adjust `ingressClassName` if not using nginx
|
|
- Verify cert-manager issuer name
|
|
|
|
**flux/gitrepository.yaml:**
|
|
- Update Git repository URL with your fork
|
|
|
|
### Step 3: Use the Deploy Script
|
|
|
|
```bash
|
|
cd /home/openclaw/.openclaw/workspace/crepes-demo
|
|
|
|
# Set your configuration
|
|
export REGISTRY="ghcr.io/YOUR_USERNAME"
|
|
export DOMAIN="crepes.yourdomain.com"
|
|
|
|
# Run deployment script
|
|
./deploy.sh $REGISTRY $DOMAIN
|
|
```
|
|
|
|
### Step 4: Push to Git
|
|
|
|
```bash
|
|
# Initialize git repository
|
|
git init
|
|
git add .
|
|
git commit -m "Initial commit: json-render crêpes demo"
|
|
|
|
# Push to your GitHub repository
|
|
git remote add origin https://github.com/YOUR_USERNAME/crepes-demo.git
|
|
git push -u origin main
|
|
```
|
|
|
|
### Step 5: Deploy Flux Resources
|
|
|
|
```bash
|
|
# Apply Flux GitRepository and Kustomization
|
|
kubectl apply -f flux/gitrepository.yaml
|
|
kubectl apply -f flux/kustomization.yaml
|
|
|
|
# Watch deployment
|
|
kubectl get kustomizations -n flux-system -w
|
|
```
|
|
|
|
### Step 6: Verify Deployment
|
|
|
|
```bash
|
|
# Check pods
|
|
kubectl get pods -l app=crepes-demo
|
|
|
|
# Check service
|
|
kubectl get svc crepes-demo
|
|
|
|
# Check ingress
|
|
kubectl get ingress crepes-demo
|
|
|
|
# Check Flux sync status
|
|
flux get kustomizations
|
|
```
|
|
|
|
### Step 7: Configure Cloudflare
|
|
|
|
If using external-dns with Cloudflare:
|
|
|
|
1. DNS records should be created automatically
|
|
2. Verify in Cloudflare dashboard
|
|
3. Ensure SSL/TLS mode is "Full (strict)" or "Full"
|
|
|
|
If configuring manually:
|
|
|
|
1. Create an A or CNAME record pointing to your ingress IP/hostname
|
|
2. Enable Cloudflare proxy (orange cloud) for DDoS protection
|
|
3. Configure SSL/TLS settings
|
|
|
|
## 🔄 Continuous Deployment
|
|
|
|
Flux will automatically:
|
|
- Monitor your Git repository every 1 minute
|
|
- Apply changes to the cluster
|
|
- Check deployment health
|
|
- Rollback on failures
|
|
|
|
To update the app:
|
|
|
|
```bash
|
|
# Make changes
|
|
git add .
|
|
git commit -m "Update recipe"
|
|
git push
|
|
|
|
# Flux will sync automatically, or force sync:
|
|
flux reconcile kustomization crepes-demo
|
|
```
|
|
|
|
## 🎨 Customization
|
|
|
|
### Adding New Recipes
|
|
|
|
1. Define your recipe spec in `lib/recipes.ts`:
|
|
```typescript
|
|
{
|
|
root: "root",
|
|
elements: {
|
|
root: {
|
|
type: "RecipeCard",
|
|
props: { title: "My Recipe", /* ... */ },
|
|
children: ["chef", "header", /* ... */]
|
|
},
|
|
// ... more elements
|
|
}
|
|
}
|
|
```
|
|
|
|
2. The spec will be automatically rendered using the existing component catalog!
|
|
|
|
### Adding New Components
|
|
|
|
1. Update component schema in `lib/catalog.ts`:
|
|
```typescript
|
|
MyNewComponent: {
|
|
props: z.object({
|
|
myProp: z.string(),
|
|
}),
|
|
description: "My new component"
|
|
}
|
|
```
|
|
|
|
2. Add React implementation in `lib/registry.tsx`:
|
|
```typescript
|
|
MyNewComponent: ({ props }) => (
|
|
<div>{props.myProp}</div>
|
|
)
|
|
```
|
|
|
|
3. Use in recipe specs with `type: "MyNewComponent"`
|
|
|
|
### Styling
|
|
|
|
The app uses Tailwind CSS. Modify component styles in `lib/registry.tsx` or adjust the theme in `tailwind.config.ts`.
|
|
|
|
## 📚 Project Structure
|
|
|
|
```
|
|
crepes-demo/
|
|
├── app/
|
|
│ ├── page.tsx # Main page with Renderer
|
|
│ ├── layout.tsx # Root layout
|
|
│ └── globals.css # Global styles
|
|
├── lib/
|
|
│ ├── catalog.ts # json-render component catalog
|
|
│ ├── registry.tsx # React component implementations + Provider
|
|
│ └── recipes.ts # Recipe JSON specs
|
|
├── k8s/
|
|
│ ├── deployment.yaml # Kubernetes Deployment
|
|
│ ├── service.yaml # Kubernetes Service
|
|
│ ├── ingress.yaml # Kubernetes Ingress (Cloudflare)
|
|
│ └── kustomization.yaml # Kustomize config
|
|
├── flux/
|
|
│ ├── gitrepository.yaml # Flux GitRepository
|
|
│ └── kustomization.yaml # Flux Kustomization
|
|
├── .github/workflows/
|
|
│ └── deploy.yml # GitHub Actions CI/CD
|
|
├── Dockerfile # Multi-stage Docker build
|
|
├── deploy.sh # Automated deployment script
|
|
├── README.md # Full documentation (this file)
|
|
└── QUICKSTART.md # Quick deployment guide
|
|
```
|
|
|
|
## 🔧 Troubleshooting
|
|
|
|
### Pods not starting
|
|
|
|
```bash
|
|
kubectl describe pod -l app=crepes-demo
|
|
kubectl logs -l app=crepes-demo
|
|
```
|
|
|
|
### Flux not syncing
|
|
|
|
```bash
|
|
flux logs
|
|
flux get sources git
|
|
flux get kustomizations
|
|
```
|
|
|
|
### Certificate issues
|
|
|
|
```bash
|
|
kubectl describe certificate crepes-demo-tls
|
|
kubectl describe certificaterequest
|
|
kubectl logs -n cert-manager -l app=cert-manager
|
|
```
|
|
|
|
### DNS not resolving
|
|
|
|
```bash
|
|
# Check external-dns logs
|
|
kubectl logs -n external-dns -l app=external-dns
|
|
|
|
# Verify ingress has external IP
|
|
kubectl get ingress crepes-demo
|
|
```
|
|
|
|
## 🌐 Architecture
|
|
|
|
```
|
|
User Request
|
|
↓
|
|
Cloudflare (CDN, DDoS protection, SSL)
|
|
↓
|
|
Ingress Controller (nginx)
|
|
↓
|
|
Kubernetes Service
|
|
↓
|
|
Deployment (2 replicas)
|
|
↓
|
|
Next.js App (json-render Renderer)
|
|
```
|
|
|
|
## 🎓 Learning json-render
|
|
|
|
This demo shows the core concepts:
|
|
|
|
1. **Catalog Definition**: Define allowed components with typed props (Zod schemas)
|
|
2. **Registry**: Map component names to React implementations
|
|
3. **Spec Format**: JSON tree structure with `root` + `elements`
|
|
4. **Renderer**: Takes spec + registry, outputs React components
|
|
5. **Provider**: Wraps Renderer with required contexts (Action, Data, Visibility)
|
|
|
|
**In production with AI**:
|
|
- User types: "Show me a recipe for chocolate chip cookies"
|
|
- AI generates a JSON spec using your catalog components
|
|
- json-render's `Renderer` turns it into React instantly
|
|
- **Safe**: AI can't inject arbitrary code, only use your components
|
|
|
|
## 📖 Additional Resources
|
|
|
|
- [json-render GitHub](https://github.com/vercel-labs/json-render) - Official repository
|
|
- [Flux documentation](https://fluxcd.io/docs/) - GitOps toolkit
|
|
- [Next.js deployment](https://nextjs.org/docs/deployment) - Deployment guides
|
|
- [Cloudflare Pages/Workers](https://developers.cloudflare.com/) - Edge deployment
|
|
|
|
## 🍴 Recipe Credits
|
|
|
|
Recipes inspired by the legendary techniques of:
|
|
- **Auguste Escoffier** - Father of modern French cuisine (Crêpes Suzette)
|
|
- **Olivier Roellinger** - Three-Michelin-starred Breton chef (Buckwheat Galettes)
|
|
- **Pierre Hermé** - "Picasso of Pastry" (Salted Caramel Crêpes)
|
|
- **Joël Robuchon** - Most Michelin-starred chef in history (Crêpes Parmentier)
|
|
|
|
## 📜 License
|
|
|
|
MIT License - Feel free to use this demo for your own projects!
|
|
|
|
---
|
|
|
|
**Built with ❤️ using json-render. Bon Appétit! 🇫🇷**
|