pounce/README.md
2025-12-12 10:27:00 +01:00

628 lines
19 KiB
Markdown

# pounce — Domain Intelligence Platform
A professional full-stack platform for domain hunters, investors, and portfolio managers. Features live auction aggregation, TLD price tracking, domain marketplace, and intelligent monitoring.
---
## Security remediation (required)
This repo previously contained **accidentally committed secrets** (`DEPLOY_backend.env`, `DEPLOY_frontend.env`) and **session cookies** (`backend/data/cookies/session_cookies.json`). The codebase was updated to **Cookie-based auth (HttpOnly)** and the **git history was rewritten** to remove the leaked files.
### Do this now (simple checklist)
1) **Rotate ALL secrets (treat old values as compromised)**
- **Backend secrets**: `SECRET_KEY`
- **Stripe**: `STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`, price IDs if necessary
- **OAuth**: `GOOGLE_CLIENT_SECRET`, `GITHUB_CLIENT_SECRET` (and IDs if you want)
- **Email**: `SMTP_PASSWORD`
- **Other integrations** (if used): `DROPCATCH_CLIENT_SECRET`, `SEDO_SIGN_KEY`, `MOZ_SECRET_KEY`
Generate a new `SECRET_KEY` locally:
```bash
python3 -c "import secrets; print(secrets.token_hex(32))"
```
2) **Force-push the rewritten history to your remote**
```bash
git push --force-with-lease --all
git push --force-with-lease --tags
```
3) **Re-clone on every server/CI machine**
Because history changed, **do not** `git pull` on old clones. The simplest safe path:
```bash
rm -rf pounce
git clone <your-repo> pounce
```
4) **Re-deploy**
- **Backend**: `pip install -r backend/requirements.txt`
- **Frontend**: `npm ci && npm run build`
5) **Quick verification**
- Login now sets an **HttpOnly cookie**:
- `POST /api/v1/auth/login` returns `{ "expires_in": ... }` (no token in JSON)
- `POST /api/v1/auth/logout` clears the cookie
### Deployment note (keep it simple)
For the new cookie-auth to “just work”, the recommended setup is:
- **Serve the frontend on your main domain**
- **Route `/api/v1/*` to the backend via reverse proxy** (nginx/caddy/Next rewrite)
### Env files (important)
- **Never commit** any of these:
- `DEPLOY_backend.env`, `DEPLOY_frontend.env`, `backend/data/cookies/*.json`
- Use templates:
- `DEPLOY_backend.env.example` → copy to `DEPLOY_backend.env` (local only)
- `DEPLOY_frontend.env.example` → copy to `DEPLOY_frontend.env` (local only)
---
## 🚀 What's New (v2.0)
### User Command Center
A complete redesign of the authenticated user experience:
| Feature | Description |
|---------|-------------|
| **Dashboard** | Personal overview with quick stats, trending TLDs, and activity |
| **Watchlist** | Monitor domain availability with 4-layer health checks (DNS, HTTP, SSL, WHOIS) |
| **Portfolio** | Track owned domains, valuations, and manage listings |
| **Auctions** | Live aggregated auctions from GoDaddy, Sedo, NameJet, DropCatch |
| **Marketplace** | Browse "For Sale" listings from other users |
| **My Listings** | Manage your own domain listings for sale |
| **TLD Pricing** | Compare prices across 886+ TLDs with trends and alerts |
| **Sniper Alerts** | Personalized auction alerts based on custom criteria |
| **SEO Juice** | Domain authority & backlink analysis (Tycoon only) |
### Public Pages
| Page | Route | Description |
|------|-------|-------------|
| Landing | `/` | Hero, features, pricing preview |
| Auctions | `/auctions` | Public auction browser (blurred Deal Score) |
| TLD Pricing | `/tld-pricing` | TLD overview (preview for non-auth) |
| TLD Detail | `/tld-pricing/[tld]` | Registrar comparison, price chart |
| Marketplace | `/buy` | Browse domains for sale |
| Listing Detail | `/buy/[slug]` | View listing, contact seller |
### Admin Panel (`/admin`)
Dedicated admin interface with its own sidebar:
- User Management (bulk upgrade, export CSV)
- Platform Statistics
- Auction & TLD Stats
- Scheduler Monitoring
- Email Testing
- Activity Logs
### Technical Highlights
- **Performance Optimized**: All pages use `useMemo` and `useCallback` for minimal re-renders
- **Consistent UI Components**: `PremiumTable`, `StatCard`, `SearchInput`, `TabBar`, `ActionButton`
- **Responsive Design**: Mobile-first with collapsible sidebar
- **Keyboard Shortcuts**: ⌘K search, navigation shortcuts
---
## ⚠️ After Fresh Clone / Database Reset
```bash
cd backend
source venv/bin/activate
# 1. Initialize database (creates ALL tables)
python scripts/init_db.py
# 2. Scrape TLD prices (886+ TLDs from Porkbun)
python scripts/seed_tld_prices.py
# 3. Scrape auctions (or let scheduler do it hourly)
python3 -c "
import asyncio
from app.services.auction_scraper import AuctionScraperService
from app.database import AsyncSessionLocal
async def scrape():
scraper = AuctionScraperService()
async with AsyncSessionLocal() as db:
await scraper.scrape_all_platforms(db)
await scraper.close()
asyncio.run(scrape())
"
```
---
## ⚡ Quick Start (Local Development)
**Terminal 1 - Backend:**
```bash
cd backend
source venv/bin/activate
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
```
**Terminal 2 - Frontend:**
```bash
cd frontend
npm run dev
```
**Access:**
- Frontend: http://localhost:3000
- Backend: http://localhost:8000
- API Docs: http://localhost:8000/docs
---
## 📁 Updated Project Structure
```
pounce/
├── backend/
│ ├── app/
│ │ ├── api/
│ │ │ ├── auth.py # Authentication
│ │ │ ├── oauth.py # Google/GitHub OAuth
│ │ │ ├── check.py # Domain availability
│ │ │ ├── domains.py # Watchlist CRUD
│ │ │ ├── portfolio.py # Portfolio management
│ │ │ ├── tld_prices.py # TLD pricing API
│ │ │ ├── auctions.py # Live auction aggregator
│ │ │ ├── listings.py # For Sale marketplace
│ │ │ ├── sniper_alerts.py # Personalized alerts
│ │ │ ├── seo.py # SEO Juice Detector
│ │ │ ├── subscription.py # Stripe payments
│ │ │ └── admin.py # Admin endpoints
│ │ ├── models/
│ │ │ ├── user.py
│ │ │ ├── domain.py
│ │ │ ├── subscription.py
│ │ │ ├── tld_price.py
│ │ │ ├── portfolio.py
│ │ │ ├── auction.py
│ │ │ ├── listing.py # NEW: For Sale listings
│ │ │ ├── sniper_alert.py # NEW: Alert configurations
│ │ │ └── seo_data.py # NEW: SEO cache
│ │ ├── services/
│ │ │ ├── domain_checker.py
│ │ │ ├── domain_health.py # 4-layer health monitoring
│ │ │ ├── auction_scraper.py # Multi-platform scraping
│ │ │ ├── seo_analyzer.py # Moz API / estimation
│ │ │ ├── stripe_service.py
│ │ │ └── email_service.py
│ │ └── scheduler.py # APScheduler cron jobs
│ └── scripts/
│ ├── init_db.py
│ └── seed_tld_prices.py
├── frontend/
│ ├── src/
│ │ ├── app/
│ │ │ ├── page.tsx # Landing page
│ │ │ ├── auctions/ # Public auctions
│ │ │ ├── tld-pricing/ # Public TLD pricing
│ │ │ ├── buy/ # Public marketplace
│ │ │ ├── pricing/ # Subscription plans
│ │ │ ├── login/
│ │ │ ├── register/
│ │ │ ├── command/ # 🆕 User Command Center
│ │ │ │ ├── dashboard/
│ │ │ │ ├── watchlist/
│ │ │ │ ├── portfolio/
│ │ │ │ ├── auctions/
│ │ │ │ ├── marketplace/
│ │ │ │ ├── listings/
│ │ │ │ ├── alerts/
│ │ │ │ ├── pricing/
│ │ │ │ │ └── [tld]/ # TLD detail
│ │ │ │ ├── seo/
│ │ │ │ ├── settings/
│ │ │ │ └── welcome/ # Post-payment onboarding
│ │ │ └── admin/ # Admin panel
│ │ ├── components/
│ │ │ ├── Header.tsx
│ │ │ ├── Footer.tsx
│ │ │ ├── Sidebar.tsx # Command Center nav
│ │ │ ├── AdminLayout.tsx # Admin nav
│ │ │ ├── CommandCenterLayout.tsx
│ │ │ ├── PremiumTable.tsx # Reusable table + UI components
│ │ │ └── DomainChecker.tsx
│ │ └── lib/
│ │ ├── api.ts # API client
│ │ └── store.ts # Zustand state
│ └── tailwind.config.ts
├── DATABASE_MIGRATIONS.md # 🆕 Migration guide
├── DEPLOYMENT.md
└── README.md
```
---
## 🗄️ Database Tables
### Core Tables (Existing)
| Table | Description |
|-------|-------------|
| `users` | User accounts |
| `subscriptions` | Plan tiers (Scout/Trader/Tycoon) |
| `domains` | Watchlist |
| `domain_checks` | Check history |
| `tld_prices` | Price data |
| `tld_info` | TLD metadata |
| `portfolio_domains` | Owned domains |
| `domain_valuations` | Valuation history |
| `domain_auctions` | Scraped auctions |
| `price_alerts` | TLD price alerts |
### NEW Tables (v2.0)
| Table | Description |
|-------|-------------|
| `domain_listings` | For Sale marketplace listings |
| `listing_inquiries` | Buyer contact messages |
| `listing_views` | View analytics |
| `sniper_alerts` | Personalized alert configs |
| `sniper_alert_matches` | Matched auctions |
| `domain_seo_data` | SEO metrics cache |
See `DATABASE_MIGRATIONS.md` for full SQL schemas.
---
## 🔧 Server Deployment Guide
### 1. Clone & Install
```bash
git clone https://git.6bit.ch/yvg/pounce.git
cd pounce
# Backend
cd backend
python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt
cp env.example .env
# Edit .env with your secrets
# Frontend
cd ../frontend
npm install
echo "NEXT_PUBLIC_API_URL=https://api.pounce.ch" > .env.local
```
### 2. Environment Variables
**Backend `.env`:**
```env
# Core
SECRET_KEY=your-32-char-secret-key
DATABASE_URL=postgresql+asyncpg://user:pass@localhost/pounce
ALLOWED_ORIGINS=https://pounce.ch,https://www.pounce.ch
SITE_URL=https://pounce.ch
# SMTP (Zoho)
SMTP_HOST=smtp.zoho.eu
SMTP_PORT=465
SMTP_USER=hello@pounce.ch
SMTP_PASSWORD=your-password
SMTP_FROM_EMAIL=hello@pounce.ch
SMTP_USE_SSL=true
# Stripe
STRIPE_SECRET_KEY=sk_live_xxx
STRIPE_WEBHOOK_SECRET=whsec_xxx
STRIPE_PRICE_TRADER=price_xxx
STRIPE_PRICE_TYCOON=price_xxx
# OAuth
GOOGLE_CLIENT_ID=xxx
GOOGLE_CLIENT_SECRET=xxx
GOOGLE_REDIRECT_URI=https://api.pounce.ch/api/v1/oauth/google/callback
GITHUB_CLIENT_ID=xxx
GITHUB_CLIENT_SECRET=xxx
GITHUB_REDIRECT_URI=https://api.pounce.ch/api/v1/oauth/github/callback
# SEO (Optional - uses estimation if not set)
MOZ_ACCESS_ID=your-moz-id
MOZ_SECRET_KEY=your-moz-secret
```
**Frontend `.env.local`:**
```env
NEXT_PUBLIC_API_URL=https://api.pounce.ch
```
### 3. Initialize Database
```bash
cd backend
source venv/bin/activate
# Create all tables
python scripts/init_db.py
# Seed TLD prices
python scripts/seed_tld_prices.py
```
### 4. Start Services
**With PM2 (Recommended):**
```bash
# Backend
cd backend && source venv/bin/activate
pm2 start "uvicorn app.main:app --host 0.0.0.0 --port 8000" --name pounce-api
# Frontend
cd frontend
npm run build
pm2 start "npm start" --name pounce-web
# Save & auto-restart
pm2 save
pm2 startup
```
**With Docker:**
```bash
docker-compose up -d --build
```
### 5. Nginx Configuration
```nginx
# API
server {
listen 443 ssl http2;
server_name api.pounce.ch;
ssl_certificate /etc/letsencrypt/live/api.pounce.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.pounce.ch/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Frontend
server {
listen 443 ssl http2;
server_name pounce.ch www.pounce.ch;
ssl_certificate /etc/letsencrypt/live/pounce.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/pounce.ch/privkey.pem;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
```
### 6. Stripe Webhook
Configure Stripe to send webhooks to:
```
https://api.pounce.ch/api/v1/webhooks/stripe
```
Events to enable:
- `checkout.session.completed`
- `customer.subscription.updated`
- `customer.subscription.deleted`
- `invoice.payment_succeeded`
- `invoice.payment_failed`
---
## 📊 Scheduler Jobs (Automatic)
The backend includes APScheduler that runs automatically:
| Job | Schedule | Description |
|-----|----------|-------------|
| **TLD Price Scrape** | 03:00 & 15:00 UTC | Scrapes 886+ TLDs from Porkbun + 4 registrars |
| **Auction Scrape** | Every 2h at :30 | Scrapes from ExpiredDomains |
| **Domain Check (Scout)** | Daily 06:00 UTC | Checks all watched domains |
| **Domain Check (Trader)** | Hourly :00 | Checks Trader domains |
| **Domain Check (Tycoon)** | Every 10 min | Checks Tycoon domains |
| **Health Checks** | Daily 06:00 UTC | DNS/HTTP/SSL health analysis |
| **Expiry Warnings** | Weekly Mon 08:00 | Warns about domains <30 days |
| **Weekly Digest** | Weekly Sun 10:00 | Summary email to all users |
| **Price Alerts** | 04:00 & 16:00 UTC | Sends email for >5% changes |
| **Sniper Match** | Every 30 min | Matches auctions to alerts |
| **Auction Cleanup** | Every 15 min | Removes expired auctions |
---
## 📧 Email Notifications & Monitoring
### What Gets Monitored
The Watchlist automatically monitors domains and sends alerts:
| Alert Type | Trigger | Email Subject |
|------------|---------|---------------|
| **Domain Available** | Domain becomes free | `🐆 POUNCE NOW: domain.com just dropped` |
| **Expiry Warning** | Domain expires in <30 days | `⏰ 3 domains expiring soon` |
| **Health Critical** | Domain goes offline/critical | `🐆 POUNCE NOW: domain.com` |
| **Price Change** | TLD price changes >5% | `💰 .ai moved down 12%` |
| **Sniper Match** | Auction matches your criteria | `🎯 Sniper Alert: 5 matching domains found!` |
| **Weekly Digest** | Every Sunday | `📊 Your week in domains` |
### Check Frequency by Subscription
| Tier | Frequency | Use Case |
|------|-----------|----------|
| Scout (Free) | Daily | Hobby monitoring |
| Trader ($9) | Hourly | Active domain hunters |
| Tycoon ($29) | Every 10 min | Professional investors |
### ⚠️ Required: Email Configuration
**Email notifications will NOT work without SMTP configuration!**
Add these to your `.env` file:
```env
# SMTP Configuration (Required for email alerts)
SMTP_HOST=smtp.zoho.eu # Your SMTP server
SMTP_PORT=465 # Usually 465 (SSL) or 587 (TLS)
SMTP_USER=hello@pounce.ch # SMTP username
SMTP_PASSWORD=your-password # SMTP password
SMTP_FROM_EMAIL=hello@pounce.ch # Sender address
SMTP_FROM_NAME=pounce # Sender name
SMTP_USE_SSL=true # Use SSL (port 465)
SMTP_USE_TLS=false # Use STARTTLS (port 587)
```
**Recommended SMTP Providers:**
- **Zoho Mail** (Free tier available) - Port 465 SSL
- **Resend** (Developer-friendly) - Port 587 TLS
- **SendGrid** (10k free/month) - Port 587 TLS
- **Amazon SES** (Cheap at scale) - Port 587 TLS
### Verify Email is Working
```bash
cd backend && source venv/bin/activate
python3 -c "
from app.services.email_service import email_service
print('Email configured:', email_service.is_configured())
"
```
### Test Email Manually
```bash
python3 -c "
import asyncio
from app.services.email_service import email_service
async def test():
result = await email_service.send_email(
to_email='your@email.com',
subject='Test from Pounce',
html_content='<h1>It works!</h1>'
)
print('Sent:', result)
asyncio.run(test())
"
```
---
## 💳 Subscription Tiers
| Feature | Scout (Free) | Trader (€9/mo) | Tycoon (€29/mo) |
|---------|--------------|----------------|-----------------|
| Watchlist Domains | 5 | 50 | 500 |
| Portfolio Domains | — | 25 | Unlimited |
| Marketplace Listings | — | 3 | 25 |
| Sniper Alerts | — | 3 | 25 |
| TLD Pricing | Preview | Full | Full |
| Auction Deal Score | Blurred | Full | Full |
| SEO Juice Detector | — | — | ✓ |
| Health Monitoring | Basic | Advanced | Advanced |
| Notifications | Email | Email + SMS | All + Webhook |
---
## 🔗 API Endpoints (New)
### Listings (For Sale)
| Method | Endpoint | Auth |
|--------|----------|------|
| GET | `/api/v1/listings` | No |
| GET | `/api/v1/listings/{slug}` | No |
| POST | `/api/v1/listings/{slug}/inquire` | No |
| GET | `/api/v1/listings/my` | Yes |
| POST | `/api/v1/listings` | Yes |
| PUT | `/api/v1/listings/{id}` | Yes |
| DELETE | `/api/v1/listings/{id}` | Yes |
| POST | `/api/v1/listings/{id}/verify-dns` | Yes |
### Sniper Alerts
| Method | Endpoint | Auth |
|--------|----------|------|
| GET | `/api/v1/sniper-alerts` | Yes |
| POST | `/api/v1/sniper-alerts` | Yes |
| PUT | `/api/v1/sniper-alerts/{id}` | Yes |
| DELETE | `/api/v1/sniper-alerts/{id}` | Yes |
| POST | `/api/v1/sniper-alerts/{id}/test` | Yes |
| GET | `/api/v1/sniper-alerts/{id}/matches` | Yes |
### SEO Data (Tycoon Only)
| Method | Endpoint | Auth |
|--------|----------|------|
| GET | `/api/v1/seo/{domain}` | Tycoon |
| POST | `/api/v1/seo/batch` | Tycoon |
| GET | `/api/v1/seo/{domain}/quick` | Trader+ |
---
## 🎨 UI Components
The Command Center uses these reusable components from `PremiumTable.tsx`:
| Component | Usage |
|-----------|-------|
| `PremiumTable` | Elegant data tables with sorting |
| `StatCard` | Stats display with icons |
| `PageContainer` | Consistent max-width wrapper |
| `SearchInput` | Search with clear button |
| `TabBar` | Tab navigation with counts |
| `FilterBar` | Row of filter controls |
| `SelectDropdown` | Styled select inputs |
| `ActionButton` | Primary/secondary/ghost buttons |
| `Badge` | Status badges with variants |
| `TableActionButton` | Icon buttons for table rows |
---
## 🐛 Troubleshooting
### Common Console Warnings (Ignore)
| Warning | Explanation |
|---------|-------------|
| `SES Removing unpermitted intrinsics` | MetaMask browser extension |
| `PostHog CORS blocked` | Normal on localhost |
| `-webkit-text-size-adjust` error | Old CSS prefixes |
### Actual Errors
| Error | Solution |
|-------|----------|
| `CORS request did not succeed` to localhost:8000 | **Start the backend!** |
| `500 Internal Server Error` on `/seo/` | Run `init_db.py` to create tables |
| `404` on `/listings/my` | API routes ordered correctly in v2.0 |
---
## 📄 License
MIT License
---
## 📧 Support
For issues and feature requests, please open a GitHub issue or contact support@pounce.ch