# 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 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) ## Server deployment (recommended) See `SERVER_DEPLOYMENT.md`. ### 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='

It works!

' ) 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