AI-powered microsite builder for small nonprofits and community organizations
Launch a professional website in under 2 minutes with AI-generated branding, copy, and accessibility checks. Built with Next.js 14, FastAPI, Google Gemini, and Supabase.
Good Studio helps tiny nonprofits, mutual-aid pods, and community organizers create beautiful, accessible websites without hiring a developer. Just provide your mission statement and logo — our multi-agent AI system handles the rest, streaming real-time progress as it works.
AI Generation & Streaming:
- Multi-agent AI pipeline: 4 specialized agents (Brand, Copywriter, Accessibility, Developer) work together using Google Gemini
- Real-time SSE streaming: Watch agents work in real-time (not fake delays) via Server-Sent Events
- React component generation: Auto-generates custom React components with Tailwind CSS
- Smart fallback system: Never crashes - uses deterministic fallbacks if AI fails
Site Management:
- Visual site editing: Live content editor with drag-and-drop block system (text, images, flex layouts)
- Brand customization: Edit colors, fonts, and brand voice with visual color picker
- Inline text editing: Click any text to edit it directly on your site
- Content regeneration: Re-generate copy with different tone/audience settings
Event Management:
- Public events: Create events with title, date, location, capacity, and virtual options
- RSVP system: Built-in RSVP forms with email validation and capacity limits
- Rate limiting: IP-based rate limiting prevents spam RSVPs
- Dashboard analytics: View RSVP counts and attendee lists
Developer Experience:
- Type-safe API: Pydantic models with full validation
- Comprehensive tests: 100+ pytest tests with 90%+ coverage
- Row Level Security: Multi-tenant data isolation via Supabase RLS
- React Query integration: No useEffect for data fetching - modern async patterns
- Node.js 18+ (for Next.js frontend)
- Python 3.13+ (for FastAPI backend)
- PostgreSQL 14+ (via Supabase - free tier works)
- Google AI Studio account (for Gemini 2.5 Flash Lite API key - free tier: 15 RPM)
-
Clone the repo
git clone https://github.com/yourusername/good-studio.git cd good-studio -
Install dependencies
npm install # Backend dependencies (create virtual environment first) cd api python -m venv venv source venv/bin/activate # On Windows: venv\Scripts\activate pip install -r requirements.txt cd ..
-
Set up Supabase 📚
IMPORTANT: Follow the complete setup guide: SUPABASE_SETUP.md
This includes:
- Creating a Supabase project
- Configuring authentication (magic links)
- Setting up redirect URLs (critical for auth to work!)
- Running database migrations
- Enabling Row Level Security
- Creating storage bucket for logos
Quick version:
- Site URL:
http://localhost:3000 - Redirect URLs:
http://localhost:3000/**andhttp://localhost:3000/auth/callback - Run SQL schema from
docs/examples/fixtures.sql
-
Set up environment variables
Frontend (.env.local):
cp .env.local.example .env.local
Fill in:
NEXT_PUBLIC_SUPABASE_URL— from Supabase Settings → APINEXT_PUBLIC_SUPABASE_ANON_KEY— from Supabase Settings → API
Backend (api/.env.api):
cp api/.env.api.example api/.env.api
Fill in:
GEMINI_API_KEY=your-gemini-api-key-here SUPABASE_URL=https://yourproject.supabase.co SUPABASE_SERVICE_ROLE_KEY=your-service-role-key DATABASE_URL=postgresql://postgres:password@localhost:5432/good_studio ALLOWED_ORIGINS=http://localhost:3000,http://localhost:3001
-
Start dev servers
npm run dev
This runs both servers concurrently:
- Next.js frontend: http://localhost:3000
- FastAPI backend: http://localhost:8000
Or run them separately:
# Terminal 1 - Frontend npm run dev:web # Terminal 2 - Backend npm run dev:api
good-studio/
├── app/ # Next.js 14 App Router
│ ├── page.tsx # Landing page
│ ├── login/ # Login page
│ ├── signup/ # Signup page
│ ├── auth/callback/ # Supabase auth callback
│ ├── dashboard/ # Protected dashboard
│ │ ├── page.tsx # User dashboard with org list
│ │ └── org/[slug]/ # Org-specific dashboard
│ ├── new/ # Onboarding flow
│ │ └── page.tsx # 3-step wizard with SSE streaming
│ ├── orgs/ # Public org pages
│ │ └── [slug]/page.tsx # Live-editable org site
│ └── events/ # Event pages
│ ├── page.tsx # Event list
│ └── [id]/page.tsx # Event detail with RSVP
│
├── components/ # React components
│ ├── ui/ # Shadcn-style base components
│ ├── dashboard/ # Dashboard-specific
│ │ ├── OrgSettings.tsx # Brand & settings editor
│ │ ├── LiveContentEditor.tsx # Visual content block editor
│ │ ├── BrandEditor.tsx # Color/font picker
│ │ ├── ContentEditor.tsx # Text editing interface
│ │ ├── ContentBlocks.tsx # Block type definitions
│ │ ├── EventManager.tsx # Event CRUD
│ │ ├── CreateEventForm.tsx # Event creation form
│ │ └── RSVPList.tsx # RSVP viewer
│ ├── onboarding/
│ │ └── StepThreeSSE.tsx # SSE streaming UI
│ └── navbar.tsx # Main navigation
│
├── api/ # FastAPI backend
│ ├── main.py # FastAPI app with all routes
│ ├── sse_generator.py # SSE streaming endpoint
│ ├── multi_agent_system.py # LangGraph agent orchestration
│ ├── ai_generator.py # AI generation with fallback
│ ├── developer_agent.py # React component generator
│ ├── visual_agent.py # Image generation (deprecated)
│ ├── models.py # Pydantic models
│ ├── database.py # Database connection utilities
│ ├── rate_limiter.py # IP-based rate limiting
│ ├── agent_state.py # Agent state TypedDict
│ ├── requirements.txt # Python dependencies
│ └── tests/ # Pytest suite (100+ tests)
│ ├── test_*.py # Test files
│ └── conftest.py # Pytest fixtures
│
├── lib/ # Shared utilities
│ ├── supabase/ # Supabase clients
│ │ ├── client.ts # Browser client
│ │ └── server.ts # Server client
│ └── queries/ # React Query hooks
│ ├── orgs.ts # Org CRUD operations
│ └── events.ts # Event operations
│
├── hooks/ # Custom React hooks
│ └── useSSEGeneration.ts # SSE streaming hook
│
├── docs/ # Documentation
│ ├── index.md # Project overview
│ ├── requirements.md # User stories & database schema
│ ├── arch.md # Architecture deep dive
│ ├── build-order.md # 24-hour timeline
│ └── prompts/ # Implementation guides
│
└── scripts/ # Utility scripts
├── test_rls.sh # RLS validation
└── test_rls_simple.sh # Simple RLS test
- Project Overview — Problem statement, target audience, success metrics
- Requirements — User stories, data model, acceptance criteria
- Architecture — Multi-agent AI pipeline, data flow, security model
- Build Order — 24-hour hackathon timeline with smoke tests
- ADR-001: Stack Lock — Technology decisions and rationale
Frontend:
- Next.js 14 (App Router with Server Components)
- React 18.3+ (TypeScript)
- TailwindCSS 3.4
- TanStack React Query 5 (no useEffect for data fetching)
- React Hook Form + Zod (form validation)
- Lucide React (icons)
Backend:
- FastAPI 0.115+ (Python 3.13)
- Google Gemini 2.5 Flash Lite (15 RPM free tier)
- LangGraph (multi-agent orchestration)
- Pydantic v2 (data validation)
- SSE-Starlette (Server-Sent Events)
- psycopg2 (PostgreSQL driver)
Database & Infrastructure:
- Supabase Postgres 14+ (multi-tenant with RLS)
- Supabase Auth (email magic links)
- Supabase Storage (logo uploads)
- Row Level Security for data isolation
Testing & Quality:
- Pytest (100+ tests, 90%+ coverage)
- FastAPI TestClient
- Database fixtures for consistent testing
Four specialized AI agents work sequentially, streaming progress via SSE:
User Input (name, mission, logo, audience, tone)
↓
[SSE Stream Begins]
↓
🎨 Brand Agent (Google Gemini)
→ Analyzes mission and extracts brand personality
→ Generates 5-color palette (primary, secondary, accent, neutral, highlight)
→ Suggests font pairing (primary + accent fonts)
→ Defines brand voice (3-5 adjectives)
[SSE Event: agent_complete with brand data]
↓
✍️ Copywriter Agent (Google Gemini)
→ Writes hero headline (5-8 words)
→ Crafts hero subheadline (1-2 sentences)
→ Drafts about section (2-3 paragraphs)
→ Creates primary & secondary CTAs
[SSE Event: agent_complete with copy data]
↓
♿ Accessibility Agent
→ Generates descriptive logo alt-text
→ Validates WCAG AA contrast ratios
→ Checks reading level (target: 8th grade)
→ May trigger Brand Agent revision if contrast fails
[SSE Event: agent_complete with a11y data]
↓
💻 Developer Agent
→ Generates custom React components with Tailwind
→ Creates layout structure (Hero, About, CTA sections)
→ Builds Tailwind config with brand colors
[SSE Event: agent_complete with components]
↓
[SSE Event: workflow_complete with full site data]
↓
Frontend receives complete data → renders preview → saves to database
Key Innovation: Unlike fake progress bars, this uses actual Server-Sent Events to stream real-time updates as each agent completes its work. Total generation time: 5-15 seconds.
Step 1: Basic Info
- Enter organization name
- Write mission statement (2-3 sentences)
- Select primary audience (volunteers, donors, community members, etc.)
Step 2: Branding
- Upload logo (optional, 5MB max, stored in Supabase Storage)
- Choose tone (warm, professional, energetic, friendly, etc.)
Step 3: Generation & Preview
- Watch real-time SSE stream as 4 agents work (5-15 seconds total)
- Preview generated site with brand colors, fonts, and copy
- See generated React components and accessibility report
- Publish to
/orgs/[slug]
After publishing, users can edit their site visually:
Content Block System:
- Drag-and-drop blocks (text, images, flex containers)
- Live preview while editing
- Block types: markdown text, images with alt text, flex-row layouts
- Nested blocks for complex layouts
Brand Customization:
- Visual color picker for palette editing
- Font selection (Google Fonts)
- Real-time preview updates
- Re-generate content with new tone/audience
Event Creation:
- Title, description, date/time
- Location (physical address or virtual link)
- Capacity limits with automatic cutoff
- Public/private visibility
RSVP System:
- Public RSVP form with email validation
- Duplicate email prevention
- IP-based rate limiting (5 attempts per 15 minutes)
- Real-time capacity tracking ("12/50 spots filled")
Dashboard Analytics:
- List all events with RSVP counts
- View attendee lists (name, email, timestamp)
- Copy shareable event links
- Filter upcoming/past events
# All tests
npm test
# Backend tests (recommended)
npm run test:backend # All backend tests
npm run test:backend:unit # Unit tests only
npm run test:backend:integration # Integration tests only
npm run test:backend:rls # Row Level Security validation
# Or directly with pytest
cd api
pytest -v --tb=short # Verbose with short tracebacks
pytest test_ai_generator.py -v # Specific test file
pytest -k "test_rsvp" -v # Tests matching patternTest Coverage:
- 100+ tests across all backend modules
- 90%+ code coverage
- AI generation (with fallback testing)
- RSVP flow (capacity, duplicates, rate limiting)
- Row Level Security (RLS) validation
- Database constraints and triggers
Test with AI fallback (no API key required):
cd api
export TEST_AI_FALLBACK=true
pytest test_ai_generator.py -vTest with real Gemini API:
cd api
# Make sure GEMINI_API_KEY is in .env.api
pytest test_ai_generator.py -vnpm run lint # ESLint for TypeScript/React
# Backend uses type hints and Pydantic for validation# Via Supabase SQL Editor (recommended)
# Copy SQL from docs/requirements.md and run in Supabase dashboard
# Or via command line (if DATABASE_URL is set)
npm run db:migrate- Push to GitHub
- Import repo in Vercel
- Add environment variables
- Deploy
- Create new web service
- Set build command:
pip install -r api/requirements.txt - Set start command:
uvicorn api.main:app --host 0.0.0.0 --port $PORT - Add
GEMINI_API_KEYenvironment variable - Deploy
GET /- API health checkGET /health- Detailed health statusGET /api/events?org={slug}- List public events for an organizationGET /api/events/{id}- Get single event detailsPOST /api/rsvp- Create RSVP for an event (rate limited)POST /events/{id}/rsvps- RESTful RSVP endpoint
POST /generate-site- Generate site with AI (with fallback)POST /generate-site-stream- SSE streaming endpoint for real-time progressPOST /generate-site-complete- Full multi-agent pipeline with componentsPUT /api/orgs/{id}- Update org details (name, mission, logo)PUT /api/orgs/{id}/brand- Update brand colors and fontsPUT /api/orgs/{id}/content- Update content blocks (visual editor)POST /api/orgs/{id}/regenerate- Regenerate content with new tone/audience
- RSVP endpoints: 5 requests per 15 minutes per IP+event
- Uses in-memory rate limiter with sliding window
- Row Level Security (RLS) policies enforce data isolation
- Each org has a single owner (no multi-user orgs yet)
- Public pages readable by all; dashboard requires authentication
- Service role key used for server-side operations only
- Never crashes - if AI fails, uses deterministic fallback data
- Environment variable
TEST_AI_FALLBACK=trueforces fallback mode - Fallback data is production-quality, not lorem ipsum
- Useful for development without API key or rate limit testing
- Uses
sse-starlettefor Server-Sent Events - Frontend connects via
fetch()withReadableStream - Events:
agent_start,agent_complete,workflow_complete,error - Automatic reconnection and error handling
- No polling required - real-time updates
This was built as a hackathon project. Contributions welcome!
- ✅ Multi-agent AI generation with SSE streaming
- ✅ Real-time progress updates (not fake delays)
- ✅ React component code generation
- ✅ Visual content block editor
- ✅ Brand customization (colors, fonts)
- ✅ Event management with RSVP system
- ✅ Rate limiting and spam prevention
- ✅ Row Level Security (RLS)
- ✅ 100+ tests with 90%+ coverage
- ✅ AI fallback system
- Multi-page sites (About, Contact, etc.)
- Image generation (HuggingFace integration - currently deprecated)
- Email notifications (Resend integration)
- Private events with join codes
- Team member roles (multi-user orgs)
- Custom domains
- Donation integration (Stripe)
- Analytics dashboard (page views, RSVP trends)
- A/B testing for copy variations
- SEO optimization tools
See docs/build-order.md for detailed implementation timeline.
If you see "Failed to fetch" errors in the frontend:
# Check if backend is running
curl http://localhost:8000
# If not, start it
cd api
source venv/bin/activate
uvicorn main:app --reload --port 8000# Test database connection
psql $DATABASE_URL -c "SELECT 1"
# Check if tables exist
psql $DATABASE_URL -c "\dt"If AI generation times out or fails:
# Use fallback mode (no API key needed)
cd api
export TEST_AI_FALLBACK=true
uvicorn main:app --reload# Find process using port 8000
lsof -i :8000
# Kill it
pkill -f uvicorn
# Or use a different port
uvicorn main:app --reload --port 8001Free tier: 15 requests per minute
- Wait 1 minute between requests
- Or use
TEST_AI_FALLBACK=truefor unlimited testing
MIT License - feel free to use this for your own projects!
- Documentation: See docs/ folder for detailed guides
- Requirements: docs/requirements.md - User stories & database schema
- Architecture: docs/arch.md - System design deep dive
- Build Timeline: docs/build-order.md - 24-hour implementation plan
- Supabase Setup: SUPABASE_SETUP.md - Complete database setup guide
Built for HackNC 2025 using:
- Next.js by Vercel
- FastAPI by Sebastián Ramírez
- Google Gemini AI models
- Supabase for database, auth, and storage
- TailwindCSS for styling
- TanStack Query for data fetching