Turn your PDF resume into a hosted web portfolio in under 60 seconds.
Upload a PDF. AI parses it. Get a shareable link.
- Instant PDF Parsing - AI extracts your information automatically
- Clean Public URLs - Get
yoursite.com/yournameimmediately - Privacy Controls - Show/hide phone numbers and addresses
- Multiple Templates - Professional, modern designs
- Mobile Responsive - Looks great on all devices
- SEO Optimized - Proper metadata, Open Graph tags
| Layer | Technology |
|---|---|
| Framework | Next.js 15 (App Router) |
| Runtime | Cloudflare Workers |
| Database | Cloudflare D1 (SQLite) + Drizzle ORM |
| Auth | Better Auth (Google OAuth) |
| Storage | Cloudflare R2 (S3-compatible) |
| AI Parsing | Replicate (datalab-to/marker) |
| Styling | Tailwind CSS 4 + Radix UI |
We chose Cloudflare Workers over traditional hosting for several reasons:
- Edge Computing: Code runs in 300+ data centers worldwide, closest to your users
- Cold Start: ~0ms cold starts vs. 200-500ms on traditional serverless
- Latency: Sub-50ms response times globally
- Free Tier: 100,000 requests/day free
- D1 Database: 5GB free, built-in SQLite
- R2 Storage: 10GB free, no egress fees
- Total: A production app can run free for most use cases
- No Container Management: Just deploy code
- Automatic Scaling: From 0 to millions of requests
- Integrated Stack: D1, R2, and Workers work seamlessly together
- No
fsModule: Must use R2 for file operations - No Native Next.js Image: Use
<img>with CSS instead - Edge Middleware Limits: No D1 access in middleware
- Bundle Size: Keep dependencies minimal
- Bun v1.0+ (package manager)
- Cloudflare Account with R2 and D1 enabled
- Google Cloud Console project for OAuth
- Replicate account for AI parsing
# Clone the repository
git clone https://github.com/divkix/webresume.now.git
cd webresume.now
# Install dependencies
bun install
# Copy environment template
cp .env.example .env.local
# Set up local database
bun run db:migrate
# Start development server
bun run dev-
Create a Cloudflare account at cloudflare.com
-
Create D1 Database
bunx wrangler d1 create webresume-db
Copy the
database_idtowrangler.jsonc -
Create R2 Bucket
- Go to Cloudflare Dashboard > R2
- Create bucket named
webresume-uploads - Generate API token with Read & Write permissions
- Note your Account ID and Access Keys
-
Configure R2 CORS Add CORS policy in R2 bucket settings:
[ { "AllowedOrigins": ["http://localhost:3000", "https://your-domain.com"], "AllowedMethods": ["GET", "PUT", "POST"], "AllowedHeaders": ["*"], "MaxAgeSeconds": 3000 } ]
- Go to Google Cloud Console
- Create a new project (or select existing)
- Enable Google+ API and People API
- Go to APIs & Services > Credentials
- Create OAuth 2.0 Client ID (Web application type)
- Add authorized redirect URIs:
- Development:
http://localhost:3000/api/auth/callback/google - Production:
https://your-domain.com/api/auth/callback/google
- Development:
- Copy Client ID and Client Secret
- Create account at replicate.com
- Go to Account Settings > API Tokens
- Create new token and copy it
Optional: Cloudflare AI Gateway (BYOK) For enhanced reliability and caching:
- Go to Cloudflare Dashboard > AI > AI Gateway
- Create a gateway
- Store your Replicate token in Cloudflare Secrets Store
- Use
CF_AI_GATEWAY_*environment variables
Create .env.local for development:
# Generate a secure secret
openssl rand -base64 32
# Copy to .env.local
BETTER_AUTH_SECRET=your-generated-secret
BETTER_AUTH_URL=http://localhost:3000
GOOGLE_CLIENT_ID=your-client-id.apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=your-client-secret
R2_ENDPOINT=https://your-account-id.r2.cloudflarestorage.com
R2_ACCESS_KEY_ID=your-access-key
R2_SECRET_ACCESS_KEY=your-secret-key
R2_BUCKET_NAME=webresume-uploads
REPLICATE_API_TOKEN=r8_your-token
REPLICATE_WEBHOOK_SECRET=whsec_your-webhook-secret
NEXT_PUBLIC_APP_URL=http://localhost:3000See .env.example for complete template with all options.
-
Apply database migrations
bun run db:migrate:prod
-
Set production secrets
bunx wrangler secret put BETTER_AUTH_SECRET bunx wrangler secret put BETTER_AUTH_URL bunx wrangler secret put GOOGLE_CLIENT_ID bunx wrangler secret put GOOGLE_CLIENT_SECRET bunx wrangler secret put R2_ENDPOINT bunx wrangler secret put R2_ACCESS_KEY_ID bunx wrangler secret put R2_SECRET_ACCESS_KEY bunx wrangler secret put R2_BUCKET_NAME bunx wrangler secret put REPLICATE_API_TOKEN bunx wrangler secret put REPLICATE_WEBHOOK_SECRET bunx wrangler secret put NEXT_PUBLIC_APP_URL
-
Deploy
bun run deploy
-
Configure custom domain (optional)
- In Cloudflare Dashboard > Workers & Pages > Your Worker
- Add custom domain in Settings > Domains & Routes
# Development
bun run dev # Start dev server at localhost:3000
bun run lint # Biome linting
bun run fix # Biome auto-fix
bun run type-check # TypeScript check
# Build & Deploy
bun run build # Next.js production build
bun run build:worker # OpenNext Cloudflare bundle
bun run preview # Local Cloudflare preview
bun run deploy # Build and deploy to Cloudflare Workers
# Database (D1 + Drizzle)
bun run db:generate # Generate migrations from schema
bun run db:migrate # Apply migrations locally
bun run db:migrate:prod # Apply migrations to production
bun run db:studio # Drizzle Studio UI (port 4984)
bun run db:reset # Wipe local D1 and re-migrate
# Quality
bun run ci # type-check + lint + buildapp/
βββ (auth)/ # /api/auth/* - Better Auth handlers
βββ (public)/ # / and /[handle] - no auth required
β βββ page.tsx # Homepage with upload dropzone
β βββ [handle]/ # Public resume viewer (SSR)
βββ (protected)/ # Auth required pages
βββ dashboard/ # User dashboard
βββ edit/ # Content editor with auto-save
βββ settings/ # Privacy toggles, theme selection
βββ waiting/ # AI parsing status polling
components/
βββ templates/ # Resume templates (MinimalistEditorial, etc.)
βββ ui/ # Reusable UI components (shadcn/ui)
lib/
βββ auth/ # Better Auth configuration
βββ db/ # Drizzle schema and client
βββ schemas/ # Zod validation schemas
βββ utils/ # Utility functions
Allows anonymous users to upload before authenticating:
1. POST /api/upload/sign β Get presigned R2 URL
2. Client uploads to R2 β Store temp key in localStorage
3. User authenticates β Google OAuth
4. POST /api/resume/claim β Link upload to user, trigger parsing
5. Poll /api/resume/status β Wait for AI parsing (~30-40s)
Before rendering public profiles:
- Phone numbers: Hidden by default
- Addresses: City/State only (full address hidden)
- Email: Public (for contact)
- User controls visibility in settings
Four built-in templates in components/templates/:
| Template | Description |
|---|---|
| MinimalistEditorial | Serif fonts, editorial aesthetic (default) |
| NeoBrutalist | Bold borders, high contrast |
| GlassMorphic | Blur effects, dark background |
| BentoGrid | Mosaic grid layout |
All templates receive content (ResumeContent) and user props, respect privacy settings, and are mobile-responsive.
- Application-Level Authorization: All data access controlled in code
- Rate Limiting: 5 uploads/day, 10 updates/hour per user
- Input Validation: Zod schemas on all endpoints
- XSS Protection: React's default sanitization
- Encrypted Secrets: All secrets encrypted in Cloudflare
See SECURITY.md for security policy and vulnerability reporting.
Contributions welcome! Please read CONTRIBUTING.md for guidelines.
- Fork the repository
- Create a feature branch (
git checkout -b feat/amazing-feature) - Use conventional commits (
feat:,fix:,docs:) - Run quality checks (
bun run ci) - Submit a pull request
bun run type-check # See all errors
bun run build # Fix errors and rebuild- Verify
BETTER_AUTH_URLincludeshttps://for production - Check redirect URIs match in Google Cloud Console
- Clear browser cookies
- Check R2 CORS includes your domain
- Verify R2 API token has Read & Write permissions
- Confirm
R2_BUCKET_NAMEmatches actual bucket
- Verify Replicate API token is valid
- Check PDF isn't corrupted
- Use retry button (max 2 retries)
- Check Replicate dashboard for job status
You're on Cloudflare Workers. Use R2 presigned URLs for file operations.
MIT License - see LICENSE for details.
- Next.js - React framework
- Better Auth - Authentication
- Drizzle ORM - Type-safe database
- Cloudflare - Edge infrastructure
- Replicate - AI inference
- Radix UI - Accessible components
- Tailwind CSS - Styling
Built with TypeScript. Deployed on the edge. Designed for speed.