Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 93 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Combined with **Next.js 15**, you get modern React features, Server Components,
- 🌐 **Cloudflare Workers** - Serverless edge compute platform
- 🗃️ **Cloudflare D1** - Distributed SQLite database at the edge
- 📦 **Cloudflare R2** - S3-compatible object storage
- 🤖 **Cloudflare Workers AI** - Edge AI inference with OpenSource models
- 🔑 **Better Auth** - Modern authentication with Google OAuth
- 🛠️ **Drizzle ORM** - TypeScript-first database toolkit

Expand All @@ -45,6 +46,7 @@ Combined with **Next.js 15**, you get modern React features, Server Components,
### 📊 **Data Flow Architecture**
- **Fetching**: Server Actions + React Server Components for optimal performance
- **Mutations**: Server Actions with automatic revalidation
- **AI Processing**: Edge AI inference with Cloudflare Workers AI
- **Type Safety**: End-to-end TypeScript from database to UI
- **Caching**: Built-in Next.js caching with Cloudflare edge caching

Expand All @@ -64,14 +66,16 @@ Create an API token for Wrangler authentication:
2. Select **Create Token** > find **Edit Cloudflare Workers** > select **Use Template**
3. Customize your token name (e.g., "Next.js Cloudflare Template")
4. Scope your token to your account and zones (if using custom domains)
5. **Add additional permissions** for D1 database access:
5. **Add additional permissions** for D1 database and AI access:
- Account - D1:Edit
- Account - D1:Read
- Account - Cloudflare Workers AI:Read

**Final Token Permissions:**
- All permissions from "Edit Cloudflare Workers" template
- Account - D1:Edit (for database operations)
- Account - D1:Read (for database queries)
- Account - Cloudflare Workers AI:Read (for AI inference)

### 3. Clone and Setup

Expand Down Expand Up @@ -182,7 +186,10 @@ Update `wrangler.jsonc` with your resource IDs:
"bucket_name": "your-app-bucket",
"binding": "FILES"
}
]
],
"ai": {
"binding": "AI"
}
}
```

Expand Down Expand Up @@ -465,6 +472,7 @@ src/
├── app/ # Next.js App Router
│ ├── (auth)/ # Auth-related pages
│ ├── api/ # API routes (for external access)
│ │ └── summarize/ # AI summarization endpoint
│ ├── dashboard/ # Dashboard pages
│ └── globals.css # Global styles
├── components/ # Shared UI components
Expand All @@ -487,6 +495,8 @@ src/
│ ├── components/ # Todo components
│ ├── models/ # Todo models
│ └── schemas/ # Todo schemas
├── services/ # Business logic services
│ └── summarizer.service.ts # AI summarization service
└── drizzle/ # Database migrations
```

Expand All @@ -497,6 +507,72 @@ src/
- 🛡️ **Type Safety** - End-to-end TypeScript from database to UI
- 🧪 **Testable** - Clear separation of concerns makes testing easier

## 🤖 AI Development & Testing

### Testing the AI API

**⚠️ Authentication Required**: Login to your app first, then test the API.

**Browser Console (Easiest):**
1. Login at `http://localhost:3000`
2. Open DevTools Console (F12)
3. Run:
```javascript
fetch('/api/summarize', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({
text: "Your text to summarize here...",
config: { maxLength: 100, style: "concise" }
})
}).then(r => r.json()).then(console.log);
```

**cURL (with session cookies):**
1. Login in browser first
2. DevTools → Application → Cookies → Copy `better-auth.session_token`
3. Use cookie in cURL:
```bash
curl -X POST http://localhost:3000/api/summarize \
-H "Content-Type: application/json" \
-H "Cookie: better-auth.session_token=your-token-here" \
-d '{"text": "Your text here...", "config": {"maxLength": 100}}'
```

**Postman:**
1. Login in browser, copy session cookie from DevTools
2. Add header: `Cookie: better-auth.session_token=your-token-here`

**Unauthenticated Request Response:**
```json
{
"success": false,
"error": "Authentication required",
"data": null
}
```


### AI Service Architecture

The AI integration follows a clean service-based architecture:

1. **API Route** (`/api/summarize`) - Handles HTTP requests, authentication, and validation
2. **Authentication Layer** - Validates user session before processing requests
3. **SummarizerService** - Encapsulates AI business logic
4. **Error Handling** - Comprehensive error responses with proper status codes
5. **Type Safety** - Full TypeScript support with Zod validation

### AI Model Options

Cloudflare Workers AI supports various models:
- **@cf/meta/llama-3.2-1b-instruct** - Text generation (current)
- **@cf/meta/llama-3.2-3b-instruct** - More capable text generation
- **@cf/meta/m2m100-1.2b** - Translation
- **@cf/baai/bge-base-en-v1.5** - Text embeddings
- **@cf/microsoft/resnet-50** - Image classification

## 🔧 Advanced Configuration

### Database Schema Changes
Expand Down Expand Up @@ -573,10 +649,25 @@ pnpm run deploy:preview

## ✍️ Todos

### 🤖 AI Features
- [ ] Add text translation service with `@cf/meta/m2m100-1.2b`
- [ ] Implement text embeddings for semantic search with `@cf/baai/bge-base-en-v1.5`
- [ ] Add image classification API with `@cf/microsoft/resnet-50`
- [ ] Create chat/conversation API with conversation memory
- [ ] Add content moderation with AI classification
- [ ] Implement sentiment analysis for user feedback

