A full-stack web application that resurfaces overlooked and under-covered global news. The core UX pairs trusted longform articles with curated social media eyewitness content, inspired by Ground News.
- Frontend: Next.js 14 (App Router) + TailwindCSS + shadcn/ui
- Backend: FastAPI + PostgreSQL + Elasticsearch
- Payments: Stripe subscriptions
- Data Ingestion: Twitter API, Apify (TikTok), Instagram Graph API
- Discovery Feed: List of topics with filters (region, category, recency)
- Topic Pages: Split-view layout with article (left) and curated social posts (right)
- Social Content Ingestion: Automated scraping from Twitter, TikTok, Instagram
- Curation Dashboard: Admin CRUD for topics, articles, and social posts
- Reliability Scoring: Automated scoring system for social posts
- Search: Full-text search across topics and posts (Elasticsearch)
- Auth & Subscriptions: Email/password auth with Stripe $5/month subscription
- Ads: Ethical ad slots for nonprofits
- Node.js 18+
- Python 3.11+
- PostgreSQL 14+
- Elasticsearch 8+
- Redis (optional, for background jobs)
cd web
npm install
cp .env.example .env.local
# Edit .env.local with your API URL
npm run devcd api
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -e .
cp env.example .env
# Edit .env with your database, Stripe, and API keys
# Run migrations
alembic upgrade head
# Start server
uvicorn app.main:app --reloadNEXT_PUBLIC_API_URL=http://localhost:8000
# Database
DATABASE_URL=postgresql://user:pass@localhost/spotlight
ASYNC_DATABASE_URL=postgresql+asyncpg://user:pass@localhost/spotlight
# JWT
SECRET_KEY=your-secret-key
ACCESS_TOKEN_EXPIRE_MINUTES=30
# Elasticsearch
ELASTICSEARCH_URL=http://localhost:9200
# Stripe
STRIPE_SECRET_KEY=sk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
STRIPE_PRICE_ID=price_...
FRONTEND_URL=http://localhost:3000
# Social Media APIs
TWITTER_BEARER_TOKEN=...
APIFY_API_TOKEN=...
INSTAGRAM_APP_ID=...
INSTAGRAM_APP_SECRET=...
GET /api/topics- List all topicsGET /api/topics/{slug}- Get topic by slugGET /api/search?q=...- Search topics and postsGET /api/score?post_id=...- Get reliability score
POST /api/auth/register- Register new userPOST /api/auth/login- Login (returns JWT)GET /api/auth/me- Get current user info
POST /api/subscribe/create-checkout- Create Stripe checkout sessionPOST /api/subscribe/webhook- Stripe webhook handler
POST /api/topics- Create topicPATCH /api/topics/{id}- Update topicDELETE /api/topics/{id}- Delete topicPOST /api/curation- Create curationPOST /api/scrape?topic_slug=...- Trigger scraping job
id,title,slug,summaryregion,tags[],key_facts[]primary_article_id,status(draft/published)last_updated
id,title,source,urlsummary,publication_datefull_text(optional),paywalled
id,platform,post_id,urlauthor_handle,timestampembed_html,media_urls[]reliability_score,tags[]
Each social post receives a score (0-100) based on:
- Provenance Match (0-30): Tags matching topic/article
- Source Credibility (0-25): Author verification, reputation
- Content Consistency (0-20): Text quality, length
- Verification Assets (0-25): Media attachments, geolocation
cd web
vercel deploycd api
fly deploy # or render deploy- Use managed PostgreSQL (Supabase, Neon, or AWS RDS)
- Use Elastic Cloud for Elasticsearch
# Backend
cd api
pytest
# Frontend
cd web
npm test# Backend
black app/
isort app/
mypy app/
# Frontend
cd web
npm run lintMIT
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
For issues and questions, please open a GitHub issue.