Multi-site AI content pipeline. Same features as the original Flask version, rebuilt as a Next.js 15 app deployable to Vercel.
- Manage unlimited WordPress sites from one dashboard, each with its own niche, audience, expert voice, author bio, daily cap, and minimum word count
- AI keyword research — paste a seed term, Claude proposes 30+ long-tail candidates with intent tags
- Article generator — Claude Sonnet 4.6 writes the post, embeds Article + FAQPage JSON-LD schema, assigns category + tags, internally links to past articles on the same site
- Quality gates — articles below the site's minimum word count or missing FAQ are held back as drafts for review
- Article preview, edit, and manual publish — review HTML in the dashboard before pushing to WordPress
- Activity log + cost tracking — every run recorded with token cost; per-site monthly spend visible
- Auth — credentials-based login, first-launch setup, encrypted WP application passwords at rest
- Vercel Cron — daily run across all active sites, respects per-site daily cap
# 1. Install dependencies
npm install
# 2. Provision a Postgres database (Neon free tier is easiest)
# Sign up at https://neon.tech, create a project, copy the connection string.
# 3. Generate auth + encryption secrets
node -e "console.log('AUTH_SECRET=' + require('crypto').randomBytes(32).toString('base64'))"
node -e "console.log('ENCRYPTION_KEY=' + require('crypto').randomBytes(32).toString('base64'))"
# 4. Configure environment
cp .env.example .env
# Edit .env: paste DATABASE_URL, DIRECT_DATABASE_URL, AUTH_SECRET, ENCRYPTION_KEY,
# ANTHROPIC_API_KEY. CRON_SECRET is optional for local dev.
# 5. Push the schema to your database
npx prisma db push
# 6. Run the dev server
npm run devOpen http://localhost:3000 and follow the first-run setup to create the admin account.
-
Provision Postgres. Sign up for Neon (or use Vercel Postgres). Create a project. Copy the pooled connection string (used as
DATABASE_URL) and the direct connection string (used asDIRECT_DATABASE_URL). -
Push code to a Git repo (GitHub/GitLab/Bitbucket).
-
Import the repo to Vercel. New Project → Import.
-
Set environment variables in Vercel Project Settings → Environment Variables:
Variable How to get it DATABASE_URLNeon pooled URL (with ?pgbouncer=true&connect_timeout=15)DIRECT_DATABASE_URLNeon direct URL (no pooler) — Prisma uses it for migrations AUTH_SECRETopenssl rand -base64 32ENCRYPTION_KEYopenssl rand -base64 32(32 bytes, encrypts WP passwords)ANTHROPIC_API_KEYhttps://console.anthropic.com/settings/keys CRON_SECRETAny random string. Required if you use Vercel Cron. Vercel sends it as Authorization: Bearer $CRON_SECRET.Optional (for social posting):
X_API_KEY,X_API_SECRET,X_ACCESS_TOKEN,X_ACCESS_SECRET,REDDIT_CLIENT_ID,REDDIT_CLIENT_SECRET,REDDIT_USERNAME,REDDIT_PASSWORD,REDDIT_USER_AGENT. -
Push the Prisma schema. Locally with the production
DATABASE_URL/DIRECT_DATABASE_URLset:npx prisma db push
(or run a one-off
prisma migrate deployjob if you start using migrations). -
Deploy. Vercel auto-deploys from
main. The first deploy spins up the cron job at/api/cron, scheduled daily at 09:00 UTC (seevercel.json).
- Visit your
*.vercel.appURL. You'll be redirected to/setupto create the admin account. - Add your first site. Test the WordPress connection. Add keywords. Click Run to generate one article.
- The cron job will trigger one article per active site daily, capped per site.
- To run manually outside the dashboard:
curl -H "Authorization: Bearer $CRON_SECRET" https://yoursite.vercel.app/api/cron
- Vercel Hobby plan — free, 60s function timeout (enough for one article generation per request).
- Neon free tier — 0.5 GB storage, plenty for tens of thousands of articles.
- Anthropic — ~$0.05–$0.15 per article (Sonnet 4.6). 4 sites × 1 article/day × 30 days ≈ $6–$18/month.
- One article per request. Article generation takes 20–40s; the runner does one per call to fit Vercel's 60s timeout.
- Multi-article runs. The dashboard's Run widget loops on the client, calling the action
counttimes in sequence. - Daily cap. Enforced inside
runOneForSite; cron + dashboard both honor it. - WP passwords at rest. AES-256-GCM with a key derived from
ENCRYPTION_KEY. Never stored or logged in plaintext. - Auth. Auth.js v5 (NextAuth) with a Credentials provider, JWT sessions (14-day lifetime). Middleware protects every route except
/login,/setup,/api/auth, and/api/cron. - Internal linking. When publishing, scans the new article HTML against past article titles on the same site, inserts up to 5 links to the longest-matching titles, skips matches inside headings or existing anchors.
prisma/schema.prisma Database schema
src/lib/db.ts Prisma singleton
src/lib/auth.ts Auth.js config + helpers
src/lib/encryption.ts AES-256-GCM for WP passwords
src/lib/anthropic.ts Article generator + keyword research
src/lib/wordpress.ts WP REST publisher with category/tag taxonomy
src/lib/linker.ts Internal linker
src/lib/social.ts X + Reddit (env-gated)
src/lib/runner.ts Per-site orchestration + quality gates
src/middleware.ts Auth gate for protected routes
src/app/(auth)/ Login + setup pages
src/app/(app)/ Authenticated dashboard pages
src/app/api/research/ Keyword research endpoint
src/app/api/sites/[id]/keywords/ Bulk add keywords
src/app/api/cron/ Vercel cron entry point
src/components/ UI components (Sidebar, Card, Pill, ...)
src/actions/ Server actions for forms
vercel.json Cron schedule
- SQLite → Postgres
- WP passwords now encrypted at rest (Flask version stored plaintext)
- "Run N articles" loops client-side (Vercel timeouts make in-process loops risky)
- Dry-run output stored in DB instead of files (no persistent FS on Vercel)
- Background social posting moved to env-gated; X/Reddit integrations are stubbed in v1 — wire up
twitter-api-v2andsnoowrapif you need them - Schedule is now Vercel Cron at 09:00 UTC (configurable in
vercel.json)