# Complete Plan: QRurl with Nuxt on Cloudflare Workers

## Overview
Build a URL shortener with QR code generation using Nuxt (latest stable version) deployed to Cloudflare Workers with D1 database, R2 storage, and Postmark email integration.

## Architecture Components
- **Frontend & Backend**: Single Nuxt application (SSR)
- **Database**: Cloudflare D1 (SQLite)
- **File Storage**: Cloudflare R2 (for logos)
- **Cache**: Cloudflare KV
- **Email**: Postmark API
- **Deployment**: Cloudflare Workers via Wrangler

## Prerequisites
- Node.js 20.x or later (stable LTS)
- Cloudflare account with Workers, D1, R2, KV enabled
- Domain in Cloudflare (qrurl.us)
- Postmark account and API key
- GitHub account (optional for CI/CD)

## Step-by-Step Implementation Plan

### Phase 1: Project Setup

#### 1.1 Initialize Nuxt Project
```bash
npx nuxi@latest init qrurl --package-manager npm
cd qrurl
```

#### 1.2 Install Core Dependencies
```bash
npm install --save-dev wrangler@latest
npm install @nuxt/ui @pinia/nuxt @vueuse/nuxt
npm install qrcode jsonwebtoken bcryptjs
npm install --save-dev @types/jsonwebtoken @types/bcryptjs
```

#### 1.3 Configure Nuxt for Cloudflare
Create `nuxt.config.ts`:
- Set nitro preset to `cloudflare-pages` or `cloudflare-module`
- Configure build output for Workers
- Set up environment variables
- Configure TypeScript

### Phase 2: Database Setup

#### 2.1 Create D1 Database
```bash
wrangler d1 create qrurl-db
```

#### 2.2 Database Schema
Create `schema.sql`:
```sql
CREATE TABLE links (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  slug TEXT UNIQUE NOT NULL,
  url TEXT NOT NULL,
  name TEXT,
  logo_key TEXT,
  user_email TEXT,
  clicks INTEGER DEFAULT 0,
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE auth_tokens (
  token TEXT PRIMARY KEY,
  email TEXT NOT NULL,
  used INTEGER DEFAULT 0,
  expires_at DATETIME NOT NULL
);

CREATE TABLE sessions (
  id TEXT PRIMARY KEY,
  user_email TEXT NOT NULL,
  expires_at DATETIME NOT NULL
);

CREATE INDEX idx_links_slug ON links(slug);
CREATE INDEX idx_sessions_email ON sessions(user_email);
```

#### 2.3 Initialize Database
```bash
wrangler d1 execute qrurl-db --file=./schema.sql --local
wrangler d1 execute qrurl-db --file=./schema.sql --remote
```

### Phase 3: Cloudflare Resources Setup

#### 3.1 Create R2 Bucket
```bash
wrangler r2 bucket create qrurl-logos
```

#### 3.2 Create KV Namespace
```bash
wrangler kv namespace create cache
```

#### 3.3 Update wrangler.toml
```toml
name = "qrurl"
compatibility_date = "2024-12-01"
pages_build_output_dir = ".output/public"

[[d1_databases]]
binding = "DB"
database_name = "qrurl-db"
database_id = "YOUR_DB_ID"

[[r2_buckets]]
binding = "STORAGE"
bucket_name = "qrurl-logos"

[[kv_namespaces]]
binding = "CACHE"
id = "YOUR_KV_ID"

[vars]
EMAIL_FROM = "noreply@qrurl.us"
```

### Phase 4: Application Development

#### 4.1 Directory Structure
```
qrurl/
├── server/
│   ├── api/
│   │   ├── auth/
│   │   │   ├── request.post.ts
│   │   │   └── verify.get.ts
│   │   ├── links/
│   │   │   ├── index.get.ts
│   │   │   ├── index.post.ts
│   │   │   └── [id].delete.ts
│   │   ├── qr/
│   │   │   └── [slug].get.ts
│   │   └── logo/
│   │       ├── upload.post.ts
│   │       └── [id].get.ts
│   ├── middleware/
│   │   └── auth.ts
│   └── utils/
│       ├── db.ts
│       ├── auth.ts
│       └── email.ts
├── pages/
│   ├── index.vue
│   ├── login.vue
│   ├── dashboard.vue
│   └── [slug].vue
├── components/
│   ├── NavBar.vue
│   ├── LinkForm.vue
│   ├── LinkList.vue
│   ├── QRCodeModal.vue
│   └── LogoUploader.vue
├── stores/
│   └── auth.ts
├── composables/
│   └── useApi.ts
└── public/
```

#### 4.2 Server API Implementation

**Database Utils** (`server/utils/db.ts`):
- Direct D1 binding access
- Query builders
- Migration helpers

