# pounce — Domain Availability Monitoring A professional full-stack application for monitoring domain name availability with TLD price tracking and intelligence. --- ## ⚠️ Important: After Fresh Clone / Database Reset If you just cloned the repo or reset the database, run these commands to populate data: ```bash cd backend source venv/bin/activate # 1. Initialize database (creates 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 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()) " ``` 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) **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 ### ⚠️ 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 - **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 ### 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 ``` 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 ├── 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 └── README.md ``` --- ## Pages Overview | 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 & Newsletter signup | No | | `/privacy` | Privacy policy | No | | `/terms` | Terms of service | No | | `/imprint` | Legal imprint | No | *Unauthenticated users see limited data with shimmer effects --- ## Full Setup Guide ### Prerequisites - Python 3.12+ - Node.js 18+ - npm or yarn ### 🚀 One-Command Setup (Fresh 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 ``` ### 🏃 Starting the Application **Option A: Two Terminals (Recommended for Development)** ```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 ``` **Option B: Single Command (Quick)** ```bash chmod +x start.sh ./start.sh ``` **Option C: PM2 (Production)** ```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 ``` ### 🔧 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 | ### Admin | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/api/v1/admin/users` | List all users | | POST | `/api/v1/admin/upgrade-user` | Upgrade user subscription | | POST | `/api/v1/admin/scrape-tld-prices` | Manually trigger TLD price scrape | | GET | `/api/v1/admin/tld-prices/stats` | Get TLD price database stats | --- ## 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 pm2 save pm2 startup ``` This way, the APScheduler handles all cron jobs automatically. ### Manual Scrape Trigger a manual TLD price scrape: ```bash curl -X POST http://localhost:8000/api/v1/admin/scrape-tld-prices ``` Response: ```json { "message": "TLD price scrape completed", "status": "success", "tlds_scraped": 886, "prices_saved": 886, "sources_succeeded": 1 } ``` ### Database Stats Get current TLD price data statistics: ```bash curl http://localhost:8000/api/v1/admin/tld-prices/stats ``` 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) --- ## Email Notifications pounce supports automated email notifications for domain availability and price changes. ### 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()) " ``` --- ## Design System ### 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) --- ## Development ### Backend ```bash cd backend source venv/bin/activate # 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 ``` --- ## Troubleshooting ### Backend won't start 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 --- ## License MIT License --- ## Support For issues and feature requests, please open a GitHub issue or contact support@pounce.dev