Back to DevLog

Building a Blog Admin Panel with AI-Powered Social Media Generation

3 min read

Had a solid development session today working on two main projects - building out a blog admin interface and squashing some pesky bugs in AdminStack.

The Main Event: Blog Admin Panel

I spent most of my time building a full admin interface for my affiliate investments blog at /admin/blog. The setup is pretty straightforward - cookie-based auth using a BLOG_ADMIN_KEY that keeps things simple and portable for Netlify deployment.

The admin panel shows all blog posts (published, queued, and drafts) with nice status badges. But the cool part is the "Generate Social Posts" button that calls the Claude API server-side to automatically create Facebook, LinkedIn, and X/Twitter posts from the blog content. Each platform gets its own branded card with copy buttons that track when content gets copied to the database.

I'm using the claude-sonnet-4-20250514 model for cost efficiency - it's perfect for generating social media copy without breaking the bank. The generated posts get persisted so we don't regenerate the same content over and over.

For the database side, I created a new social_posts table with RLS enabled but no anonymous policies, which effectively makes it admin-only access. Clean and secure.

I also added a "Publish Now" button for queued posts - just a simple teal button next to the status badge that calls the publish API with inline success/error feedback. Sometimes the simple features are the most satisfying to build.

AdminStack Bug Hunting

On the AdminStack side, I tackled two annoying bugs that had been bothering me:

First was a modal that wouldn't close after canceling an order. The issue was that the modal close logic was happening after a refetch that could potentially fail. Simple fix - just moved the cleanup before the refetch call.

The second bug was trickier - order deletion was throwing internal server errors because of a foreign key constraint. The email_sends table had an order_id FK to orders without ON DELETE CASCADE. Rather than alter the table structure, I went with the safer approach of explicitly deleting related email_sends records before deleting the order.

Technical Decisions

A few key decisions I made today:

  • Sticking with cookie-based auth instead of Supabase Auth for simplicity
  • Using RLS with no policies for admin-only tables (service_role bypasses anyway)
  • Always closing modals immediately after API success, then wrapping secondary operations like refetches in separate try/catch blocks
  • Explicit cleanup of related records rather than adding CASCADE constraints

What's Next

I need to run the migration in Supabase and add the Anthropic API key to Netlify before the social generation feature will work in production. But everything's committed and clean, ready for the next session.

Both projects are in a good state with no uncommitted work hanging around. Sometimes the best coding sessions are the ones where you build something useful and fix the stuff that's been nagging at you.

Share this post