### 💳 Payments & Communication
- [ ] Implement email sending with [Resend](https://resend.com/) & [Cloudflare Email Routing](https://www.cloudflare.com/developer-platform/products/email-routing/)
- [ ] Implement international payment gateway with [Polar.sh](https://polar.sh/)
- [ ] Implement Indonesian payment gateway either with [Xendit](https://www.xendit.co/en-id/), [Midtrans](https://midtrans.com/en), or [Duitku](https://www.duitku.com/)

### 📊 Analytics & Performance
- [ ] Add Cloudflare Analytics integration
- [ ] Implement custom metrics tracking
- [ ] Add performance monitoring dashboard
- [ ] Create AI usage analytics and cost tracking



## 🤝 Contributing
Expand Down
4 changes: 3 additions & 1 deletion cloudflare-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable */
// Generated by Wrangler by running `wrangler types --env-interface CloudflareEnv ./cloudflare-env.d.ts` (hash: 27c9b47fcb70f49031934291d4d721fa)
// Generated by Wrangler by running `wrangler types --env-interface CloudflareEnv ./cloudflare-env.d.ts` (hash: 25474971f7656c5a2eec353ec5d54b13)
// Runtime types generated with workerd@1.20250906.0 2025-03-01 global_fetch_strictly_public,nodejs_compat
declare namespace Cloudflare {
interface Env {
Expand All @@ -14,6 +14,8 @@ declare namespace Cloudflare {
GOOGLE_CLIENT_SECRET: string;
next_cf_app_bucket: R2Bucket;
next_cf_app: D1Database;
VECTORIZE: VectorizeIndex;
AI: Ai;
ASSETS: Fetcher;
}
}
Expand Down
6 changes: 0 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,31 +6,25 @@
"dev": "next dev",
"build": "next build",
"start": "next start",

"wrangler:dev": "npx wrangler dev",
"dev:cf": "npx @opennextjs/cloudflare build && wrangler dev",
"dev:remote": "npx @opennextjs/cloudflare build && wrangler dev --remote",

"build:cf": "npx @opennextjs/cloudflare build",
"deploy:cf": "npx @opennextjs/cloudflare deploy",
"preview:cf": "npx @opennextjs/cloudflare preview",

"deploy": "npx @opennextjs/cloudflare deploy",
"deploy:preview": "npx @opennextjs/cloudflare deploy --env preview",

"db:generate": "drizzle-kit generate",
"db:generate:named": "drizzle-kit generate --name",
"db:migrate:local": "wrangler d1 migrations apply next-cf-app --local",
"db:migrate:preview": "wrangler d1 migrations apply next-cf-app --env preview",
"db:migrate:prod": "wrangler d1 migrations apply next-cf-app --remote",
"db:studio": "drizzle-kit studio",
"db:studio:local": "drizzle-kit studio --config=drizzle.local.config.ts",

"db:inspect:local": "wrangler d1 execute next-cf-app --local --command=\"SELECT name FROM sqlite_master WHERE type='table';\"",
"db:inspect:preview": "wrangler d1 execute next-cf-app --env preview --command=\"SELECT name FROM sqlite_master WHERE type='table';\"",
"db:inspect:prod": "wrangler d1 execute next-cf-app --remote --command=\"SELECT name FROM sqlite_master WHERE type='table';\"",
"db:reset:local": "wrangler d1 execute next-cf-app --local --command=\"DROP TABLE IF EXISTS todos;\" && pnpm run db:migrate:local",

"cf:secret": "npx wrangler secret put",
"cf-typegen": "pnpm exec wrangler types && pnpm exec wrangler types --env-interface CloudflareEnv ./cloudflare-env.d.ts",
"lint": "npx biome format --write"
Expand Down
23 changes: 16 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions src/app/api/summarize/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { getCloudflareContext } from "@opennextjs/cloudflare";
import { headers } from "next/headers";
import handleApiError from "@/lib/api-error";
import { getAuthInstance } from "@/modules/auth/utils/auth-utils";
import {
SummarizerService,
summarizeRequestSchema,
} from "@/services/summarizer.service";

export async function POST(request: Request) {
try {
// Check authentication
const auth = await getAuthInstance();
const session = await auth.api.getSession({
headers: await headers(),
});

if (!session?.user) {
return new Response(
JSON.stringify({
success: false,
error: "Authentication required",
data: null,
}),
{
status: 401,
headers: {
"Content-Type": "application/json",
},
},
);
}

const { env } = await getCloudflareContext();

if (!env.AI) {
return new Response(
JSON.stringify({
success: false,
error: "AI service is not available",
data: null,
}),
{
status: 500,
headers: {
"Content-Type": "application/json",
},
},
);
}

// parse request body
const body = await request.json();
const validated = summarizeRequestSchema.parse(body);

const summarizerService = new SummarizerService(env.AI);
const result = await summarizerService.summarize(
validated.text,
validated.config,
);

return new Response(
JSON.stringify({
success: true,
data: result,
error: null,
}),
{
status: 200,
headers: {
"Content-Type": "application/json",
},
},
);
} catch (error) {
return handleApiError(error);
}
}
Loading
Loading