A self-hosted social media scheduling application for scheduling and publishing content to X/Twitter and LinkedIn. Built with Next.js, Convex, and Better Auth as a single-user productivity tool to replace expensive subscription services.
- Multi-Platform Publishing - Schedule posts to X/Twitter and LinkedIn with platform-specific content
- Twitter Thread Support - Create and schedule multi-tweet threads (up to 25 tweets) with drag-and-drop reordering
- AI Content Assistant - Google Gemini-powered features for content creation:
- Tone adjustment (professional, casual, engaging, formal)
- Twitter-to-LinkedIn expansion (auto-expand short tweets to LinkedIn posts)
- Smart hashtag generation (platform-specific, relevant hashtags)
- Intelligent Posting Time Recommendations - Research-based optimal posting times with custom preference overrides
- Staggered Scheduling - Set different publish times for each platform
- Template System - Create and reuse content templates with tags
- URL Auto-Posting - Automatically post URLs as replies/comments after main content
- Smart Retry Logic - Automatic retry with exponential backoff for failed posts
- Failure Notifications - Telegram notifications when posts fail after all retries
- Token Management - Automatic LinkedIn and Twitter token refresh
- Encrypted Storage - AES-256-GCM encryption for OAuth tokens
- Real-Time Updates - Reactive UI updates with Convex subscriptions
- Mobile Responsive - Optimized for scheduling on the go
- Analytics Dashboard - Track published, scheduled, and failed posts
- Usage Tracking & Cost Management - AI request rate limiting and cost estimation
Master modern full-stack development with AI-powered tools and techniques
✨ What You'll Learn:
🚀 I've taught over 50,000 developers to date.
🎯 Top 1% TypeScript engineers globally on GitHub.
🤖 Learn how to use AI coding agents like Claude Code effectively
SocialPost was built using the AI Starter Kit - a production-ready foundation for building full-stack applications with modern tools.
What the Starter Kit Provides:
⚡ Next.js 16 with App Router and React 19
🔄 Convex real-time database with zero infrastructure
🔐 Better Auth authentication (email/password + OAuth ready)
🎨 20+ pre-installed shadcn/ui components
✅ Complete testing setup with Vitest
📚 Comprehensive documentation and examples
🤖 Optimized for AI coding agents like Claude Code
Perfect for rapidly building: SaaS apps, dashboards, real-time tools, and production applications
- Quick Start
- Prerequisites
- Installation
- Configuration
- Development
- Architecture
- Documentation
- Testing
- Deployment
- Troubleshooting
- Contributing
- License
# Clone the repository
git clone https://github.com/yourusername/social_post.git
cd social_post
# Install dependencies
pnpm install
# Set up environment variables (see Configuration section)
cp .env.example .env.local
# Start development servers
pnpm run devVisit http://localhost:3000 to see your app.
- Node.js 20+ and pnpm 8+
- Convex Account - Sign up at convex.dev
- Better Auth Setup - Configured with Convex
- X/Twitter Developer Account - Apply at developer.twitter.com
- LinkedIn Developer Account - Apply at developer.linkedin.com
- Google Gemini API Key (optional) - Get from Google AI Studio for AI content features
- Telegram Bot (optional) - Create via @BotFather
git clone https://github.com/yourusername/social_post.git
cd social_post
pnpm install# Initialize Convex development deployment
pnpm dlx convex dev
# Follow the prompts to:
# - Log in to your Convex account
# - Create a new project or select existing one
# - Link your local code to the deployment-
Better Auth is already configured with Convex in this project
-
Configure Better Auth environment variables in your Convex deployment:
# Follow the Better Auth + Convex setup guide: # https://www.better-auth.com/docs
-
Add required environment variables (see Configuration section below)
X/Twitter OAuth 2.0:
- Create an app at developer.twitter.com
- Enable OAuth 2.0 with these scopes:
tweet.readtweet.writeusers.readoffline.access
- Add callback URL:
http://localhost:3000/api/auth/twitter/callback
LinkedIn OAuth 2.0:
- Create an app at linkedin.com/developers
- Request these permissions:
openidprofilew_member_social
- Add callback URL:
http://localhost:3000/api/auth/linkedin/callback
Create .env.local for Next.js:
# Better Auth
BETTER_AUTH_SECRET=your_secret_key
BETTER_AUTH_URL=http://localhost:3000
# Convex
NEXT_PUBLIC_CONVEX_URL=https://your-deployment.convex.cloud
# OAuth Callback Base URL
NEXT_PUBLIC_BASE_URL=http://localhost:3000
# Single-User Mode (optional - set to "true" to disable new signups)
DISABLE_SIGNUPS=false
NEXT_PUBLIC_DISABLE_SIGNUPS=falseAdd to Convex Dashboard (Settings → Environment Variables):
# Better Auth Configuration
BETTER_AUTH_SECRET=your_secret_key
BETTER_AUTH_URL=http://localhost:3000
# X/Twitter OAuth
TWITTER_CLIENT_ID=your_twitter_client_id
TWITTER_CLIENT_SECRET=your_twitter_client_secret
# LinkedIn OAuth
LINKEDIN_CLIENT_ID=your_linkedin_client_id
LINKEDIN_CLIENT_SECRET=your_linkedin_client_secret
# Encryption (generate with: openssl rand -base64 32)
ENCRYPTION_KEY=your_base64_encoded_32_byte_key
# Telegram Notifications (optional)
TELEGRAM_BOT_TOKEN=your_bot_token
TELEGRAM_CHAT_ID=your_chat_id
# Google Gemini AI (optional - for AI content features)
GEMINI_API_KEY=your_gemini_api_key
# AI Rate Limiting (optional - defaults shown)
AI_DAILY_RATE_LIMIT=1000 # Daily AI request limit
AI_MONTHLY_RATE_LIMIT=20000 # Monthly AI request limit
AI_WARNING_THRESHOLD_PCT=90 # Warning at 90% of limit
# Performance Tracking (optional - for posting time recommendations)
PERFORMANCE_TRACKING_ENABLED=true # Enable historical performance tracking
# Single-User Mode (optional - set to "true" to disable new signups)
DISABLE_SIGNUPS=false# Generate a secure 32-byte key and encode as base64
openssl rand -base64 32Copy the output to ENCRYPTION_KEY in Convex environment variables.
By default, the application allows multiple users to sign up. If you want to restrict your deployment to a single user (yourself), you can enable single-user mode:
How it works:
- Set
DISABLE_SIGNUPS=trueandNEXT_PUBLIC_DISABLE_SIGNUPS=truein your environment variables - The signup API endpoint will reject new registration attempts
- The signup page will display a "Signups Closed" message
- The "Sign up" link on the login page will be hidden
- Existing users can continue to log in normally
To enable:
-
For production deployments, add to your
.env.productionor Vercel environment variables:DISABLE_SIGNUPS=true NEXT_PUBLIC_DISABLE_SIGNUPS=true
-
For Convex production, add to Convex Dashboard → Production → Settings → Environment Variables:
DISABLE_SIGNUPS=true
-
Create your account first, then enable single-user mode to lock it down
Use case: This is perfect if you're deploying SocialPost as a personal productivity tool and want to ensure no one else can create an account on your instance.
# Start both frontend and backend in parallel
pnpm run dev
# Start frontend only (Next.js dev server)
pnpm run dev:frontend
# Start backend only (Convex dev server)
pnpm run dev:backend
# Open Convex dashboard
pnpm dlx convex dashboard
# Build for production
pnpm run build
# Start production server
pnpm run start
# Run linter
pnpm run lint
# Run tests
pnpm run test
# Run tests in watch mode
pnpm run test:watch
# Generate test coverage
pnpm run test:coveragesocial_post/
├── app/ # Next.js App Router pages
│ ├── layout.tsx # Root layout with providers
│ ├── page.tsx # Home page
│ ├── dashboard/ # Dashboard page
│ ├── schedule/ # Post scheduling page
│ ├── history/ # Post history page
│ ├── settings/ # Settings page (OAuth + posting preferences)
│ ├── templates/ # Template library page
│ └── api/ # API routes for OAuth callbacks
├── components/ # React components
│ ├── ui/ # shadcn/ui components
│ └── features/ # Feature-specific components
│ ├── TwitterThreadComposer.tsx # Thread creation UI
│ ├── TwitterThreadPreview.tsx # Thread preview
│ ├── AIAssistantButton.tsx # AI feature access
│ ├── AISuggestionPanel.tsx # AI content suggestions
│ ├── HashtagSuggestionPanel.tsx # Hashtag insertion UI
│ └── RecommendedTimes.tsx # Posting time suggestions
├── convex/ # Convex backend
│ ├── schema.ts # Database schema
│ ├── posts.ts # Post mutations & queries (thread support)
│ ├── publishing.ts # Publishing actions (thread publishing)
│ ├── connections.ts # OAuth connections
│ ├── templates.ts # Template CRUD
│ ├── dashboard.ts # Dashboard queries
│ ├── encryption.ts # Token encryption
│ ├── tokenRefresh.ts # Twitter & LinkedIn token refresh
│ ├── notifications.ts # Telegram notifications
│ ├── aiAssistant.ts # AI feature actions
│ ├── gemini.ts # Gemini API integration utilities
│ ├── aiUsageTracking.ts # AI usage tracking & rate limiting
│ ├── aiFeedback.ts # User feedback collection
│ ├── postingRecommendations.ts # Posting time recommendations
│ └── postingPreferences.ts # User preference overrides
├── docs/ # Documentation
│ ├── setup/ # Setup guides (Gemini, OAuth, etc.)
│ └── archive/ # Completed user stories
├── hooks/ # Custom React hooks
├── lib/ # Utility functions
│ └── utils/
│ └── characterCount.ts # Twitter/LinkedIn character counting
└── middleware.ts # Better Auth route protection
- Frontend: Next.js 15.5.4 (App Router), React 19, Tailwind CSS 4
- Backend: Convex (database, queries, mutations, actions)
- Authentication: Better Auth (single-user auth)
- UI Components: shadcn/ui
- Language: TypeScript throughout
Convex Backend Philosophy:
All backend logic lives in convex/. Convex provides:
- Queries - Read data reactively (realtime updates)
- Mutations - Write data transactionally
- Actions - Call external APIs (X, LinkedIn, Telegram) with Node.js runtime
- Scheduled Functions - Critical for timed post publishing
Scheduled Publishing Architecture:
User creates post → Convex mutation → ctx.scheduler.runAt(scheduledTime)
→ Convex Action publishes to X/LinkedIn → Update post status
→ On failure: Retry logic → Telegram notification
Security:
- OAuth tokens encrypted with AES-256-GCM before database storage
- All Convex functions verify user authentication
- Data scoped to
userIdfor single-user isolation
posts - Stores scheduled and published content
- Fields:
status,twitterContent,twitterThread,linkedInContent,scheduledTimes,url,twitterPostIds,errorMessage,retryCount - Supports both single tweets (
twitterContent) and threads (twitterThreadarray) - Index:
by_useron[userId]
user_connections - Stores encrypted OAuth tokens
- Fields:
platform,accessToken,refreshToken,expiresAt - Index:
by_user_platformon[userId, platform]
templates - Stores reusable content templates
- Fields:
name,content,tags,lastUsedAt,usageCount - Index:
by_useron[userId]
ai_usage_logs - Tracks AI API usage and costs
- Fields:
userId,timestamp,feature,tokensUsed,cost,modelUsed,requestId,duration,success - Indexes:
by_user,by_timestamp,by_user_timestamp,by_feature
ai_feedback - User feedback on AI-generated content
- Fields:
userId,feature,requestId,originalContent,aiResponse,feedbackType,feedbackText,timestamp - Indexes:
by_user,by_feature,by_timestamp
posting_time_recommendations - Research-based optimal posting times
- Fields:
platform,dayOfWeek,hourStart,hourEnd,engagementScore,timeZone(stored in UTC) - Index:
by_platform_dayon[platform, dayOfWeek]
posting_preferences - User custom posting time preferences
- Fields:
userId,platform,dayOfWeek,hourStart,hourEnd,engagementScore,timeZone(stored in local timezone) - Index:
by_user_platform_dayon[userId, platform, dayOfWeek]
post_performance - Historical engagement metrics (feature-gated)
- Fields:
postId,userId,platform,likes,shares,comments,impressions,timestamp - Index:
by_poston[postId]
Comprehensive documentation is available in the docs/ directory:
- API Reference - Complete Convex function reference
- Architecture Diagrams - Visual system architecture
- User Guide - Step-by-step usage instructions
- Developer Guide - Setup and contribution guide
- Deployment Guide - Production deployment instructions
- Architecture Document - Technical architecture details
- Product Requirements - Product requirements document
- Gemini Setup Guide - Google Gemini API configuration
SocialPost includes AI-powered content creation features using Google Gemini 1.5 Flash:
1. Tone Adjustment
- Rewrite content in different tones: professional, casual, engaging, or formal
- Preserves meaning while adjusting voice and style
- Respects platform character limits (280 for Twitter, 3,000 for LinkedIn)
2. Twitter-to-LinkedIn Expansion
- Automatically expand short Twitter content into longer LinkedIn posts
- Targets 500-1,000 characters for LinkedIn
- Preserves URLs, hashtags, and @mentions from original
- Only available when Twitter has content and LinkedIn is empty/shorter
3. Smart Hashtag Generation
- Generate 3-5 relevant hashtags based on post content
- Platform-aware: short/casual for Twitter, professional/descriptive for LinkedIn
- Click individual hashtags to insert at cursor position or "Insert All"
- Character limit validation prevents over-limit insertion
Rate Limits:
- Daily: 1,000 requests (configurable via
AI_DAILY_RATE_LIMIT) - Monthly: 20,000 requests (configurable via
AI_MONTHLY_RATE_LIMIT) - Warning displayed at 90% of limit (configurable via
AI_WARNING_THRESHOLD_PCT)
Cost Estimation:
- Uses Gemini 1.5 Flash model
- Pricing (paid tier): $0.075/1M input tokens, $0.30/1M output tokens
- Typical cost per request: $0.000023 - $0.000065
- All requests tracked in
ai_usage_logstable with token counts and costs
User Feedback:
- Report issues with AI-generated content via "Report Issue" button
- Feedback categories: inappropriate, low-quality, other
- Helps improve AI feature quality over time
- Click the AI Assistant button (sparkles icon) in the post composer
- Select a feature from the menu
- Review the AI-generated suggestion in a side-by-side panel
- Accept, Reject, or Edit the suggestion before applying
Create and schedule multi-tweet threads with advanced composition tools:
Composer Features:
- Toggle "Thread Mode" to switch from single tweet to thread composition
- Add up to 25 tweets (Twitter API limit)
- Drag-and-drop reordering of tweets in the thread
- Alternative move up/down buttons for reordering
- Individual character counters per tweet (280 char limit each)
- Numbered badges showing tweet position in thread
- Visual warnings for empty tweets (auto-removed on scheduling)
- Red borders for tweets exceeding character limit
Thread Preview:
- Live preview showing how thread will appear on Twitter
- Visual thread connector lines between tweets
- Character count display per tweet with color-coded warnings
- Simulated Twitter UI with avatar, handle, timestamp
- Thread info showing URL will be posted as final reply
- Empty tweets filtered from preview
Publishing Flow:
- First tweet published to your timeline
- Subsequent tweets posted as replies, creating thread continuity
- Optional URL posted as final reply (if provided)
- All tweet IDs stored in
twitterPostIdsarray for tracking
Character Counting:
- Each tweet: 280 character maximum (strict validation)
- URLs automatically counted as 23 characters (Twitter's t.co shortening)
- Emoji counted properly (most count as 2 characters)
- Real-time character counter with warnings at 260 chars
Error Handling:
- Automatic retry with exponential backoff for transient errors
- Token refresh before publishing if expiring soon
- Individual tweet failures don't fail entire thread
- Detailed error messages for troubleshooting
Backward Compatibility:
- Existing single tweets stored in
twitterContentfield - New threads stored in
twitterThreadarray field - Publishing logic checks for thread first, falls back to single tweet
- Edit existing posts to convert between single tweet and thread
Get data-driven suggestions for optimal posting times based on research and your preferences:
Research-Based Recommendations:
- Pre-loaded with best practices from social media research
- Twitter: Tuesday-Thursday 9-11am EST recommended
- LinkedIn: Wednesday 10am-12pm EST for maximum engagement
- All times stored in UTC, displayed in your local timezone
- Top 3 time suggestions shown with engagement level indicators
Custom Preferences:
- Override research-based times with your own preferred posting windows
- Set custom time ranges per platform and day of week
- Custom preferences prioritized (95 engagement score) over research data
- Manage preferences at
/settings/preferences - Side-by-side comparison of custom vs default recommendations
Conflict Detection:
- Shows when suggested times overlap with already-scheduled posts
- Helps avoid posting multiple times in short succession
- Visual indicators for conflicting time slots
Historical Performance Integration (Feature-Gated):
- Track engagement metrics (likes, shares, comments) from published posts
- Algorithm blends research data (60%) with your historical performance (40%)
- Requires Twitter/LinkedIn analytics API access
- Enable via
PERFORMANCE_TRACKING_ENABLEDenvironment variable
- Open post scheduler and select a date
- View "Recommended Times" section below date picker
- See top 3 suggestions with engagement levels (High/Good/Moderate/Low)
- Click a suggestion to auto-fill the posting time
- Customize preferences in Settings to override defaults
# Run all tests
pnpm run test
# Watch mode
pnpm run test:watch
# With coverage
pnpm run test:coverage- Unit Tests: Character counting, validation logic, timestamp conversion
- Integration Tests: Convex queries/mutations with auth, post creation flow
- Component Tests: React components with Convex hooks
- Action Tests: Mock external APIs, test retry logic
# Install Vercel CLI
pnpm i -g vercel
# Deploy to production
vercel --prodEnvironment Variables:
Add all .env.local variables to Vercel project settings.
# Create production deployment
pnpm dlx convex deploy
# Set production environment variables
# Via Convex Dashboard → Production → Settings → Environment VariablesPost-Deployment:
- Update OAuth callback URLs to production domain
- Test OAuth flows in production
- Verify scheduled posts are publishing
- Set up monitoring and alerting
- Environment variables set in Vercel and Convex
- OAuth apps configured with production callback URLs
- Encryption key generated and set
- Telegram bot configured (optional)
- Better Auth production instance configured
- DNS records pointed to Vercel
- SSL certificate active
- Error tracking configured
- Backup strategy implemented
Posts Not Publishing:
- Check Convex logs in dashboard
- Verify OAuth tokens are not expired (check Settings page)
- Check scheduled function status in Convex dashboard
- Review error messages in Post History
OAuth Connection Fails:
- Verify callback URLs match exactly
- Check client ID and secret in Convex environment variables
- Ensure scopes are correctly configured
- Check browser console for errors
Encryption Errors:
-
Verify
ENCRYPTION_KEYis set in Convex environment variables -
Ensure key is exactly 32 bytes (base64-encoded)
-
Run migration if upgrading from plain-text tokens:
// In Convex dashboard console await ctx.runAction(internal.encryption.migrateTokensToEncrypted, { dryRun: true, // Test first });
Rate Limiting:
- X/Twitter: Space out posts, retry logic handles 429 errors
- LinkedIn: Built-in retry with exponential backoff
Enable verbose logging:
# In convex function code
console.log('[Debug]', { postId, status, error });View logs in Convex Dashboard → Logs.
Contributions are welcome! This is a personal productivity tool, but improvements benefit everyone.
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Run tests:
pnpm run test - Run linter:
pnpm run lint - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature - Open a Pull Request
- Use TypeScript for type safety
- Follow existing code style
- Add tests for new features
- Update documentation
- Use conventional commits
- All PRs require review
- Tests must pass
- No linting errors
- Documentation updated
- Twitter thread support (up to 25 tweets)
- AI content assistant (tone adjustment, expansion, hashtag generation)
- Intelligent posting time recommendations
- Historical performance tracking foundation
- Custom posting preference overrides
- AI usage tracking and cost management
- Twitter token auto-refresh
- Support for additional platforms (Mastodon, Bluesky)
- Image/media attachment support
- Full post analytics integration (Twitter/LinkedIn APIs)
- Bulk scheduling from CSV
- Multi-user support (future)
- Browser extension for quick scheduling
- AI content improvement based on historical performance
This project is licensed under the MIT License - see the LICENSE file for details.
- Built with Convex for backend infrastructure
- Styled with shadcn/ui components
- Authenticated with Better Auth
- Deployed on Vercel
- Documentation: See
docs/directory - Issues: GitHub Issues
- Discussions: GitHub Discussions
Built with ❤️ as a replacement for expensive social media scheduling tools.
Star this repo if you find it useful!