Skip to content
kurashizuPublic

About

Next.js blog + AI agent running on Cloudflare Workers with R2 storage

Resources

Stars

2 stars

Watchers

0 watching

Forks

Latest commit

 

History

1,154 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cf-blog

A personal blog + AI agent deployed to Cloudflare Workers. The homepage has a real-time activity dashboard (GitHub contributions + top languages) refreshed every 30 min by a cron worker.

Features

  • Blog — Markdown articles in D1, syntax-highlighted code
  • HN News Archive — Top 5 HN stories fetched every 30 min, with full-length AI rewrites (Gemma 4 via 3-min heartbeat cron)
  • GitHub Contributions Heatmap — Real-time activity ribbon in the hero
  • Top Languages Donut — 5-segment SVG donut with hover interaction (shows language name + percentage in center), color-coded legend (hero sidebar)
  • Visitor Terminal — Typewriter effect showing your IP/location/ISP at the top of the hero
  • KurAgent — AI assistant with tool calling (web search, time, JS eval) at agent.krsz.in
  • LLM Leaderboard — Full-screen modal showing top 50 models from Artificial Analysis (V2 Free endpoint, refreshed at most every 2 h to respect the 100 req/day quota)
  • Guestbook — Visitor messages on the homepage (flow layout + modal)
  • News Archive — Paginated HN news index at /news with full-text AI rewrites
  • Particle Background — Canvas character rain (ku/ra/shi/zu)
  • Three Themes — dark / deep-blue / deep-green, toggle in the header

Architecture

Three Cloudflare Workers, deployed in parallel by GitHub Actions on push to main:

Worker Path URL Purpose
cf-blog ./ https://blog.krsz.in Main blog (Next.js)
cf-agent agent-worker/ https://agent.krsz.in AI agent with tool calling
cf-blog-cache cache-worker/ (cron-only) Homepage cache refresh (30-min) + News rewrite heartbeat (3-min)

Apex domain is centralized in shared/site-config.ts (APEX_DOMAIN, currently krsz.in). All worker URLs fan out from a single constant — change one line to migrate to a new apex.

The homepage never calls GitHub / Artificial Analysis on user requests. cf-blog-cache pre-fetches data into D1 (posts, news, github_repos, leaderboard, contributions); the homepage reads everything from D1. Cold cache = empty hero; ISR revalidates every 5 min.

CI runs on push to main: migrate-db (database/schema.sql) first, then deploys all three workers in parallel.

Tech Stack

  • Frontend: Next.js 15 (App Router) + React 19
  • Styling: Tailwind CSS (utility classes in JSX) + tokens.css/base.css (CSS variables + body effects) + component-level CSS (activity.css, nes.css) where color-mix()/pseudo-elements can't be expressed in Tailwind
  • Storage: Cloudflare D1 (articles, news, github_repos, leaderboard, contributions, guestbook) + Cloudflare KV (sessions, rate limits)
  • AI: Gemini (with quota-based model fallback for TPD/RPM 429s)
  • Deploy: Cloudflare Workers via @opennextjs/cloudflare

Development

npm ci --legacy-peer-deps    # Install
npm run dev                  # Local dev server on :3000
npm run build:cf             # Build for Cloudflare Workers
npm run lint                 # ESLint

Direct deploy (skips CI):

npx wrangler deploy                                       # cf-blog
cd agent-worker && npm run build:cf && npx wrangler deploy   # cf-agent
cd cache-worker && npx wrangler deploy                    # cf-blog-cache

Upload API

POST /api/upload — Generate a presigned URL to upload directly to the public-files R2 bucket. Files are publicly accessible at https://bucket.krsz.in/<filename> (driven by BUCKET_URL in shared/site-config.ts).

# Get an upload link
curl -X POST https://blog.krsz.in/api/upload \
  -H "Authorization: Bearer <UPLOAD_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"filename":"avatar.png","contentType":"image/png"}'

# Response
# { "url": "...", "key": "avatar.png", "publicUrl": "https://bucket.krsz.in/avatar.png", "expiresIn": 300 }

# Upload directly to R2 (URL valid for 5 minutes)
curl -X PUT "<url>" -H "Content-Type: image/png" --data-binary @avatar.png

GET /api/upload — List files in the bucket

curl https://blog.krsz.in/api/upload \
  -H "Authorization: Bearer <UPLOAD_API_KEY>"

# Optional prefix filter
curl "https://blog.krsz.in/api/upload?prefix=images/" \
  -H "Authorization: Bearer <UPLOAD_API_KEY>"

AI Agent (cf-agent)

KurAgent supports three tools via the /api/tool and /api/chat endpoints:

  • @web_search <query> — Brave Search API
  • @get_time <timezone> — Current time in any IANA timezone
  • @eval_expression <code> — Safe JS expression evaluator (recursive-descent parser, no eval/Function)

Default model: gemma-4-31b-it. Streams via SSE. Max 5 tool-call iterations per turn.

Project Structure

.                         # cf-blog (main blog)
├── app/                  # Next.js App Router
├── components/           # React components (UI, activity, agent, llm, theme, layout, providers)
├── lib/                  # Backend logic (D1, articles, guestbook, model pool, languages)
├── database/             # D1 schema (schema.sql — all tables: posts, news_items, github_repos, etc.)
├── public/               # Static assets served by Next.js
├── agent-worker/         # cf-agent (separate Next.js worker)
├── cache-worker/         # cf-blog-cache (cron refresher)
└── AGENTS.md             # AI agent orientation

See Also

  • AGENTS.md — Multi-worker architecture, hero features, conventions, D1/R2 storage layout

About

Next.js blog + AI agent running on Cloudflare Workers with R2 storage

Resources

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages