Pastebin for AI-generated web apps. Publish raw HTML at a short URL.
Live: ship.tanav.me
- Next.js 15 (App Router) + React 19
- TypeScript (strict mode)
- Tailwind CSS for styling
- Vercel Blob for HTML storage
- Upstash Redis for metadata + rate limiting
- Zod for input validation
- Vitest for testing
# Install dependencies
npm install
# Copy environment variables
cp .env.example .env
# Start development server
npm run dev| Variable | Required | Description |
|---|---|---|
BLOB_READ_WRITE_TOKEN |
Yes | Vercel Blob storage token |
UPSTASH_REDIS_REST_URL |
Yes | Upstash Redis REST URL |
UPSTASH_REDIS_REST_TOKEN |
Yes | Upstash Redis REST token |
NEXT_PUBLIC_SITE_URL |
No | Public site URL (defaults to https://ship.tanav.me) |
npm run dev # Start dev server
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLint
npm run lint:fix # Auto-fix lint issues
npm run typecheck # TypeScript type checking
npm run check # Run typecheck + lint
npm run test # Run tests
npm run test:watch # Run tests in watch modePOST /api/publish
Content-Type: application/json
{"html":"<!DOCTYPE html><html><body>Hello</body></html>"}Response (201):
{
"success": true,
"id": "abc123",
"url": "https://ship.tanav.me/abc123"
}GET /api/page/[slug]Open https://ship.tanav.me/[slug] in a browser.
GET /api/health- Global: 200 requests/60s per IP
- Publish: 20 requests/60s per IP
Rate-limited responses include a code field and retryAfter in seconds.
app/
[slug]/route.ts # GET /[slug] - Serve published HTML
api/
health/route.ts # GET /api/health
page/[slug]/route.ts # GET /api/page/[slug]
publish/route.ts # POST /api/publish
docs/page.tsx # API documentation page
layout.tsx # Root layout
page.tsx # Home page
components/
copy-button.tsx # Clipboard copy button
lib/
config.ts # Site URL configuration
env.ts # Environment detection
http.ts # HTTP response helpers
id.ts # Unique ID generation
log.ts # Structured logging
rate-limit.ts # Rate limiting with Redis fallback
redis.ts # Upstash Redis client
sanitize.ts # HTML helpers
site.ts # Page storage/retrieval
types.ts # TypeScript types
- HTML is stored and served raw, exactly as published
- Content Security Policy headers on all HTML responses
x-content-type-options: nosniffprevents MIME sniffingreferrer-policy: no-referrerprevents data leakagex-robots-tag: noindex, nofollowkeeps pages out of search enginesframe-ancestors 'none'prevents clickjacking
MIT