From 4cb5f42d90b46fc2c4c29e581fd694f43515ce61 Mon Sep 17 00:00:00 2001 From: "yves.gugger" Date: Wed, 10 Dec 2025 17:07:23 +0100 Subject: [PATCH] docs: Complete documentation overhaul for v2.0 README.md - Full rewrite: - New feature overview (Command Center, Marketplace, Alerts) - Updated project structure with all new files/directories - Complete server deployment guide - Environment variables for all features - API endpoint documentation for new routes - UI component reference - Troubleshooting guide DATABASE_MIGRATIONS.md - Expanded: - All 6 new tables with full SQL schemas - Indexes for performance - Environment variables (Moz API, Stripe) - Verification queries - Rollback instructions - Scheduler job reference Tables documented: 1. domain_listings (For Sale marketplace) 2. listing_inquiries (Buyer messages) 3. listing_views (Analytics) 4. sniper_alerts (Personalized alerts) 5. sniper_alert_matches (Matched auctions) 6. domain_seo_data (SEO cache for Tycoon) --- DATABASE_MIGRATIONS.md | 261 +++++---- README.md | 1215 +++++++++++----------------------------- 2 files changed, 497 insertions(+), 979 deletions(-) diff --git a/DATABASE_MIGRATIONS.md b/DATABASE_MIGRATIONS.md index b2479df..4950ad7 100644 --- a/DATABASE_MIGRATIONS.md +++ b/DATABASE_MIGRATIONS.md @@ -1,37 +1,35 @@ # Database Migrations Guide -## Overview +## Quick Overview -This document lists all database tables that need to be created when deploying Pounce to a new server. +When deploying Pounce to a new server, these tables need to be created: -## Required Tables - -### Core Tables (Already Implemented) - -| Table | Model | Description | -|-------|-------|-------------| -| `users` | User | User accounts and authentication | -| `subscriptions` | Subscription | User subscription plans (Scout, Trader, Tycoon) | -| `domains` | Domain | Tracked domains in watchlists | -| `domain_checks` | DomainCheck | Domain availability check history | -| `tld_prices` | TLDPrice | TLD price history | -| `tld_info` | TLDInfo | TLD metadata | -| `portfolio_domains` | PortfolioDomain | User-owned domains | -| `domain_valuations` | DomainValuation | Domain valuation history | -| `domain_auctions` | DomainAuction | Scraped auction listings | -| `auction_scrape_logs` | AuctionScrapeLog | Scraping job logs | -| `newsletter_subscribers` | NewsletterSubscriber | Email newsletter list | -| `price_alerts` | PriceAlert | TLD price change alerts | -| `admin_activity_logs` | AdminActivityLog | Admin action audit log | -| `blog_posts` | BlogPost | Blog content | +``` +✅ Core Tables (17) - User, Subscription, Domain, TLD, etc. +🆕 New Tables (6) - Listings, Sniper Alerts, SEO Data +``` --- -### NEW Tables (To Be Created) +## Automatic Migration -These tables were added for the **"For Sale" Marketplace** and **Sniper Alerts** features: +The easiest way to create all tables: -#### 1. Domain Listings (For Sale Marketplace) +```bash +cd backend +source venv/bin/activate +python scripts/init_db.py +``` + +This creates all tables from the SQLAlchemy models automatically. + +--- + +## Manual SQL Migration + +If you need to run migrations manually (e.g., on an existing database), use the SQL below. + +### NEW Table 1: Domain Listings (For Sale Marketplace) ```sql -- Main listing table @@ -45,13 +43,13 @@ CREATE TABLE domain_listings ( asking_price FLOAT, min_offer FLOAT, currency VARCHAR(3) DEFAULT 'USD', - price_type VARCHAR(20) DEFAULT 'fixed', + price_type VARCHAR(20) DEFAULT 'fixed', -- 'fixed', 'negotiable', 'make_offer' pounce_score INTEGER, estimated_value FLOAT, - verification_status VARCHAR(20) DEFAULT 'not_started', + verification_status VARCHAR(20) DEFAULT 'not_started', -- 'not_started', 'pending', 'verified', 'failed' verification_code VARCHAR(64), verified_at TIMESTAMP, - status VARCHAR(30) DEFAULT 'draft', + status VARCHAR(30) DEFAULT 'draft', -- 'draft', 'published', 'sold', 'expired', 'removed' show_valuation BOOLEAN DEFAULT TRUE, allow_offers BOOLEAN DEFAULT TRUE, featured BOOLEAN DEFAULT FALSE, @@ -67,8 +65,11 @@ CREATE INDEX idx_listings_user_id ON domain_listings(user_id); CREATE INDEX idx_listings_domain ON domain_listings(domain); CREATE INDEX idx_listings_slug ON domain_listings(slug); CREATE INDEX idx_listings_status ON domain_listings(status); +CREATE INDEX idx_listings_price ON domain_listings(asking_price); ``` +### NEW Table 2: Listing Inquiries + ```sql -- Contact inquiries from potential buyers CREATE TABLE listing_inquiries ( @@ -80,7 +81,7 @@ CREATE TABLE listing_inquiries ( company VARCHAR(200), message TEXT NOT NULL, offer_amount FLOAT, - status VARCHAR(20) DEFAULT 'new', + status VARCHAR(20) DEFAULT 'new', -- 'new', 'read', 'replied', 'archived' ip_address VARCHAR(45), user_agent VARCHAR(500), created_at TIMESTAMP DEFAULT NOW(), @@ -89,8 +90,11 @@ CREATE TABLE listing_inquiries ( ); CREATE INDEX idx_inquiries_listing_id ON listing_inquiries(listing_id); +CREATE INDEX idx_inquiries_status ON listing_inquiries(status); ``` +### NEW Table 3: Listing Views + ```sql -- Analytics: page views CREATE TABLE listing_views ( @@ -104,41 +108,53 @@ CREATE TABLE listing_views ( ); CREATE INDEX idx_views_listing_id ON listing_views(listing_id); +CREATE INDEX idx_views_date ON listing_views(viewed_at); ``` -#### 2. Sniper Alerts +### NEW Table 4: Sniper Alerts ```sql --- Saved filter configurations for personalized alerts +-- Saved filter configurations for personalized auction alerts CREATE TABLE sniper_alerts ( id SERIAL PRIMARY KEY, user_id INTEGER NOT NULL REFERENCES users(id), name VARCHAR(100) NOT NULL, description VARCHAR(500), + + -- Filter criteria (stored as JSON for flexibility) filter_criteria JSONB NOT NULL DEFAULT '{}', - tlds VARCHAR(500), - keywords VARCHAR(500), - exclude_keywords VARCHAR(500), + + -- Quick filters (also stored as columns for fast queries) + tlds VARCHAR(500), -- comma-separated: "com,net,io" + keywords VARCHAR(500), -- comma-separated search terms + exclude_keywords VARCHAR(500), -- words to exclude max_length INTEGER, min_length INTEGER, max_price FLOAT, min_price FLOAT, max_bids INTEGER, ending_within_hours INTEGER, - platforms VARCHAR(200), + platforms VARCHAR(200), -- "GoDaddy,Sedo,NameJet" + + -- Vanity filters no_numbers BOOLEAN DEFAULT FALSE, no_hyphens BOOLEAN DEFAULT FALSE, exclude_chars VARCHAR(50), + + -- Notification settings notify_email BOOLEAN DEFAULT TRUE, notify_sms BOOLEAN DEFAULT FALSE, notify_push BOOLEAN DEFAULT FALSE, max_notifications_per_day INTEGER DEFAULT 10, cooldown_minutes INTEGER DEFAULT 30, + + -- Status is_active BOOLEAN DEFAULT TRUE, matches_count INTEGER DEFAULT 0, notifications_sent INTEGER DEFAULT 0, last_matched_at TIMESTAMP, last_notified_at TIMESTAMP, + created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() ); @@ -147,6 +163,8 @@ CREATE INDEX idx_alerts_user_id ON sniper_alerts(user_id); CREATE INDEX idx_alerts_active ON sniper_alerts(is_active); ``` +### NEW Table 5: Sniper Alert Matches + ```sql -- Matched auctions for each alert CREATE TABLE sniper_alert_matches ( @@ -164,128 +182,159 @@ CREATE TABLE sniper_alert_matches ( ); CREATE INDEX idx_matches_alert_id ON sniper_alert_matches(alert_id); +CREATE INDEX idx_matches_domain ON sniper_alert_matches(domain); +CREATE INDEX idx_matches_notified ON sniper_alert_matches(notified); ``` -#### 3. SEO Data (Tycoon Feature) +### NEW Table 6: SEO Data (Tycoon Feature) ```sql --- Cached SEO metrics for domains +-- Cached SEO metrics for domains (Moz API or estimation) CREATE TABLE domain_seo_data ( id SERIAL PRIMARY KEY, domain VARCHAR(255) NOT NULL UNIQUE, - domain_authority INTEGER, - page_authority INTEGER, - spam_score INTEGER, + + -- Core metrics + domain_authority INTEGER, -- 0-100 + page_authority INTEGER, -- 0-100 + spam_score INTEGER, -- 0-100 total_backlinks INTEGER, referring_domains INTEGER, - top_backlinks JSONB, - notable_backlinks TEXT, + + -- Backlink analysis + top_backlinks JSONB, -- [{domain, authority, page}, ...] + notable_backlinks TEXT, -- comma-separated high-value domains + + -- Notable link flags has_wikipedia_link BOOLEAN DEFAULT FALSE, has_gov_link BOOLEAN DEFAULT FALSE, has_edu_link BOOLEAN DEFAULT FALSE, has_news_link BOOLEAN DEFAULT FALSE, - seo_value_estimate FLOAT, - data_source VARCHAR(50) DEFAULT 'moz', + + -- Value estimation + seo_value_estimate FLOAT, -- Estimated $ value based on SEO metrics + + -- Metadata + data_source VARCHAR(50) DEFAULT 'estimated', -- 'moz', 'estimated' last_updated TIMESTAMP DEFAULT NOW(), - expires_at TIMESTAMP, + expires_at TIMESTAMP, -- Cache expiry (7 days) fetch_count INTEGER DEFAULT 0 ); CREATE INDEX idx_seo_domain ON domain_seo_data(domain); +CREATE INDEX idx_seo_da ON domain_seo_data(domain_authority); ``` --- -## Migration Commands +## All Tables Summary -### Using Alembic (Recommended) +### Core Tables (Already Implemented) -```bash -cd backend +| Table | Purpose | +|-------|---------| +| `users` | User accounts and authentication | +| `subscriptions` | Subscription plans (Scout, Trader, Tycoon) | +| `domains` | Tracked domains in watchlists | +| `domain_checks` | Domain availability check history | +| `tld_prices` | TLD price history (886+ TLDs) | +| `tld_info` | TLD metadata and categories | +| `portfolio_domains` | User-owned domains | +| `domain_valuations` | Domain valuation history | +| `domain_auctions` | Scraped auction listings | +| `auction_scrape_logs` | Scraping job logs | +| `newsletter_subscribers` | Email newsletter list | +| `price_alerts` | TLD price change alerts | +| `admin_activity_logs` | Admin action audit log | +| `blog_posts` | Blog content | -# Generate migration -alembic revision --autogenerate -m "Add marketplace and sniper alert tables" +### New Tables (v2.0) -# Apply migration -alembic upgrade head -``` - -### Manual SQL (Alternative) - -Run the SQL statements above in order on your PostgreSQL database. +| Table | Purpose | Required For | +|-------|---------|--------------| +| `domain_listings` | For Sale marketplace | `/command/listings`, `/buy` | +| `listing_inquiries` | Buyer messages | Marketplace inquiries | +| `listing_views` | View analytics | Listing stats | +| `sniper_alerts` | Alert configurations | `/command/alerts` | +| `sniper_alert_matches` | Matched auctions | Alert notifications | +| `domain_seo_data` | SEO metrics cache | `/command/seo` (Tycoon) | --- ## Verification -After running migrations, verify tables exist: +After migration, verify all tables exist: ```sql +-- PostgreSQL SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' -AND table_name IN ( - 'domain_listings', - 'listing_inquiries', - 'listing_views', - 'sniper_alerts', - 'sniper_alert_matches', - 'domain_seo_data' -); +ORDER BY table_name; + +-- Should include: +-- domain_listings +-- listing_inquiries +-- listing_views +-- sniper_alerts +-- sniper_alert_matches +-- domain_seo_data ``` --- -## Feature References +## Environment Variables for New Features -These tables implement features from: +### Moz API (Optional - for real SEO data) -- **analysis_3.md** - "Micro-Marktplatz" (For Sale Landing Pages) -- **analysis_3.md** - "Sniper Alerts" (Strategie 4: Alerts nach Maß) -- **concept.md** - "For Sale Pages" marketplace feature +```env +MOZ_ACCESS_ID=your_moz_access_id +MOZ_SECRET_KEY=your_moz_secret_key +``` + +Without these variables, the SEO analyzer uses **estimation mode** based on domain characteristics (length, TLD, keywords). + +### Stripe (Required for payments) + +```env +STRIPE_SECRET_KEY=sk_live_xxx +STRIPE_WEBHOOK_SECRET=whsec_xxx +STRIPE_PRICE_TRADER=price_xxx # €9/month +STRIPE_PRICE_TYCOON=price_xxx # €29/month +``` --- -## API Endpoints +## Scheduler Jobs -### Listings (For Sale) +These background jobs run automatically when the backend starts: -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/listings` | Browse public listings | -| GET | `/api/v1/listings/{slug}` | View listing details | -| POST | `/api/v1/listings/{slug}/inquire` | Contact seller | -| POST | `/api/v1/listings` | Create listing (auth) | -| GET | `/api/v1/listings/my` | Get my listings (auth) | -| PUT | `/api/v1/listings/{id}` | Update listing (auth) | -| DELETE | `/api/v1/listings/{id}` | Delete listing (auth) | -| POST | `/api/v1/listings/{id}/verify-dns` | Start verification | -| GET | `/api/v1/listings/{id}/verify-dns/check` | Check verification | +| Job | Schedule | Table Affected | +|-----|----------|----------------| +| Sniper Alert Matching | Every 15 min | `sniper_alert_matches` | +| Auction Scrape | Hourly | `domain_auctions` | +| TLD Price Scrape | Daily 03:00 | `tld_prices` | +| Domain Check | Daily 06:00 | `domain_checks` | -### Sniper Alerts +--- -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/sniper-alerts` | Get my alerts | -| POST | `/api/v1/sniper-alerts` | Create alert | -| PUT | `/api/v1/sniper-alerts/{id}` | Update alert | -| DELETE | `/api/v1/sniper-alerts/{id}` | Delete alert | -| GET | `/api/v1/sniper-alerts/{id}/matches` | Get matched auctions | -| POST | `/api/v1/sniper-alerts/{id}/test` | Test alert criteria | +## Rollback -### SEO Data (Tycoon Only) +If you need to remove the new tables: -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/seo/{domain}` | Full SEO analysis (Tycoon) | -| POST | `/api/v1/seo/batch` | Batch analyze domains (Tycoon) | -| GET | `/api/v1/seo/{domain}/quick` | Quick summary (Trader+) | - -**Environment Variables for Moz API:** -``` -MOZ_ACCESS_ID=your_access_id -MOZ_SECRET_KEY=your_secret_key +```sql +DROP TABLE IF EXISTS sniper_alert_matches CASCADE; +DROP TABLE IF EXISTS sniper_alerts CASCADE; +DROP TABLE IF EXISTS listing_views CASCADE; +DROP TABLE IF EXISTS listing_inquiries CASCADE; +DROP TABLE IF EXISTS domain_listings CASCADE; +DROP TABLE IF EXISTS domain_seo_data CASCADE; ``` -Without these, the system uses estimation mode based on domain characteristics. +--- +## Related Documentation + +- `README.md` - Full deployment guide +- `DEPLOYMENT.md` - Server setup details +- `backend/app/models/` - SQLAlchemy model definitions diff --git a/README.md b/README.md index 5260538..772109a 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,66 @@ -# pounce — Domain Availability Monitoring +# pounce — Domain Intelligence Platform -A professional full-stack application for monitoring domain name availability with TLD price tracking and intelligence. +A professional full-stack platform for domain hunters, investors, and portfolio managers. Features live auction aggregation, TLD price tracking, domain marketplace, and intelligent monitoring. --- -## ⚠️ Important: After Fresh Clone / Database Reset +## 🚀 What's New (v2.0) -If you just cloned the repo or reset the database, run these commands to populate data: +### 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 tables) +# 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. (Optional) Scrape auctions immediately +# 3. Scrape auctions (or let scheduler do it hourly) python3 -c " import asyncio from app.services.auction_scraper import AuctionScraperService @@ -34,11 +76,6 @@ asyncio.run(scrape()) " ``` -Without these steps: -- TLD Pricing page shows only 18 TLDs (instead of 886+) -- Auctions page shows 0 auctions -- The scheduler will auto-scrape TLDs daily at 03:00 UTC and auctions hourly - --- ## ⚡ Quick Start (Local Development) @@ -61,285 +98,201 @@ npm run dev - Backend: http://localhost:8000 - API Docs: http://localhost:8000/docs -### ⚠️ Common Console Warnings (Can be ignored) - -| Warning | Explanation | -|---------|-------------| -| `SES Removing unpermitted intrinsics` | MetaMask/browser extension - harmless | -| `PostHog CORS blocked` | Normal on localhost, works in production | -| `-webkit-text-size-adjust` error | Old CSS prefixes - harmless | -| `-moz-column-gap` unknown | Old Firefox prefix - harmless | -| `preloaded font not used` | Next.js optimization - harmless | - -### ❌ Actual Errors to Fix - -| Error | Solution | -|-------|----------| -| `CORS request did not succeed` to localhost:8000 | **Start the backend!** | -| `500 Internal Server Error` | Check backend logs, run `python scripts/init_db.py` | - --- -## Features - -### Core Functionality -- **Domain Availability Monitoring** — Track any domain and get notified when it becomes available -- **Domain Portfolio Management** — Track domains you own with purchase price, value, and ROI -- **Domain Valuation Engine** — 100% transparent algorithmic valuation with detailed breakdown -- **Smart Pounce Auctions** — Aggregated auctions from GoDaddy, Sedo, NameJet & more -- **TLD Price Intelligence** — Compare prices across 886+ TLDs from Porkbun API -- **Automated Price Scraping** — Daily cronjob scrapes real TLD prices from public APIs -- **Price Change Alerts** — Email notifications when TLD prices change >5% -- **Expiration Tracking** — Monitor domain expiration dates and plan ahead -- **Real-time Checks** — RDAP, WHOIS, and DNS-based availability verification -- **Swiss Domain Support** — Special RDAP integration for .ch/.li domains via nic.ch -- **Historical Data** — Track price trends and availability history over time (12 months) -- **Email Notifications** — Domain available alerts, price changes, weekly digests - -### Smart Pounce Auction Aggregator -- **Multi-Platform Search** — GoDaddy, Sedo, NameJet, SnapNames, DropCatch -- **Ending Soon** — Auctions ending in < 1 hour for sniping opportunities -- **Hot Auctions** — Most-bid domains across all platforms -- **Smart Opportunities** — AI-powered detection of undervalued domains -- **Affiliate Integration** — Click through to bid (no payment handling = Swiss GwG compliant) -- **Filters** — Search by keyword, TLD, platform, price range - -### Domain Valuation (Transparent) -- **Clear Formula** — `Value = Base × Length × TLD × Keyword × Brand` -- **Factor Breakdown** — Each multiplier explained (e.g., "4-letter domain ×5.0") -- **Confidence Levels** — High/Medium/Low based on score consistency -- **Real TLD Costs** — Registration cost context from database -- **Value-to-Cost Ratio** — Investment decision metric -- **Disclaimer** — Clear limitations of algorithmic valuation - -### User Experience -- **Modern UI** — Clean, minimalist dark-mode design with smooth animations -- **Responsive** — Fully optimized for desktop, tablet, and mobile -- **Authentication** — Secure JWT-based auth with subscription tiers -- **Dashboard** — Personal watchlist with status indicators and actions - -### Security Features (v1.1) -- **Password Reset** — Secure token-based password recovery via email -- **Email Verification** — Optional email confirmation for new accounts -- **OAuth Login** — Sign in with Google or GitHub -- **Rate Limiting** — Protection against brute-force attacks (slowapi) -- **Stripe Payments** — Secure subscription payments with Stripe Checkout -- **Stripe Customer Portal** — Manage billing, view invoices, cancel subscriptions -- **Contact Form** — With email confirmation and spam protection -- **Newsletter** — Subscribe/unsubscribe with double opt-in - -### Blog System (v1.3) -- **Blog Posts** — Create and manage blog articles -- **Categories & Tags** — Organize content -- **Featured Posts** — Highlight posts on homepage -- **View Tracking** — Analytics for post views -- **SEO Metadata** — Custom meta titles and descriptions -- **Draft/Publish** — Content workflow management - -### Admin Panel (v1.3) -- **User Management** — List, search, upgrade, and manage users -- **Bulk Operations** — Upgrade multiple users at once -- **User Export** — Export all users to CSV -- **Price Alerts** — View all active TLD price alerts -- **Domain Health** — Manually trigger domain checks -- **Scheduler Status** — Monitor background jobs and last runs -- **Email Test** — Verify SMTP configuration -- **Activity Log** — Track admin actions -- **Blog Management** — Full CRUD for blog posts - -### CI/CD Pipeline (v1.2) -- **GitHub Actions** — Automated CI/CD on push to main -- **Frontend Lint** — ESLint + TypeScript type checking -- **Backend Lint** — Ruff linter for Python -- **Docker Build** — Multi-stage build verification -- **Security Scan** — Trivy vulnerability scanner -- **Automated Deploy** — SSH deployment to production server - -### TLD Detail Page (Professional) -- **Price Hero** — Instant view of cheapest price with direct registration link -- **Price Alert System** — Subscribe to email notifications for price changes -- **Interactive Price Chart** — 1M/3M/1Y/ALL time periods with hover tooltips -- **Domain Search** — Check availability directly on the TLD page -- **Registrar Comparison** — Full table with Register/Renew/Transfer prices + external links -- **Savings Calculator** — Shows how much you save vs most expensive registrar -- **Renewal Warning** — ⚠️ indicator when renewal price is >1.5x registration price -- **Related TLDs** — Smart suggestions based on current TLD (e.g., .com → .net, .org, .io) -- **Quick Stats** — Average price, price range, 30-day change, registrar count - ---- - -## Tech Stack - -### Backend -| Technology | Purpose | -|------------|---------| -| **Python 3.12+** | Runtime | -| **FastAPI** | Async web framework | -| **SQLAlchemy 2.0** | Async ORM | -| **SQLite/PostgreSQL** | Database | -| **APScheduler** | Background job scheduling | -| **python-whois** | WHOIS domain lookups | -| **whodap** | RDAP domain lookups | -| **httpx** | Async HTTP client (for custom RDAP) | -| **BeautifulSoup4** | Web scraping (backup) | -| **Pydantic** | Data validation | -| **JWT (python-jose)** | Authentication | - -### Frontend -| Technology | Purpose | -|------------|---------| -| **Next.js 14** | React framework (App Router) | -| **TypeScript** | Type safety | -| **Tailwind CSS** | Styling | -| **Zustand** | State management | -| **Lucide React** | Icons (outlined style) | -| **clsx** | Conditional classes | - ---- - -## Project Structure +## 📁 Updated Project Structure ``` pounce/ ├── backend/ │ ├── app/ -│ │ ├── api/ # API endpoints -│ │ │ ├── auth.py # Authentication (register, login, me) -│ │ │ ├── check.py # Domain availability check -│ │ │ ├── domains.py # Domain watchlist CRUD -│ │ │ ├── subscription.py # User subscription management -│ │ │ ├── tld_prices.py # TLD pricing data -│ │ │ └── admin.py # Admin endpoints -│ │ ├── models/ # SQLAlchemy models -│ │ │ ├── user.py # User model -│ │ │ ├── domain.py # Domain watchlist model -│ │ │ ├── subscription.py # Subscription tiers -│ │ │ └── tld_price.py # TLD pricing model -│ │ ├── schemas/ # Pydantic schemas -│ │ ├── services/ # Business logic -│ │ │ ├── auth.py # Auth service (JWT, hashing) -│ │ │ ├── domain_checker.py # RDAP/WHOIS/DNS checks -│ │ │ ├── email_service.py # Email notifications (SMTP) -│ │ │ ├── price_tracker.py # Price change detection & alerts -│ │ │ └── tld_scraper/ # TLD price scraping -│ │ │ ├── __init__.py -│ │ │ ├── base.py # Base scraper class -│ │ │ ├── porkbun.py # Porkbun API scraper -│ │ │ ├── tld_list.py # TLD-List scraper (placeholder) -│ │ │ └── aggregator.py # Combines sources, saves to DB -│ │ ├── config.py # Environment settings -│ │ ├── database.py # Database connection -│ │ ├── main.py # FastAPI app entry -│ │ └── scheduler.py # Background jobs -│ ├── scripts/ # Utility scripts -│ │ ├── seed_tld_prices.py # Initial TLD data scrape -│ │ ├── export_tld_prices.py # Export DB to JSON -│ │ ├── import_tld_prices.py # Import JSON to DB -│ │ └── tld_prices_export.json # Price data backup -│ ├── requirements.txt -│ ├── Dockerfile -│ └── env.example +│ │ ├── 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/ # Next.js App Router pages -│ │ │ ├── page.tsx # Homepage with domain checker -│ │ │ ├── layout.tsx # Root layout -│ │ │ ├── dashboard/ # User dashboard -│ │ │ ├── login/ # Login page -│ │ │ ├── register/ # Registration page -│ │ │ ├── pricing/ # Pricing plans -│ │ │ └── tld-pricing/ # TLD price intelligence -│ │ │ ├── page.tsx # TLD overview -│ │ │ └── [tld]/ # TLD detail page -│ │ ├── components/ # React components -│ │ │ ├── Header.tsx # Navigation header -│ │ │ ├── Footer.tsx # Site footer -│ │ │ └── DomainChecker.tsx # Domain search component -│ │ └── lib/ # Utilities -│ │ ├── api.ts # API client -│ │ └── store.ts # Zustand store -│ ├── tailwind.config.ts # Tailwind configuration -│ ├── package.json -│ ├── Dockerfile -│ └── env.example -├── docker-compose.yml # Docker deployment -├── DEPLOYMENT.md # Deployment guide +│ │ ├── 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 ``` --- -## Pages Overview +## 🗄️ Database Tables -| Route | Description | Auth Required | -|-------|-------------|---------------| -| `/` | Homepage with domain checker, features, pricing preview | No | -| `/login` | User login | No | -| `/register` | User registration | No | -| `/forgot-password` | Request password reset | No | -| `/reset-password` | Reset password with token | No | -| `/verify-email` | Verify email with token | No | -| `/dashboard` | Personal domain watchlist | Yes | -| `/pricing` | Subscription plans with Stripe checkout | No | -| `/tld-pricing` | TLD price overview with trends | No* | -| `/tld-pricing/[tld]` | TLD detail with registrar comparison | Yes | -| `/auctions` | Smart Pounce auction aggregator | No* | -| `/contact` | Contact form | No | -| `/about` | About us | No | -| `/blog` | Blog posts listing | No | -| `/blog/{slug}` | Blog post detail | No | -| `/admin` | Admin panel | Yes (Admin) | -| `/privacy` | Privacy policy | No | -| `/terms` | Terms of service | No | -| `/imprint` | Legal imprint | No | +### 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 | -*Unauthenticated users see limited data with shimmer effects +### 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. --- -## Full Setup Guide +## 🔧 Server Deployment Guide -### Prerequisites -- Python 3.12+ -- Node.js 18+ -- npm or yarn - -### 🚀 One-Command Setup (Fresh Install) +### 1. Clone & Install ```bash -# Clone repository git clone https://git.6bit.ch/yvg/pounce.git cd pounce -# Run deployment script (installs everything + initializes database) -chmod +x deploy.sh -./deploy.sh +# 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 ``` -### 🏃 Starting the Application +### 2. Environment Variables -**Option A: Two Terminals (Recommended for Development)** +**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 -# Terminal 1 - Backend cd backend source venv/bin/activate -uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 -# Terminal 2 - Frontend -cd frontend -npm run dev +# Create all tables +python scripts/init_db.py + +# Seed TLD prices +python scripts/seed_tld_prices.py ``` -**Option B: Single Command (Quick)** - -```bash -chmod +x start.sh -./start.sh -``` - -**Option C: PM2 (Production)** +### 4. Start Services +**With PM2 (Recommended):** ```bash # Backend cd backend && source venv/bin/activate @@ -349,665 +302,181 @@ pm2 start "uvicorn app.main:app --host 0.0.0.0 --port 8000" --name pounce-api cd frontend npm run build pm2 start "npm start" --name pounce-web -``` -### 🔧 Manual Setup (Step by Step) - -#### 1. Clone Repository - -```bash -git clone https://git.6bit.ch/yvg/pounce.git -cd pounce -``` - -#### 2. Backend Setup - -```bash -cd backend - -# Create virtual environment -python3 -m venv venv -source venv/bin/activate # Linux/macOS -# .\venv\Scripts\activate # Windows - -# Install dependencies -pip install -r requirements.txt - -# Create environment file -cp env.example .env - -# Generate secret key -python -c "import secrets; print(secrets.token_hex(32))" -# Copy the output and paste into .env as SECRET_KEY=... - -# Initialize database (creates tables + seeds basic data) -python scripts/init_db.py - -# Scrape TLD prices from Porkbun API (886+ TLDs) -python scripts/seed_tld_prices.py - -# (Optional) Scrape auctions - or let the 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: - result = await scraper.scrape_all_platforms(db) - print(f'Scraped auctions: {result}') - await scraper.close() - -asyncio.run(scrape()) -" -``` - -Start backend: -```bash -uvicorn app.main:app --reload --host 0.0.0.0 --port 8000 -``` - -#### 3. Frontend Setup - -```bash -cd frontend - -# Install dependencies -npm install - -# Create environment file -echo "NEXT_PUBLIC_API_URL=http://localhost:8000" > .env.local - -# Start development server -npm run dev -``` - -#### 4. Verify Everything Works - -```bash -# Check backend health -curl http://localhost:8000/health -# Should return: {"status":"healthy","service":"DomainWatch","version":"1.0.0"} - -# Open frontend -open http://localhost:3000 -``` - -### 📍 Application URLs - -| Service | URL | Description | -|---------|-----|-------------| -| Frontend | http://localhost:3000 | Main application | -| Backend API | http://localhost:8000 | REST API | -| API Docs (Swagger) | http://localhost:8000/docs | Interactive API docs | -| API Docs (ReDoc) | http://localhost:8000/redoc | Alternative docs | - ---- - -## API Endpoints - -### Authentication -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/api/v1/auth/register` | Register new user (sends verification email) | -| POST | `/api/v1/auth/login` | Login (returns JWT) | -| GET | `/api/v1/auth/me` | Get current user | -| PUT | `/api/v1/auth/me` | Update current user | -| POST | `/api/v1/auth/forgot-password` | Request password reset | -| POST | `/api/v1/auth/reset-password` | Reset password with token | -| POST | `/api/v1/auth/verify-email` | Verify email with token | -| POST | `/api/v1/auth/resend-verification` | Resend verification email | - -### Domain Check -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/api/v1/check` | Check domain availability | - -### Domain Watchlist -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/domains` | List watched domains | -| POST | `/api/v1/domains` | Add domain to watchlist | -| DELETE | `/api/v1/domains/{id}` | Remove domain | -| POST | `/api/v1/domains/{id}/refresh` | Refresh domain status | -| GET | `/api/v1/domains/{id}/history` | Get check history | - -### TLD Prices -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/tld-prices/overview` | Get all TLDs overview | -| GET | `/api/v1/tld-prices/trending` | Get trending TLDs | -| GET | `/api/v1/tld-prices/{tld}` | Get TLD details | -| GET | `/api/v1/tld-prices/{tld}/history` | Get price history | -| GET | `/api/v1/tld-prices/{tld}/compare` | Compare registrar prices | - -#### Price Data Consistency - -All TLD price endpoints use a unified pricing strategy: - -1. **Static TLDs (18 popular)**: Rich multi-registrar data (5+ registrars per TLD) - - .com, .net, .org, .io, .ai, .dev, .app, .xyz, .tech, .online, .store, .me, .info, .biz, .ch, .de, .uk, .co - -2. **Database TLDs (869+)**: Scraped from Porkbun API (single registrar) - -**Price Calculation** (consistent across all endpoints): -``` -avg_price = sum(registrar_prices) / count(registrars) -min_price = min(registrar_prices) -max_price = max(registrar_prices) -``` - -This ensures identical prices on: -- Overview table (`/overview`) -- Detail page (`/{tld}`) -- Compare page (`/{tld}/compare`) -- Trending cards (`/trending`) - -### Subscription & Payments -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/subscription` | Get current subscription | -| GET | `/api/v1/subscription/tiers` | Get available tiers | -| GET | `/api/v1/subscription/features` | Get current features | -| POST | `/api/v1/subscription/checkout` | Create Stripe checkout session | -| POST | `/api/v1/subscription/portal` | Create Stripe customer portal | -| POST | `/api/v1/subscription/cancel` | Cancel subscription | - -### Price Alerts -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/price-alerts` | List user's price alerts | -| POST | `/api/v1/price-alerts` | Create new price alert | -| GET | `/api/v1/price-alerts/status/{tld}` | Check alert status for TLD | -| GET | `/api/v1/price-alerts/{tld}` | Get alert for specific TLD | -| PUT | `/api/v1/price-alerts/{tld}` | Update alert settings | -| DELETE | `/api/v1/price-alerts/{tld}` | Delete alert | -| POST | `/api/v1/price-alerts/{tld}/toggle` | Toggle alert on/off | - -### Contact & Newsletter -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/api/v1/contact` | Submit contact form | -| POST | `/api/v1/contact/newsletter/subscribe` | Subscribe to newsletter | -| POST | `/api/v1/contact/newsletter/unsubscribe` | Unsubscribe from newsletter | -| GET | `/api/v1/contact/newsletter/status` | Check subscription status | - -### Webhooks -| Method | Endpoint | Description | -|--------|----------|-------------| -| POST | `/api/v1/webhooks/stripe` | Stripe webhook handler | - -### Smart Pounce (Auctions) -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/auctions` | Search auctions across platforms | -| GET | `/api/v1/auctions/ending-soon` | Auctions ending within X hours | -| GET | `/api/v1/auctions/hot` | Most-bid auctions | -| GET | `/api/v1/auctions/opportunities` | AI-powered opportunities (auth) | -| GET | `/api/v1/auctions/stats` | Platform statistics | - -### OAuth -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/oauth/providers` | List enabled OAuth providers | -| GET | `/api/v1/oauth/google/login` | Initiate Google OAuth | -| GET | `/api/v1/oauth/google/callback` | Handle Google callback | -| GET | `/api/v1/oauth/github/login` | Initiate GitHub OAuth | -| GET | `/api/v1/oauth/github/callback` | Handle GitHub callback | - -### Blog -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/blog/posts` | List published posts | -| GET | `/api/v1/blog/posts/featured` | Get featured posts | -| GET | `/api/v1/blog/posts/categories` | Get all categories | -| GET | `/api/v1/blog/posts/{slug}` | Get single post | -| GET | `/api/v1/blog/admin/posts` | Admin: List all posts | -| POST | `/api/v1/blog/admin/posts` | Admin: Create post | -| GET | `/api/v1/blog/admin/posts/{id}` | Admin: Get post | -| PATCH | `/api/v1/blog/admin/posts/{id}` | Admin: Update post | -| DELETE | `/api/v1/blog/admin/posts/{id}` | Admin: Delete post | -| POST | `/api/v1/blog/admin/posts/{id}/publish` | Admin: Publish post | -| POST | `/api/v1/blog/admin/posts/{id}/unpublish` | Admin: Unpublish post | - -### Admin -| Method | Endpoint | Description | -|--------|----------|-------------| -| GET | `/api/v1/admin/stats` | Get platform statistics | -| GET | `/api/v1/admin/users` | List all users | -| GET | `/api/v1/admin/users/export` | Export users as CSV | -| POST | `/api/v1/admin/users/bulk-upgrade` | Bulk upgrade users | -| GET | `/api/v1/admin/users/{id}` | Get user details | -| PATCH | `/api/v1/admin/users/{id}` | Update user | -| DELETE | `/api/v1/admin/users/{id}` | Delete user | -| POST | `/api/v1/admin/users/{id}/upgrade` | Upgrade single user | -| GET | `/api/v1/admin/price-alerts` | List all price alerts | -| POST | `/api/v1/admin/domains/check-all` | Trigger domain checks | -| GET | `/api/v1/admin/newsletter` | List newsletter subs | -| GET | `/api/v1/admin/newsletter/export` | Export newsletter | -| POST | `/api/v1/admin/scrape-tld-prices` | Trigger TLD scrape | -| GET | `/api/v1/admin/tld-prices/stats` | Get TLD stats | -| GET | `/api/v1/admin/system/health` | System health check | -| GET | `/api/v1/admin/system/scheduler` | Scheduler status | -| POST | `/api/v1/admin/system/test-email` | Send test email | -| GET | `/api/v1/admin/activity-log` | Get activity log | - ---- - -## Subscription Tiers - -| Feature | Scout (Free) | Trader (€19/mo) | Tycoon (€49/mo) | -|---------|--------------|-----------------|------------------| -| Watchlist Domains | 5 | 50 | 500 | -| Portfolio Domains | — | 25 | Unlimited | -| Check Frequency | Daily | Hourly | 10 min | -| Notifications | Email | SMS/Telegram | + Webhooks | -| Domain Valuation | — | ✓ | ✓ | -| SEO Metrics | — | — | ✓ | -| Check History | — | 90 days | Unlimited | -| API Access | — | — | ✓ | -| Bulk Tools | — | — | ✓ | - ---- - -## Environment Variables - -### Backend (.env) - -| Variable | Description | Default | -|----------|-------------|---------| -| `DATABASE_URL` | Database connection string | `sqlite+aiosqlite:///./domainwatch.db` | -| `SECRET_KEY` | JWT signing key (min 32 chars) | **Required** | -| `ALLOWED_ORIGINS` | Allowed origins (comma-separated) | `http://localhost:3000` | -| `ACCESS_TOKEN_EXPIRE_MINUTES` | JWT token lifetime | `10080` (7 days) | -| `SITE_URL` | Frontend URL (for email links) | `http://localhost:3000` | -| `REQUIRE_EMAIL_VERIFICATION` | Require email verification | `false` | -| `SCHEDULER_CHECK_INTERVAL_HOURS` | Domain check interval | `24` | -| **SMTP Settings (Zoho)** | | | -| `SMTP_HOST` | Email server host | `smtp.zoho.eu` | -| `SMTP_PORT` | Email server port | `465` | -| `SMTP_USER` | Email username | `hello@pounce.ch` | -| `SMTP_PASSWORD` | Email password | **Required** | -| `SMTP_FROM_EMAIL` | Sender email | `hello@pounce.ch` | -| `SMTP_USE_SSL` | Use SSL (port 465) | `true` | -| `SMTP_USE_TLS` | Use STARTTLS (port 587) | `false` | -| `CONTACT_EMAIL` | Contact form recipient | `hello@pounce.ch` | -| **Stripe Settings** | | | -| `STRIPE_SECRET_KEY` | Stripe API secret key | Optional | -| `STRIPE_WEBHOOK_SECRET` | Stripe webhook secret | Optional | -| `STRIPE_PRICE_TRADER` | Trader plan Price ID | Optional | -| `STRIPE_PRICE_TYCOON` | Tycoon plan Price ID | Optional | - -### Frontend (.env.local) - -| Variable | Description | Default | -|----------|-------------|---------| -| `NEXT_PUBLIC_API_URL` | Backend API URL | `http://localhost:8000` | - ---- - -## Production Deployment - -### Required Cron Jobs Summary - -| Job | Frequency | Command | -|-----|-----------|---------| -| TLD Prices | Daily 03:00 UTC | `python scripts/seed_tld_prices.py` | -| Domain Check | Daily 06:00 UTC | Built-in scheduler | -| Auction Scrape | Hourly :30 | Built-in scheduler | -| Price Alerts | Daily 04:00 UTC | Built-in scheduler | - -**Note:** All cron jobs run automatically via APScheduler when the backend is running. External cron is only needed if you disable the built-in scheduler. - -### Docker (Recommended) - -```bash -# Clone repository -git clone https://git.6bit.ch/yvg/pounce.git -cd pounce - -# Set environment variables -export SECRET_KEY=$(python3 -c "import secrets; print(secrets.token_hex(32))") - -# Build and start -docker-compose up -d --build -``` - -### Manual Deployment - -See [DEPLOYMENT.md](./DEPLOYMENT.md) for detailed instructions including: -- Systemd service setup -- PM2 for Node.js -- Nginx reverse proxy configuration -- SSL/HTTPS with Let's Encrypt -- PostgreSQL database setup - ---- - -## Domain Checking Methods - -pounce uses multiple methods to verify domain availability: - -1. **Custom RDAP** — For TLDs with own RDAP servers (e.g., `.ch`, `.li` via `rdap.nic.ch`) -2. **RDAP via whodap** — Modern protocol with detailed data, supported by major registries -3. **WHOIS (Fallback)** — Traditional protocol for domains without RDAP support -4. **DNS (Quick check)** — Fast initial availability check via DNS queries - -The system automatically falls back to the next method if one fails. - -### Supported TLDs with Custom RDAP - -| TLD | RDAP Server | Notes | -|-----|-------------|-------| -| `.ch` | `rdap.nic.ch` | Swiss domains - WHOIS blocked | -| `.li` | `rdap.nic.ch` | Liechtenstein - same registry | - -### Standard RDAP TLDs - -`.com`, `.net`, `.org`, `.info`, `.biz`, `.io`, `.co`, `.ai`, `.app`, `.dev`, `.xyz`, `.me`, and many more. - ---- - -## TLD Price Scraping - -pounce automatically scrapes TLD prices from public sources for price intelligence and trend tracking. - -### Data Source - -**Primary:** Porkbun Public API (no API key required!) - -``` -POST https://api.porkbun.com/api/json/v3/pricing/get -``` - -- ✅ **886+ TLDs** with pricing -- ✅ Registration, Renewal, Transfer prices -- ✅ No authentication needed -- ✅ Updated daily via scheduler - -### Scheduler Jobs (Built-in APScheduler) - -The backend includes a built-in scheduler that starts automatically with the application. **No external cron jobs needed!** - -| Job | Schedule | Description | -|-----|----------|-------------| -| **Domain Check** | Daily at configured hour (default: 06:00 UTC) | Checks all watched domains for availability | -| **TLD Price Scrape** | Daily at 03:00 UTC | Scrapes 886+ TLD prices from Porkbun API | -| **Price Change Alerts** | Daily at 04:00 UTC | Sends email for price changes >5% | -| **Auction Scrape** | Every hour at :30 | Scrapes domain auctions from ExpiredDomains | - -**Scheduler is enabled by default.** When you run `uvicorn app.main:app`, the scheduler starts automatically via the lifespan handler. - -### External Cron Jobs (Production - Optional) - -If you want more control, you can disable the built-in scheduler and use system cron: - -```bash -# Disable built-in scheduler by setting env var -DISABLE_SCHEDULER=true - -# Add to crontab (crontab -e) -# TLD prices daily at 03:00 UTC -0 3 * * * cd /path/to/pounce/backend && source venv/bin/activate && python scripts/seed_tld_prices.py >> /var/log/pounce/tld_scrape.log 2>&1 - -# Domain check daily at 06:00 UTC -0 6 * * * cd /path/to/pounce/backend && source venv/bin/activate && python -c "import asyncio; from app.scheduler import check_all_domains; asyncio.run(check_all_domains())" >> /var/log/pounce/domain_check.log 2>&1 - -# Auction scrape hourly at :30 -30 * * * * cd /path/to/pounce/backend && source venv/bin/activate && python -c "import asyncio; from app.scheduler import scrape_auctions; asyncio.run(scrape_auctions())" >> /var/log/pounce/auction_scrape.log 2>&1 - -# Price alerts daily at 04:00 UTC -0 4 * * * cd /path/to/pounce/backend && source venv/bin/activate && python -c "import asyncio; from app.scheduler import check_price_changes; asyncio.run(check_price_changes())" >> /var/log/pounce/price_alerts.log 2>&1 -``` - -### PM2 with Scheduler (Recommended for Production) - -The easiest production setup is PM2 which keeps the backend running with its built-in scheduler: - -```bash -cd backend && source venv/bin/activate -pm2 start "uvicorn app.main:app --host 0.0.0.0 --port 8000" --name pounce-api +# Save & auto-restart pm2 save pm2 startup ``` -This way, the APScheduler handles all cron jobs automatically. - -### Manual Scrape - -Trigger a manual TLD price scrape: - +**With Docker:** ```bash -curl -X POST http://localhost:8000/api/v1/admin/scrape-tld-prices +docker-compose up -d --build ``` -Response: -```json -{ - "message": "TLD price scrape completed", - "status": "success", - "tlds_scraped": 886, - "prices_saved": 886, - "sources_succeeded": 1 +### 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; + } } ``` -### Database Stats +### 6. Stripe Webhook -Get current TLD price data statistics: - -```bash -curl http://localhost:8000/api/v1/admin/tld-prices/stats +Configure Stripe to send webhooks to: +``` +https://api.pounce.ch/api/v1/webhooks/stripe ``` -Response: -```json -{ - "total_records": 886, - "unique_tlds": 886, - "unique_registrars": 1, - "latest_record": "2025-12-08T08:04:54", - "data_range_days": 0 -} -``` - -### Price Data Model - -```python -TLDPrice: - - tld: str # e.g., "com", "io", "ai" - - registrar: str # e.g., "porkbun" - - registration_price: float - - renewal_price: float - - transfer_price: float - - currency: str # "USD" - - recorded_at: datetime # For historical tracking -``` - -### Future Data Sources - -The scraper architecture supports multiple sources: - -1. **Porkbun API** ✅ (active) -2. **TLD-List.com** (blocked - placeholder) -3. **Namecheap Affiliate** (requires signup) -4. **Cloudflare** (requires account) +Events to enable: +- `checkout.session.completed` +- `customer.subscription.updated` +- `customer.subscription.deleted` +- `invoice.payment_succeeded` +- `invoice.payment_failed` --- -## Email Notifications +## 📊 Scheduler Jobs (Automatic) -pounce supports automated email notifications for domain availability and price changes. +The backend includes APScheduler that runs automatically: -### Setup (Zoho Mail) - -Configure SMTP settings in your `.env`: - -```env -# Zoho Mail Configuration (Recommended) -SMTP_HOST=smtp.zoho.eu -SMTP_PORT=465 -SMTP_USER=hello@pounce.ch -SMTP_PASSWORD=your-zoho-app-password -SMTP_FROM_EMAIL=hello@pounce.ch -SMTP_FROM_NAME=pounce -SMTP_USE_TLS=false -SMTP_USE_SSL=true -CONTACT_EMAIL=hello@pounce.ch -``` - -Alternative providers: -```env -# Gmail (port 587, STARTTLS) -SMTP_HOST=smtp.gmail.com -SMTP_PORT=587 -SMTP_USE_SSL=false -SMTP_USE_TLS=true -``` - -### Notification Types - -| Type | Trigger | Description | -|------|---------|-------------| -| **Domain Available** | Watched domain becomes available | Instant alert to domain owner | -| **Price Change Alert** | TLD price changes >5% | Daily batch after price scrape | -| **Weekly Digest** | Every Sunday | Summary of watched domains & price trends | - -### Email Templates - -All emails feature: -- Dark mode branded design matching the app -- Clear call-to-action buttons -- Direct links to register domains -- Unsubscribe options - -### Test Email - -```bash -# Via Python -cd backend && python3 -c " -import asyncio -from app.services.email_service import email_service - -async def test(): - result = await email_service.send_domain_available_alert( - 'test@example.com', - 'example.com', - 'Test User' - ) - print('Email sent:', result) - -asyncio.run(test()) -" -``` +| Job | Schedule | Description | +|-----|----------|-------------| +| TLD Price Scrape | Daily 03:00 UTC | Scrapes 886+ TLDs from Porkbun | +| Auction Scrape | Hourly :30 | Scrapes from ExpiredDomains | +| Domain Check | Daily 06:00 UTC | Checks all watched domains | +| Price Alerts | Daily 04:00 UTC | Sends email for >5% changes | +| Sniper Alert Match | Every 15 min | Matches auctions to alerts | --- -## Design System +## 💳 Subscription Tiers -### Colors -- **Background:** Dark (`#0a0a0a`) -- **Foreground:** Light (`#fafafa`) -- **Accent:** Teal (`#00d4aa`) -- **Warning:** Orange (`#f97316`) -- **Success:** Green (same as accent) - -### Typography -- **Display:** Playfair Display (serif) -- **Body:** Inter (sans-serif) -- **Mono:** JetBrains Mono (code) - -### Components -- All icons use outlined style (Lucide React) -- Minimalist, award-winning aesthetic -- Smooth animations and transitions -- Responsive design (mobile-first) +| 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 | --- -## Development +## 🔗 API Endpoints (New) -### Backend +### 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 | -```bash -cd backend -source venv/bin/activate +### 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 | -# Run with auto-reload -uvicorn app.main:app --reload - -# API docs available at: -# http://localhost:8000/docs (Swagger) -# http://localhost:8000/redoc (ReDoc) -``` - -### Frontend - -```bash -cd frontend - -# Development server -npm run dev - -# Lint -npm run lint - -# Build for production -npm run build - -# Start production server -npm start -``` +### 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+ | --- -## Troubleshooting +## 🎨 UI Components -### Backend won't start +The Command Center uses these reusable components from `PremiumTable.tsx`: -1. Check Python version: `python3 --version` (needs 3.12+) -2. Ensure virtual environment is activated -3. Check `.env` file exists and has valid `SECRET_KEY` -4. Check for syntax errors in config files - -### Frontend can't connect to backend - -1. Ensure backend is running on port 8000 -2. Check `NEXT_PUBLIC_API_URL` in `.env.local` -3. Verify CORS origins in backend `.env` -4. Check browser console for CORS errors - -### Domain checks failing - -1. Some TLDs may have rate limits -2. Certain ccTLDs require special handling (see below) -3. Check backend logs for specific errors - -**Swiss/Liechtenstein domains (.ch, .li):** -- WHOIS is blocked for automated requests -- pounce uses the official `rdap.nic.ch` API instead -- This is automatic - no configuration needed - -**Other ccTLDs:** -- Some country code TLDs don't support RDAP -- pounce falls back to DNS checking -- Results may be less detailed (no registrar info) - -### Database errors - -1. Delete `domainwatch.db` to reset (dev only) -2. Check database URL format in `.env` -3. Ensure write permissions on database directory +| 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 | --- -## License +## 🐛 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 +## 📧 Support -For issues and feature requests, please open a GitHub issue or contact support@pounce.dev +For issues and feature requests, please open a GitHub issue or contact support@pounce.ch