Skip to content

achalbajpai/thecontentmind

Repository files navigation

thecontentmind

an ai social-media content studio. pick a platform, set tone, audience, and goal, hit generate, and watch three caption variants stream in token-by-token. save the ones you like to a library and "more like this" from there.

runs fully locally: postgres in docker, email/password auth, no cloud accounts.

product snapshots

TheContentMind screenshot TheContentMind screenshot
TheContentMind screenshot TheContentMind screenshot
TheContentMind screenshot TheContentMind screenshot
TheContentMind screenshot TheContentMind screenshot

what it does

  • generates platform-tuned captions with per-platform prompt rules
  • streams three variants live per request
  • counts one quota unit per generate action, not per variant
  • "more like this" reuses the original tone, audience, and goal

how it's put together

  • framework - tanstack start (vite, file-based routes, server functions, server routes)
  • data - postgres 17 + drizzle orm over a single shared pg pool
  • auth - better auth, email/password plus optional google oauth
  • ai - nebius ai studio (qwen3) through the openai sdk, optional

routes live in src/routes; /api/* are server routes that return plain web Responses. all business logic sits in src/server/ as framework-agnostic plain functions, so the route handlers are thin shells. protected /api/* routes call requireApiUser() and return the same 401 envelope when there's no session, while public routes like health and auth stay explicit.

running it

docker compose up -d           # local postgres
cp .env.example .env           # set BETTER_AUTH_SECRET (openssl rand -base64 32)
pnpm install
pnpm db:push                   # drizzle schema -> postgres
pnpm db:seed                   # demo user + sample content
pnpm dev                       # vite on :3000

only DATABASE_URL and BETTER_AUTH_SECRET are required. set NEBIUS_API_KEY or GROQ_API_KEY to enable generation; with neither, endpoints return a labeled AI_UNAVAILABLE envelope instead of a 500. pnpm db:seed prints demo credentials to the console.

engineering worth a look

  • a full framework cutover done in stages, not a rewrite (src/routes, src/server). the app moved off next.js app router onto tanstack start while staying shippable at every step: database first, then auth, then a framework-agnostic service layer, then the framework swap, then tooling. business logic was extracted into src/server/* plain functions before the move so the route handlers became disposable.

  • streaming with reasoning-block filtering (src/server/ai.ts → createChatStream, src/routes/api/generate-caption.ts). qwen emits <think> blocks; naive token passthrough leaks them. the stream buffers just until it knows whether it's inside a think block, drops it, then streams the rest straight through - first real token stays sub-second.

  • three variants, one quota unit (src/routes/api/generate-caption.ts, src/server/usage.ts). one user action fires three parallel streamed calls with temperature jitter; only the first carries trackUsage, so the quota check and increment run once. quotas are computed from actual content/user_activity rows for the day, not a mutable counter.

  • a typed service layer with a standard auth/error envelope (src/server/api-auth.ts, src/server/errors.ts). protected handlers call requireApiUser() before touching user-owned data, and errors return { error: { code, message } } with codes like QUOTA_EXCEEDED, UNAUTHORIZED, AI_UNAVAILABLE, VALIDATION_ERROR. the client reads message straight into a toast.

  • content library on composable drizzle queries (src/server/content.ts, src/routes/api/content/). platform/status/type/date filters and ilike search compose a single where, paginate with limit/offset, and "more like this" rehydrates the original generation inputs.

  • better auth on drizzle with additional fields (src/lib/auth.ts, src/lib/get-session.ts). email/password and conditional google, with app fields (subscription, isActive, lastLoginAt) declared as better-auth additional fields so the session carries them directly - no email-then-lookup round trip. the seed creates the demo user through auth.api.signUpEmail so the scrypt hash is real.

  • db-backed rate limiting (src/server/rate-limit.ts). a postgres sliding window via an atomic upsert, so it survives a restart and doesn't need redis.

About

🧠 AI social content studio[Jan 2025]. Pick a platform, set your tone, and watch three caption variants stream in real-time. winner of colega ai x nosu hack jan 2025.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors