Initial commit: json-render crepes demo
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
This commit is contained in:
382
README.md
Normal file
382
README.md
Normal file
@@ -0,0 +1,382 @@
|
||||
# 🥞 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! 🇫🇷**
|
||||
Reference in New Issue
Block a user