**Auth Utils** (`server/utils/auth.ts`):
- JWT token generation/verification
- Session management
- Cookie handling

**Email Utils** (`server/utils/email.ts`):
- Postmark integration
- Magic link generation
- Email templates

#### 4.3 Frontend Implementation

**Pages**:
- `index.vue`: Public URL shortener
- `login.vue`: Magic link request
- `dashboard.vue`: Authenticated link management
- `[slug].vue`: Redirect handler

**Components**:
- Form validation
- Real-time updates
- QR code generation with logo overlay
- File upload to R2

**State Management** (Pinia):
- Auth store
- Links store
- UI store

### Phase 5: Authentication Flow

1. User enters email on login page
2. Server validates email against whitelist
3. Generate magic link token, store in D1
4. Send email via Postmark
5. User clicks link
6. Verify token, create session
7. Set HTTP-only cookie
8. Redirect to dashboard

### Phase 6: Core Features

#### 6.1 URL Shortening
- Generate random slug or accept custom
- Validate URL format
- Check slug uniqueness
- Store in D1 with metadata

#### 6.2 QR Code Generation
- Use qrcode library
- High error correction for logo overlay
- Return as base64 or binary
- Cache in KV for performance

#### 6.3 Logo Upload
- Accept image upload
- Validate file type/size
- Store in R2 with unique key
- Reference in link record

#### 6.4 Analytics
- Track clicks in D1
- Store user agent, referrer
- Display in dashboard
- Export functionality

### Phase 7: Environment Configuration

#### 7.1 Development (.env)
```env
NUXT_JWT_SECRET=dev-secret
NUXT_EMAIL_API_KEY=your-postmark-key
NUXT_AUTHORIZED_EMAILS=email1@example.com,email2@example.com
```

#### 7.2 Production Secrets
```bash
wrangler secret put JWT_SECRET
wrangler secret put EMAIL_API_KEY
wrangler secret put AUTHORIZED_EMAILS
```

### Phase 8: Deployment

#### 8.1 Build Process
```bash
npm run build
```

#### 8.2 Deploy to Cloudflare
```bash
wrangler pages deploy .output/public
```

#### 8.3 Configure Custom Domain
1. Cloudflare Dashboard → Pages → Custom domains
2. Add qrurl.us
3. Configure DNS if needed

### Phase 9: Testing & Optimization

#### 9.1 Local Testing
```bash
npm run dev        # Development server
npm run preview    # Production preview
```

#### 9.2 Performance Optimization
- Implement caching strategies
- Optimize database queries
- Compress assets
- Lazy load components

#### 9.3 Security
- Rate limiting
- Input validation
- CSRF protection
- Content Security Policy

### Phase 10: CI/CD Setup (Optional)

#### 10.1 GitHub Actions
```yaml
name: Deploy
on:
  push:
    branches: [main]
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npm run build
      - uses: cloudflare/wrangler-action@v3
        with:
          apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```

## Common Issues & Solutions

### Issue 1: EBADF Errors
- Use Node.js LTS (20.x)
- Avoid Node.js 23.x
- Check file descriptor limits

### Issue 2: D1 Binding Issues
- Ensure database ID matches
- Check wrangler.toml configuration
- Verify local vs remote execution

### Issue 3: CORS Problems
- Not needed with unified deployment
- Everything on same domain

### Issue 4: Build Failures
- Clear .nuxt and node_modules
- Reinstall dependencies
- Check TypeScript errors

## Key Differences from Framework-Heavy Approach

1. **Simpler Structure**: Single deployment unit
2. **No CORS**: API and frontend on same domain
3. **Direct Bindings**: Use Cloudflare resources directly
4. **Better Performance**: Edge-optimized
5. **Easier Debugging**: Unified logs

## Testing Checklist

- [ ] Homepage loads
- [ ] URL shortening works
- [ ] QR codes generate
- [ ] Redirects work
- [ ] Login flow completes
- [ ] Dashboard accessible
- [ ] Logo upload works
- [ ] Analytics track
- [ ] Session persistence
- [ ] Logout works

## Production Checklist

- [ ] Database migrated
- [ ] Secrets configured
- [ ] Custom domain active
- [ ] SSL working
- [ ] Email sending
- [ ] Error handling
- [ ] Monitoring setup
- [ ] Backup strategy

## Estimated Timeline

- **Phase 1-3**: 1 hour (setup)
- **Phase 4-6**: 4-6 hours (development)
- **Phase 7-8**: 1 hour (deployment)
- **Phase 9-10**: 2 hours (testing/optimization)

**Total**: 8-10 hours for complete implementation

## Success Criteria

1. App deploys to qrurl.us
2. All features from original app work
3. No framework complexity
4. Fast performance (<100ms response)
5. Reliable email delivery
6. Secure authentication
7. Clean, maintainable code