diff --git a/.env.example b/.env.example index 407909f..d78534b 100644 --- a/.env.example +++ b/.env.example @@ -8,9 +8,9 @@ DATABASE_URL="postgresql://coop:CHANGE_PASSWORD@localhost:5432/coop_credits?sche POSTGRES_PASSWORD="CHANGE_THIS_PASSWORD" # Server -PORT=3001 +PORT=3002 NODE_ENV=development -API_URL=http://localhost:3001 +API_URL=http://localhost:3002 FRONTEND_URL=http://localhost:3000 # JWT Secret (generate with: openssl rand -hex 32) @@ -26,30 +26,24 @@ PLEX_REDIRECT_URI=http://localhost:3000/auth/callback # ========================================== # Tautulli (Watch Analytics) +# Backend uses host network mode to reach this # ========================================== -# Your Tautulli instance URL TAUTULLI_URL=http://172.20.1.255:8181 -# Get API key from: Tautulli Settings > Web Interface > API TAUTULLI_API_KEY="" -# Optional: Secret for webhook signature verification TAUTULLI_WEBHOOK_SECRET="" # ========================================== # Overseer (Content Requests) +# Backend uses host network mode to reach this # ========================================== -# Your Overseer instance URL OVERSEER_URL=http://172.20.1.225:5055 -# Get API key from: Overseer Settings > General OVERSEER_API_KEY="" # ========================================== -# Solana (Devnet for testing) +# Ko-fi (Optional Credit Purchases) +# Get verification token from Ko-fi Settings > Webhooks # ========================================== -SOLANA_RPC_URL=https://api.devnet.solana.com -SOLANA_PROGRAM_ID=CoopCredits111111111111111111111111111111111 -# Generate keypair with: npm run setup:solana -SOLANA_MINT_AUTHORITY_KEYPAIR="" -SOLANA_TOKEN_DECIMALS=6 +KOFI_VERIFICATION_TOKEN="" # ========================================== # Redis (optional caching) @@ -59,15 +53,13 @@ REDIS_PASSWORD="" # ========================================== # Encryption (generate with: openssl rand -base64 32) -# Used for encrypting wallet private keys +# Used for session cookie encryption # ========================================== ENCRYPTION_KEY="" # ========================================== # Frontend Public Variables # ========================================== -NEXT_PUBLIC_API_URL=http://localhost:3001 -NEXT_PUBLIC_SOLANA_NETWORK=devnet -NEXT_PUBLIC_SOLANA_RPC_URL=https://api.devnet.solana.com +NEXT_PUBLIC_API_URL=http://localhost:3002 NEXT_PUBLIC_APP_NAME=CoopCredits NEXT_PUBLIC_APP_URL=http://localhost:3000 diff --git a/FRONTEND-TODO.md b/FRONTEND-TODO.md index e8e65e9..a1e3b4a 100644 --- a/FRONTEND-TODO.md +++ b/FRONTEND-TODO.md @@ -1,21 +1,24 @@ # Frontend Todo List -## Phase 1: Accessibility & Onboarding (Priority: 1) -- [x] **Landing Page**: Marketing page at `/` with "How it Works". -- [x] **Onboarding Flow**: Better guided steps for new users to set up their wallet. -- [x] **Mobile Responsiveness**: Audit and fix layout shifts on small screens. +## Completed +- [x] **Landing Page** — Barnyard-themed "Enter the Coop" page +- [x] **Login Flow** — Plex OAuth with auto-redirect +- [x] **Dashboard** — Nest Egg balance, Eggs Ordered, Market Price cards +- [x] **Watch History** — Personal watch events with credits earned +- [x] **Request History** — Content requests with status filtering +- [x] **Search Request Modal** — Overseer search with posters, episode counts, dynamic pricing +- [x] **Buy Credits Modal** — Ko-fi store links for $5/500 and $10/1000 +- [x] **Activity Feed** — Recent transactions sidebar +- [x] **Leaderboard** — Rich Birds (top earners) and Lazy Birds (top watchers) +- [x] **Socket.io Integration** — Live balance updates, credit earned toasts +- [x] **Disk Space Bar** — The Silo: used/total GB with scarcity warning +- [x] **Admin Dashboard** — The Flock, Barn Rules, Donations, Egg Stats, The Feed Queue +- [x] **Mobile Responsiveness** — Barnyard cards stack on small screens +- [x] **Barnyard Theme** — Comic Sans, thick borders, earthy palette, jokey copy -## Phase 2: Engagement & Real-time (Priority: 2) -- [x] **Socket.io Integration**: Connect to backend for live watch events. -- [x] **Live Reward Toasts**: Show "You earned 10 $COOP" alerts in real-time. -- [x] **Activity Feed**: A small sidebar or section for "Global Recent Earners". - -## Phase 3: Core Spend Loop (Priority: 3) -- [x] **Overseer Request UI**: Create `SearchRequestModal` component. -- [x] **Request History**: Show status of Overseer requests (Pending/Processing/Available). -- [x] **Balance Refresh**: Ensure balance updates immediately after a request. - -## Phase 4: Advanced Features (Priority: 4) -- [x] **Browser Wallets**: Connect Phantom/Solflare/Backpack. -- [ ] **Referral System**: Earn bonus $COOP for inviting other Plex users. (Requires DB Migration) -- [x] **Leaderboard**: Weekly/Monthly top watchers. +## Future Ideas +- [ ] **Referral System** — Earn bonus $COOP for inviting Plex users +- [ ] **Seasonal Events** — Double credits weekends, holiday multipliers +- [ ] **Achievements** — Badges for watch milestones (100 movies, 1000 hours, etc.) +- [ ] **Notifications** — Browser push for request approvals +- [ ] **Dark Mode** — Night coop theme diff --git a/INFRASTRUCTURE-SUMMARY.md b/INFRASTRUCTURE-SUMMARY.md index ef97dd3..b97dcd3 100644 --- a/INFRASTRUCTURE-SUMMARY.md +++ b/INFRASTRUCTURE-SUMMARY.md @@ -5,25 +5,22 @@ | Service | IP Address | Port | Integration Role | |----------|----------------|------|--------------------------------| | Plex | 172.20.1.220 | 32400| User authentication, content | -| Overseer | 172.20.1.225 | 5055 | Content requests, $COOP spend | +| Overseer | 172.20.1.225 | 5055 | Content requests, disk space | | Tautulli | 172.20.1.255 | 8181 | Watch tracking, $COOP earn | | Website | coop.hobokenchicken.com | 443 | User dashboard, admin panel | ## Files Created/Updated ### Configuration Files -- `.env.production` - Production environment template +- `.env` - Production environment +- `docker-compose.yml` - Docker orchestration - `docker-compose.prod.yml` - Production Docker orchestration -- `docker/nginx/nginx.prod.conf` - Nginx reverse proxy config - -### Deployment Scripts -- `deployment/setup-infrastructure.sh` - Initial infrastructure setup -- `deployment/deploy-production.sh` - Production deployment -- `deployment/health-check.sh` - Service health monitoring ### Documentation - `docs/SETUP-INFRASTRUCTURE.md` - Step-by-step setup guide - `docs/INFRASTRUCTURE.md` - Architecture and network documentation +- `docs/CADDY-CONFIG.md` - Reverse proxy configuration +- `docs/TROUBLESHOOTING.md` - Common issues and fixes ## Network Architecture @@ -32,14 +29,15 @@ Internet │ HTTPS ▼ ┌─────────────┐ -│ Nginx │ (80/443) - SSL termination, rate limiting +│ Caddy │ (443) - SSL termination └──────┬──────┘ │ ┌───┴───┐ ▼ ▼ ┌──────┐ ┌──────┐ -│Frontend│ │Backend│ -│:3000 │ │:3001 │ +│Front-│ │Back- │ +│end │ │end │ +│:3000 │ │:3002 │ └──────┘ └───┬───┘ │ ┌──────┼──────┐ @@ -61,23 +59,18 @@ Internet ## Quick Deployment Commands ```bash -# 1. Setup infrastructure -npm run setup:infra +# 1. Setup +cp .env.example .env +# Edit .env with your API keys -# 2. Edit .env with your API keys -nano .env +# 2. Deploy +docker-compose up -d --build -# 3. Setup Solana -npm run setup:solana +# 3. Initialize database (first time only) +docker exec coop-backend npx prisma db push --accept-data-loss -# 4. Deploy -npm run deploy:prod - -# 5. Check health -npm run health - -# 6. Watch mode monitoring -npm run health -- --watch +# 4. Check health +curl https://coop.hobokenchicken.com/health ``` ## Integration Points @@ -85,84 +78,82 @@ npm run health -- --watch ### Tautulli → CoopCredits - **Trigger**: Watch events - **Webhook URL**: `https://coop.hobokenchicken.com/webhooks/tautulli` -- **Action**: Mint $COOP tokens +- **Action**: Create WatchEvent + Transaction (EARN), update user balance -### Overseer → CoopCredits +### Overseer → CoopCredits - **Trigger**: Request approval/decline - **Webhook URL**: `https://coop.hobokenchicken.com/webhooks/overseer` -- **Action**: Burn/spend $COOP tokens +- **Action**: Update ContentRequest status (refund if declined) ### Plex → CoopCredits - **Trigger**: User login - **Method**: OAuth via plex.tv -- **Action**: Authenticate users +- **Action**: Authenticate users, create session + +### Ko-fi → CoopCredits +- **Trigger**: Store purchase +- **Webhook URL**: `https://coop.hobokenchicken.com/webhooks/kofi` +- **Action**: Create KofiPayment, credit user if email matches ## Security Features -1. **SSL/TLS**: Let's Encrypt or custom certificates -2. **Rate Limiting**: Nginx level protection +1. **SSL/TLS**: Caddy with Let's Encrypt +2. **Rate Limiting**: 1000 req/15min per IP 3. **Firewall**: UFW rules for local network -4. **Secrets**: Encrypted in `.env` file -5. **Wallet Keys**: AES-256-GCM encrypted in database -6. **CORS**: Configured for your domain -7. **JWT**: Secure session tokens +4. **Secrets**: In `.env` file (gitignored) +5. **CORS**: Configured for your domain +6. **JWT**: Secure session tokens +7. **Ko-fi Verification**: Token check on webhook ## Monitoring ```bash # Health check -./deployment/health-check.sh - -# Watch mode (continuous) -./deployment/health-check.sh --watch +curl https://coop.hobokenchicken.com/health # Docker logs -docker-compose -f docker-compose.prod.yml logs -f +docker-compose logs -f # Specific service -docker-compose -f docker-compose.prod.yml logs -f backend +docker-compose logs -f backend ``` ## Backup Strategy ```bash # Database backup -docker-compose -f docker-compose.prod.yml exec -T postgres pg_dump -U coop coop_credits > backup_$(date +%Y%m%d).sql +docker exec -T coop-postgres pg_dump -U coop coop_credits > backup_$(date +%Y%m%d).sql # Environment backup cp .env .env.backup.$(date +%Y%m%d) - -# Wallet keys (secure offsite storage) -# - Solana mint authority keypair -# - Encryption key from .env ``` ## Next Steps -1. **Run setup**: `npm run setup:infra` +1. **Run setup**: `cp .env.example .env` and edit 2. **Get API keys**: - Tautulli: http://172.20.1.255:8181 → Settings → API - Overseer: http://172.20.1.225:5055 → Settings → General - Plex: https://plex.tv/claim -3. **Edit .env** with your keys -4. **Setup Solana**: `npm run setup:solana` -5. **Deploy**: `npm run deploy:prod` -6. **Configure webhooks** in Tautulli and Overseer -7. **Test**: `npm run health` +3. **Deploy**: `docker-compose up -d --build` +4. **Configure webhooks** in Tautulli, Overseer, and Ko-fi +5. **Set admin**: `UPDATE users SET is_admin = true WHERE ...` +6. **Test**: `curl https://coop.hobokenchicken.com/health` ## Troubleshooting | Issue | Solution | |-------|----------| -| Cannot reach local services | Check Docker network: `docker network ls` | -| Webhook not received | Check Nginx logs: `docker/nginx/logs/access.log` | +| Cannot reach local services | Check backend `network_mode: host` | +| Webhook not received | Check Caddy routes to port 3002 | | Database connection failed | Verify `.env` DATABASE_URL | -| SSL error | Check certificate paths in nginx config | +| SSL error | Check Caddy certificate status | | CORS errors | Verify CORS_ORIGINS in `.env` | +| Request stuck PENDING | Use admin **SYNC** button or check Overseer webhook | ## Support Resources - **Setup Guide**: `docs/SETUP-INFRASTRUCTURE.md` - **Architecture**: `docs/INFRASTRUCTURE.md` -- **Health Check**: `npm run health` -- **Logs**: `npm run docker:prod:logs` +- **Troubleshooting**: `docs/TROUBLESHOOTING.md` +- **Caddy Config**: `docs/CADDY-CONFIG.md` diff --git a/README.md b/README.md index e41eafa..8f18b7b 100644 --- a/README.md +++ b/README.md @@ -1,89 +1,192 @@ -# CoopCoins: Our Family Media Rewards +# CoopCredits ($COOP) — Hoboken Chicken Media Rewards -Welcome to our private media rewards system for the Hoboken Chicken family and friends! It's powered by Solana and integrates with our Plex, Tautulli, and Overseer setup. Earn CoopCoins ($COOP) for enjoying media, and use them to request new content! +A jokey barnyard-themed rewards system for the Hoboken Chicken family and friends. Watch stuff on Plex, earn $COOP credits, request new media through Overseer. Buy extra credits via Ko-fi when the silo runs low. -## System Architecture +> **Theme:** Everything is chickens, eggs, coops, and corn. Comic Sans / Chalkboard font, thick borders, earthy palette. + +## Architecture ``` ┌─────────────────────────────────────────────────────────────────────────────┐ │ coop.hobokenchicken.com │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌─────────────────┐ │ -│ │ Next.js │ │ Express │ │ PostgreSQL │ │ Solana Devnet │ │ -│ │ Frontend │◄─┤ API │◄─┤ Database │◄─┤ $COOP SPL │ │ -│ │ │ │ │ │ │ │ Token │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ └─────────────────┘ │ -│ ▲ ▲ │ -│ │ │ │ -│ Plex OAuth Tautulli Webhooks │ -│ │ │ │ -│ ▼ ▼ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Plex Server │ │ Tautulli │ │ Overseer │ │ -│ │ (Content) │ │ (Analytics) │ │ (Requests) │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Next.js │ │ Express │ │ PostgreSQL │ │ +│ │ Frontend │◄─┤ API │◄─┤ Database │ │ +│ │ (3000) │ │ (3002) │ │ (5432) │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ▲ ▲ ▲ │ +│ │ │ │ │ +│ Plex OAuth Tautulli Webhooks Ko-fi Webhooks │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Plex Server │ │ Tautulli │ │ Ko-fi │ │ +│ │172.20.1.220 │ │172.20.1.255 │ │ (Payments) │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ │ +│ ┌──────────────┐ │ +│ │ Overseer │ (Requests & disk space for dynamic pricing) │ +│ │172.20.1.225 │ │ +│ └──────────────┘ │ └─────────────────────────────────────────────────────────────────────────────┘ ``` +## Key Features + +- **Plex OAuth Login** — No passwords, just Plex auth +- **Watch-to-Earn** — Tautulli webhooks credit $COOP for watched content (30-day backfill on signup) +- **Request-to-Spend** — Request movies/shows through Overseer, pay $COOP +- **Dynamic Pricing** — Request costs rise as disk space fills (scarcity multiplier) +- **TV Episode Tiers** — Long-running shows cost more per episode count +- **Ko-fi Purchases** — Buy $COOP with real money via Ko-fi store items +- **Admin Dashboard** — Manage users, view donations, sync request statuses, adjust barn rules +- **Barnyard Theme** — "Peck for Feed", "Join the Flock", thick borders, earthy palette + ## Quick Start ```bash -# 1. Clone and install dependencies +# 1. Clone git clone coop-credits cd coop-credits -npm install -# 2. Set up environment +# 2. Install dependencies +npm run install:all + +# 3. Configure environment cp .env.example .env -# Edit .env with your values - -# 3. Create public directory -mkdir -p frontend/public +# Edit .env with your API keys (Plex, Tautulli, Overseer, Ko-fi) # 4. Start services docker-compose up -d --build # 5. Initialize database (first time only) -docker-compose exec backend npx prisma db push +docker exec coop-backend npx prisma db push --accept-data-loss # 6. Configure Caddy (see docs/CADDY-CONFIG.md) -# Services expose ports 3000 (frontend) and 3001 (backend) on localhost ``` -See `docs/CADDY-CONFIG.md` for reverse proxy setup. - ## Project Structure ``` coop-credits/ -├── anchor-program/ # Solana Anchor program for $COOP token -├── backend/ # Express API server -├── frontend/ # Next.js dashboard -├── shared/ # Shared types and utilities +├── backend/ # Express API server (port 3002) +│ ├── src/ +│ │ ├── routes/ # API routes (auth, users, wallet, overseer, admin, webhooks) +│ │ ├── services/ # Tautulli backfill, socket handlers +│ │ ├── middleware/ # Auth, rate limiting, error handling +│ │ └── utils/ # Prisma client +│ └── prisma/ +│ └── schema.prisma # DB schema (User, Transaction, WatchEvent, ContentRequest, KofiPayment, etc.) +├── frontend/ # Next.js 16 dashboard (port 3000) +│ └── src/ +│ ├── app/ +│ │ ├── page.tsx # Landing page ("Enter the Coop") +│ │ ├── login/page.tsx # Plex OAuth login +│ │ ├── dashboard/page.tsx # User dashboard +│ │ ├── admin/page.tsx # Admin panel +│ │ └── dashboard/components/ # WatchHistory, RequestHistory, SearchRequestModal, BuyCreditsModal, etc. +│ └── lib/ +│ ├── api.ts # API client +│ ├── store.ts # Zustand auth store +│ └── socket.ts # Socket.io connection ├── docs/ # Documentation -│ └── CADDY-CONFIG.md # Reverse proxy setup -├── docker-compose.yml # Docker orchestration (no nginx) -└── .env.example # Environment template +│ ├── CADDY-CONFIG.md # Reverse proxy setup +│ ├── INFRASTRUCTURE.md # Network architecture +│ ├── SETUP-INFRASTRUCTURE.md +│ └── TROUBLESHOOTING.md +├── docker-compose.yml # Docker orchestration +└── .env.example # Environment template ``` -**Note:** No internal reverse proxy. Use Caddy/Nginx/Traefik externally. - ## User Flow -1. **Login** → User visits coop.hobokenchicken.com, authenticates with Plex -2. **Wallet** → Auto-created Solana wallet or connect existing -3. **Watch** → Viewing on Plex → Tautulli triggers → $COOP minted -4. **Earn** → Real-time balance updates on dashboard -5. **Spend** → Request content on Overseer → $COOP deducted -6. **History** → Full transaction history visible on dashboard +1. **Login** → Visit coop.hobokenchicken.com, click "Join the Flock", authenticate with Plex +2. **Auto-Backfill** → First login triggers 30-day Tautulli history import (DB-only credits) +3. **Watch** → Viewing on Plex → Tautulli webhook → $COOP credited to DB balance +4. **Earn** → Real-time balance updates via Socket.io on dashboard +5. **Spend** → "Peck for Feed" → Search → Request content → $COOP deducted immediately +6. **Buy More** → "Buy Feed" → Ko-fi store → Credits auto-delivered via webhook ## Admin Features -- System overview and analytics -- User management and balance adjustments -- Minting rate controls -- Spending controls and promotions -- Emergency pause and recovery +Access `/admin` with an admin account: +- **The Flock** — User list with search, reward bonuses, manual backfill, promote/demote admins +- **Barn Rules** — Edit credits per minute, min watch %/minutes, movie/TV costs +- **Donations** — View all incoming Ko-fi payments with status (CREDITED / UNCLAIMED) +- **The Feed Queue** — All content requests across users, manual Overseer sync button +- **Egg Stats** — Analytics cards: flock size, total $COOP, total requests, pending count ## Environment Variables -See `backend/.env.example` and `frontend/.env.local.example` for full configuration. +See `.env.example` for full configuration. Key vars: + +```env +# Database +DATABASE_URL="postgresql://coop:password@localhost:5432/coop_credits?schema=public" + +# Server +PORT=3002 +API_URL=https://coop.hobokenchicken.com + +# JWT (generate: openssl rand -hex 32) +JWT_SECRET="..." + +# Plex OAuth +PLEX_CLIENT_ID="..." +PLEX_CLIENT_SECRET="..." +PLEX_REDIRECT_URI=https://coop.hobokenchicken.com/auth/callback + +# Tautulli (host network mode required) +TAUTULLI_URL=http://172.20.1.255:8181 +TAUTULLI_API_KEY="..." + +# Overseer (host network mode required) +OVERSEER_URL=http://172.20.1.225:5055 +OVERSEER_API_KEY="..." + +# Ko-fi +KOFI_VERIFICATION_TOKEN="..." + +# Security +ENCRYPTION_KEY="..." # openssl rand -base64 32 +``` + +## Dynamic Pricing + +Request costs are adjusted by disk scarcity: +- **Multiplier** = `1 + (usedPercent × 2)` +- At 0% disk used → 1× base cost +- At 50% disk used → 2× base cost +- At 90% disk used → 2.8× base cost + +TV shows also have episode tier multipliers on top of scarcity: +- ≤50 episodes → base +- 51-100 → 1.3× +- 101-200 → 1.6× +- 201-400 → 2.0× +- 400+ → 2.5× + +## Theme + +- **Primary:** Terracotta (`#C06F4A`) +- **Secondary:** Duck Egg Blue (`#D4E4E0`) +- **Accent:** Wheat (`#E8D5B5`) +- **Muted:** Feather Grey (`#C4B5AA`) +- **Font:** Comic Sans MS / Chalkboard SE (fallback system-ui) +- **Style:** Thick 4px borders, hard black box shadows, rounded-3xl corners + +## Tech Stack + +| Layer | Technology | +|-------|-----------| +| Frontend | Next.js 16, React 19, TypeScript, Tailwind CSS, shadcn/ui, Zustand | +| Backend | Express.js, TypeScript, Prisma ORM, Socket.io | +| Database | PostgreSQL 16 | +| Cache | Redis 7 (sessions, Socket.io adapter) | +| Auth | Plex OAuth 2.0 + JWT | +| Payments | Ko-fi webhooks | +| Deployment | Docker Compose, Caddy reverse proxy | + +## License + +Private — Hoboken Chicken family use only. diff --git a/SETUP.md b/SETUP.md index 6528f88..093a43c 100644 --- a/SETUP.md +++ b/SETUP.md @@ -1,13 +1,13 @@ # CoopCredits Setup Guide -This guide will walk you through setting up the complete CoopCredits ecosystem. +This guide walks you through setting up the complete CoopCredits ecosystem. ## Prerequisites - Node.js 20+ - Docker & Docker Compose -- Solana CLI (for blockchain development) -- Anchor Framework (for Solana program deployment) +- Access to Plex, Tautulli, and Overseer instances +- Domain name (for production) ## Quick Start @@ -31,48 +31,20 @@ nano .env Required environment variables: - `DATABASE_URL` - PostgreSQL connection string -- `JWT_SECRET` - Random string for JWT signing +- `JWT_SECRET` - Random string for JWT signing (openssl rand -hex 32) - `PLEX_CLIENT_ID` & `PLEX_CLIENT_SECRET` - From Plex.tv - `TAUTULLI_API_KEY` - From Tautulli settings - `OVERSEER_API_KEY` - From Overseer settings -- `SOLANA_MINT_AUTHORITY_KEYPAIR` - Will be generated in step 3 +- `ENCRYPTION_KEY` - For encrypting session data (openssl rand -base64 32) +- `KOFI_VERIFICATION_TOKEN` - From Ko-fi settings (optional) -### 3. Solana Setup - -```bash -# Run the Solana setup script -npm run setup:solana - -# This will: -# - Install Solana CLI if needed -# - Create a devnet keypair -# - Request airdrop (2 SOL) -# - Output the private key for your .env file -``` - -Copy the `SOLANA_MINT_AUTHORITY_KEYPAIR` value into your `.env` file. - -### 4. Deploy Solana Program - -```bash -cd anchor-program - -# Build the program -anchor build - -# Deploy to devnet -anchor deploy - -# Note the Program ID and update .env SOLANA_PROGRAM_ID -``` - -### 5. Database Setup +### 3. Database Setup ```bash # Start PostgreSQL and Redis docker-compose up -d postgres redis -# Push schema to database (no migrations needed for fresh setup) +# Push schema to database cd backend npx prisma db push @@ -80,44 +52,34 @@ npx prisma db push npx prisma generate ``` -### 6. Start Development +### 4. Start Development ```bash # Start all services npm run dev # Or individually: -npm run dev:backend # API on port 3001 -npm run dev:frontend # Next.js on port 3000 +npm run dev:backend # API on port 3002 +npm run dev:frontend # Next.js on port 3000 ``` -### 7. Configure Tautulli Webhook - -1. Open Tautulli Settings -2. Go to Notification Agents → Add Agent → Webhook -3. Configure: - - Webhook URL: `http://your-server:3001/webhooks/tautulli` - - Webhook Method: POST - - JSON Payload: See webhook template in Admin Dashboard -4. Enable "Notify on Watched" - -### 8. Production Deployment +### 5. Production Deployment ```bash # Create public directory (required for build) mkdir -p frontend/public -# Deploy (Caddy handles SSL termination) +# Deploy docker-compose up -d --build ``` -**Note:** This setup runs HTTP only on port 80. Place behind Caddy or another reverse proxy for SSL termination. +**Note:** This runs HTTP internally. Place behind Caddy or another reverse proxy for SSL termination. Backend runs on port 3002. ## Architecture Overview ``` ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ -│ User Browser │────▶│ Next.js (3000) │────▶│ Express (3001) │ +│ User Browser │────▶│ Next.js (3000) │────▶│ Express (3002) │ └─────────────────┘ └─────────────────┘ └─────────────────┘ │ ┌─────────────────┐ │ @@ -126,8 +88,8 @@ docker-compose up -d --build └─────────────────┘ ▲ ┌─────────────────┐ - │ Solana Devnet │ - │ ($COOP Token) │ + │ Ko-fi │ + │ (Purchases) │ └─────────────────┘ ▲ ┌───────────────────────┴───────────────────────┐ @@ -141,19 +103,20 @@ docker-compose up -d --build ## User Flow 1. **Login**: User authenticates with Plex OAuth -2. **Wallet**: Auto-created Solana wallet (or connect existing) -3. **Watch**: Viewing on Plex → Tautulli triggers webhook -4. **Earn**: Backend validates → Mints $COOP tokens -5. **Spend**: Request content on Overseer → Deducts $COOP +2. **Backfill**: First login triggers 30-day Tautulli history import +3. **Watch**: Viewing on Plex → Tautulli triggers webhook → $COOP credited to DB +4. **Earn**: Real-time balance updates on dashboard via Socket.io +5. **Spend**: Request content on Overseer → $COOP deducted from DB balance +6. **Buy**: Ko-fi purchase → Webhook → $COOP added to DB balance ## Admin Features Access `/admin` with an admin account to: -- View system analytics -- Manage users and grant bonuses -- Configure minting rates -- Pause/resume minting -- Monitor transactions +- **The Flock** — User list with search, bonuses, backfill, promote/demote +- **Barn Rules** — Edit credits per minute, watch thresholds, request costs +- **Donations** — View Ko-fi payment history +- **The Feed Queue** — All content requests with Overseer sync button +- **Egg Stats** — Analytics cards (users, total credits, requests, pending) ## Troubleshooting @@ -162,30 +125,26 @@ Access `/admin` with an admin account to: # Reset database docker-compose down -v docker-compose up -d postgres -npx prisma migrate dev -``` - -### Solana Transaction Failures -```bash -# Check balance -solana balance - -# Request airdrop -solana airdrop 2 +cd backend && npx prisma db push ``` ### Tautulli Webhook Not Working 1. Check webhook URL is accessible -2. Verify `TAUTULLI_WEBHOOK_SECRET` matches +2. Verify `TAUTULLI_WEBHOOK_SECRET` matches (optional) 3. Check backend logs: `docker-compose logs backend` +### Request Stuck on PENDING +1. Check Overseer webhook is configured +2. Use admin dashboard **The Feed Queue** → **SYNC** button +3. Or use dashboard **Requests** tab → refresh button + ## Security Considerations -1. **Private Keys**: Never commit `.env` files +1. **Never commit `.env` files** 2. **JWT Secret**: Use a strong random string (32+ chars) 3. **Encryption Key**: Use `openssl rand -base64 32` -4. **SSL**: Always use HTTPS in production -5. **Rate Limiting**: Nginx config includes rate limits +4. **SSL**: Always use HTTPS in production (Caddy handles this) +5. **Rate Limiting**: 1000 req/15min per IP ## Support @@ -193,3 +152,4 @@ For issues or questions: 1. Check logs: `docker-compose logs -f` 2. Review environment variables 3. Verify all services are running: `docker-compose ps` +4. Check `docs/TROUBLESHOOTING.md` diff --git a/docs/CADDY-CONFIG.md b/docs/CADDY-CONFIG.md index 2e78d0b..b1226a8 100644 --- a/docs/CADDY-CONFIG.md +++ b/docs/CADDY-CONFIG.md @@ -1,41 +1,47 @@ # Caddy Configuration -Caddy handles SSL termination and routes to the application services. +Caddy handles SSL termination and routes to the application services running on `172.20.1.238`. ## Caddyfile Example ```caddy coop.hobokenchicken.com { # Frontend (Next.js) - handle_path /* { - reverse_proxy localhost:3000 + handle /* { + reverse_proxy 172.20.1.238:3000 } - # API routes - handle_path /api/* { - reverse_proxy localhost:3001 + # API routes — use handle (NOT handle_path) to preserve /api/ prefix + handle /api/* { + reverse_proxy 172.20.1.238:3002 } # Webhooks - handle_path /webhooks/* { - reverse_proxy localhost:3001 + handle /webhooks/* { + reverse_proxy 172.20.1.238:3002 } # WebSocket support - handle_path /socket.io/* { - reverse_proxy localhost:3001 + handle /socket.io/* { + reverse_proxy 172.20.1.238:3002 } } ``` +> **Critical:** Use `handle` (not `handle_path`) for `/api/*` and `/webhooks/*`. `handle_path` strips the path prefix, which breaks Express routing. + ## Docker Compose (no nginx) -Services bind to localhost only (`127.0.0.1`): +Services bind as follows: -- **Frontend**: `127.0.0.1:3000` -- **Backend**: `127.0.0.1:3001` -- **Postgres**: `5432` (internal only) -- **Redis**: `6379` (internal only) +- **Frontend**: `0.0.0.0:3000` (bridge network, accessible from Caddy) +- **Backend**: `localhost:3002` (host network, accessible from Caddy via host IP) +- **Postgres**: `127.0.0.1:5432` (localhost only) +- **Redis**: `127.0.0.1:6379` (localhost only) + +## Backend Port + +Backend runs on **port 3002** (not 3001). Caddy must proxy to `172.20.1.238:3002`. ## Start Stack @@ -43,8 +49,11 @@ Services bind to localhost only (`127.0.0.1`): # Start services docker-compose up -d -# Verify -curl http://localhost:3001/health +# Verify backend +curl http://172.20.1.238:3002/health + +# Verify frontend +curl http://172.20.1.238:3000 ``` Caddy automatically provisions SSL and routes traffic. diff --git a/docs/INFRASTRUCTURE.md b/docs/INFRASTRUCTURE.md index 77834ab..6281685 100644 --- a/docs/INFRASTRUCTURE.md +++ b/docs/INFRASTRUCTURE.md @@ -10,15 +10,16 @@ │ HTTPS (443) ▼ ┌─────────────────────────────────────────────────────────────────────────┐ -│ CoopCredits Server │ +│ CoopCredits Server (172.20.1.238) │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Nginx │ │ Frontend │ │ Backend │ │ PostgreSQL │ │ -│ │ (80) │──│ (Next.js) │──│ (Express) │──│ (5432) │ │ +│ │ Caddy │ │ Frontend │ │ Backend │ │ PostgreSQL │ │ +│ │ (reverse) │──│ (Next.js) │ │ (Express) │──│ (5432) │ │ +│ │ Proxy │ │ :3000 │ │ :3002 │ │ │ │ │ └──────────────┘ └──────────────┘ └──────┬───────┘ └──────────────┘ │ │ │ │ │ ┌──────────────┐ │ ┌──────────────┐ │ -│ │ Redis │◀────────┘ │ Anchor CLI │ │ -│ │ (6379) │ │ (Optional) │ │ +│ │ Redis │◀────────┘ │ (Ko-fi) │ │ +│ │ (6379) │ │ (Webhooks) │ │ │ └──────────────┘ └──────────────┘ │ └──────────────────────────────────┬──────────────────────────────────────┘ │ @@ -33,80 +34,115 @@ └──────────────┘ └──────────────┘ └──────────────┘ ``` +> **Important:** Backend runs in `network_mode: host` to reach Tautulli/Overseer on `172.20.1.0/24`. Docker bridge networking cannot route to this subnet. + ## Service Details +### Caddy Reverse Proxy (Separate Machine) +- **Handles:** SSL termination (Let's Encrypt), routing +- **Routes:** + - `/*` → Frontend `:3000` + - `/api/*` → Backend `:3002` (preserves `/api/` prefix — use `handle`, not `handle_path`) + - `/webhooks/*` → Backend `:3002` + +### Frontend (coop-frontend, port 3000) +- **Next.js 16** with standalone output +- **Bridge network** — exposed on `0.0.0.0:3000` +- **Static files** served from `.next/standalone` + +### Backend (coop-backend, port 3002) +- **Express.js** API +- **Host network mode** — required to reach `172.20.1.x` services +- **Connects to:** PostgreSQL on `localhost:5432`, Redis on `localhost:6379` +- **Health check:** `GET /health` on `localhost:3002` + +### PostgreSQL (coop-postgres, port 5432) +- **User:** `coop` +- **Database:** `coop_credits` +- **Password:** `coop_password` (change in production) +- **Health check:** `pg_isready` + +### Redis (coop-redis, port 6379) +- **Used for:** Session store, Socket.io adapter +- **No auth** in current config (trusted local network) + ### Plex Server (172.20.1.220:32400) -- **Purpose**: Content streaming and user authentication -- **Integration**: OAuth authentication for website login -- **Access**: HTTP on local network, may have remote access enabled +- **Purpose:** Content streaming and user authentication +- **Integration:** OAuth 2.0 login for website ### Tautulli (172.20.1.255:8181) -- **Purpose**: Plex analytics and watch event tracking -- **Integration**: Webhook notifications to CoopCredits backend -- **Access**: HTTP on local network -- **API Key**: Required for backend queries +- **Purpose:** Plex analytics and watch event tracking +- **Integration:** Webhook notifications to CoopCredits on watch events +- **API Key:** Required for backfill queries ### Overseer (172.20.1.225:5055) -- **Purpose**: Content request management -- **Integration**: API for requesting content, webhooks for status updates -- **Access**: HTTP on local network -- **API Key**: Required for backend integration - -### CoopCredits Server -- **Public Access**: coop.hobokenchicken.com (HTTPS) -- **Internal Services**: Only accessible via Nginx reverse proxy -- **Database**: PostgreSQL on localhost only -- **Cache**: Redis on localhost only +- **Purpose:** Content request management +- **Integration:** API for creating requests, webhooks for status updates +- **API Key:** Required for search and request creation +- **Disk Space:** Used for dynamic pricing scarcity calculation ## Communication Flow ### 1. User Authentication ``` -User → Nginx → Frontend → Backend → Plex OAuth (172.20.1.220:32400) - ↓ - User authenticated, JWT issued +User → Caddy → Frontend → Backend → Plex OAuth (172.20.1.220:32400) + ↓ + User authenticated, JWT issued + Session stored in PostgreSQL ``` ### 2. Watch Event Processing ``` -Plex → Tautulli → Webhook → Nginx → Backend → Solana Devnet - ↓ - Database updated - ↓ - WebSocket → User notified +Plex → Tautulli → Webhook → Caddy → Backend → PostgreSQL + ↓ + WatchEvent + Transaction created + Socket.io → User notified ``` ### 3. Content Request ``` -User → Nginx → Frontend → Backend → Overseer API (172.20.1.225:5055) +User → Caddy → Frontend → Backend → Overseer API (172.20.1.225:5055) ↓ - Request created, $COOP reserved + Request created, $COOP deducted immediately + ContentRequest stored as PENDING ↓ - Webhook on approval → Burn $COOP + Overseer webhook → Backend → Status updated +``` + +### 4. Ko-fi Purchase +``` +User → Ko-fi Store → Payment → Ko-fi Webhook → Caddy → Backend + ↓ + KofiPayment record created + If email matches user → + Transaction (PURCHASE) + credits added + Socket.io → User notified ``` ## Security Considerations ### Network Security -1. **Local Network**: All services communicate over HTTP (trusted network) -2. **External Access**: Only Nginx exposed (ports 80/443) -3. **Internal Services**: Not accessible from external network +1. **Backend host mode** — Required for local network access, but exposes host network +2. **Caddy handles SSL** — Only HTTPS externally +3. **Internal services** — PostgreSQL/Redis not exposed externally (except through Docker ports) ### API Security -1. **Tautulli Webhook**: Secret verification recommended -2. **Overseer API**: API key authentication -3. **Plex OAuth**: Standard OAuth 2.0 flow -4. **JWT**: Secure tokens for session management +1. **Tautulli Webhook** — Optional secret verification (`TAUTULLI_WEBHOOK_SECRET`) +2. **Overseer Webhook** — No secret currently configured +3. **Plex OAuth** — Standard OAuth 2.0 flow +4. **JWT** — Secure tokens with nonce for session deduplication +5. **Rate Limiting** — 1000 requests per 15 minutes per IP ### Data Security -1. **Wallet Keys**: Encrypted with AES-256-GCM in database -2. **Database**: Not exposed externally -3. **Redis**: Password protected, localhost only +1. **No wallet keys** — Solana removed, no private keys stored +2. **Database** — Localhost only, password protected +3. **Redis** — Localhost only +4. **Ko-fi verification** — Token check on webhook endpoint ## Firewall Configuration ```bash -# Web traffic +# Web traffic (Caddy handles SSL) sudo ufw allow 80/tcp sudo ufw allow 443/tcp @@ -114,102 +150,70 @@ sudo ufw allow 443/tcp sudo ufw allow from 172.20.0.0/16 to 172.20.1.0/24 # Block external access to internal services -sudo ufw deny 3000/tcp # Frontend -sudo ufw deny 3001/tcp # Backend +sudo ufw deny 3000/tcp # Frontend (should be localhost only) +sudo ufw deny 3002/tcp # Backend (should be localhost only) sudo ufw deny 5432/tcp # PostgreSQL sudo ufw deny 6379/tcp # Redis ``` ## Docker Network Configuration -### Internal Network (coop-internal) -- Containers can communicate with each other +### Bridge Network (coop-network) +- Frontend container uses this - Isolated from external network -- Used for: frontend ↔ backend ↔ database +- Connects: frontend ↔ Caddy proxy -### External Network (coop-external) -- Allows containers to reach local services -- Subnet: 172.20.2.0/24 -- Used for: backend → Tautulli/Overseer/Plex - -## SSL/TLS - -SSL/TLS is handled by an external reverse proxy (e.g., Caddy). The application stack runs HTTP only on port 80 internally. +### Host Network (backend only) +- Backend container uses `network_mode: host` +- Shares host's network stack +- Required to reach `172.20.1.x` services (Tautulli, Overseer) +- Connects directly to PostgreSQL/Redis on `localhost` ## Monitoring and Logging ### Log Locations -- Nginx: `docker/nginx/logs/` -- Backend: Docker logs (`docker-compose logs backend`) -- Frontend: Docker logs (`docker-compose logs frontend`) +- Backend: `docker logs coop-backend` +- Frontend: `docker logs coop-frontend` - Database: Inside container (`/var/log/postgresql/`) ### Health Checks ```bash # Backend health -curl https://coop.hobokenchicken.com/health +curl http://localhost:3002/health # Database connection -docker-compose exec postgres pg_isready -U coop +docker exec coop-postgres pg_isready -U coop # Service status -docker-compose ps +docker ps ``` -## Troubleshooting - -### Cannot reach local services -1. Check Docker network: `docker network inspect coop-credits_coop-external` -2. Verify IP connectivity: `docker exec coop-backend ping 172.20.1.255` -3. Check firewall rules: `sudo ufw status` - -### Webhook not received -1. Verify Tautulli can reach CoopCredits: - ```bash - curl -X POST https://coop.hobokenchicken.com/webhooks/tautulli \ - -H "Content-Type: application/json" \ - -d '{"test": true}' - ``` -2. Check Nginx logs: `tail -f docker/nginx/logs/access.log` -3. Check backend logs: `docker-compose logs -f backend` - -### CORS errors -1. Verify CORS_ORIGINS in .env includes your domain -2. Check backend is sending correct headers -3. Nginx should pass through CORS headers - -## Performance Optimization - -### Nginx Tuning -- `worker_processes auto` - Use all CPU cores -- `worker_connections 1024` - High connection limit -- `gzip on` - Compress responses -- `proxy_cache` - Cache static assets - -### Database Tuning -- Connection pooling via Prisma -- Redis for session caching -- Indexed queries on user_id, created_at - -### Frontend Optimization -- Next.js static generation where possible -- Image optimization -- Code splitting - ## Backup and Recovery ### Database Backup ```bash -# Automated backup script -docker-compose exec -T postgres pg_dump -U coop coop_credits > backup_$(date +%Y%m%d).sql +# Automated backup +docker exec -T coop-postgres pg_dump -U coop coop_credits > backup_$(date +%Y%m%d).sql ``` -### Wallet Recovery -- Private keys are encrypted in database -- Backup keys stored securely (encrypted) -- Recovery requires encryption key from .env - ### Configuration Backup - `.env` file (contains all secrets) -- `docker/nginx/ssl/` certificates -- `docker-compose.prod.yml` service config +- `docker-compose.yml` service config +- Caddy config (on separate machine) + +## Troubleshooting + +### Cannot reach local services +1. Check backend is in host network mode: `docker inspect coop-backend | grep NetworkMode` +2. Verify IP connectivity from host: `ping 172.20.1.255` +3. Check backend logs: `docker logs --tail 50 coop-backend` + +### Webhook not received +1. Verify Caddy routes `/webhooks/*` to backend +2. Check backend logs for webhook handler output +3. Test manually: `curl -X POST https://coop.hobokenchicken.com/webhooks/tautulli -d '{"test":true}'` + +### CORS errors +1. Verify `FRONTEND_URL` and `API_URL` in `.env` +2. Check backend CORS origins include your domain +3. Caddy should pass through headers without modification diff --git a/docs/SETUP-INFRASTRUCTURE.md b/docs/SETUP-INFRASTRUCTURE.md index a60f125..de9ead3 100644 --- a/docs/SETUP-INFRASTRUCTURE.md +++ b/docs/SETUP-INFRASTRUCTURE.md @@ -5,9 +5,10 @@ This guide covers setting up CoopCredits with your existing Plex/Tautulli/Overse ## Prerequisites - Server running Docker and Docker Compose -- Access to 172.20.1.0/24 network +- Access to `172.20.1.0/24` network - API keys from Tautulli and Overseer -- Domain name (coop.hobokenchicken.com) pointing to your server +- Domain name (`coop.hobokenchicken.com`) pointing to your server +- Ko-fi account with store items set up (optional, for credit purchases) ## Network Overview @@ -38,72 +39,64 @@ git clone coop-credits cd coop-credits ``` -### 2. Run Infrastructure Setup - -```bash -./deployment/setup-infrastructure.sh -``` - -This script will: -- Test connectivity to your services (Plex, Tautulli, Overseer) -- Generate a secure `.env` file -- Output configuration instructions - -**Note:** SSL/TLS is handled by your external reverse proxy (Caddy). The application runs HTTP on port 80 internally. - -### 3. Configure Environment - -Edit the generated `.env` file: +### 2. Configure Environment ```bash +cp .env.example .env nano .env ``` -Add your API keys: +Required environment variables: ```env -# Get from Tautulli: Settings > Web Interface > API -TAUTULLI_API_KEY=your-tautulli-api-key +# Database +DATABASE_URL="postgresql://coop:coop_password@localhost:5432/coop_credits?schema=public" -# Get from Overseer: Settings > General > API Key -OVERSEER_API_KEY=your-overseer-api-key +# Server +PORT=3002 +API_URL=https://coop.hobokenchicken.com +FRONTEND_URL=https://coop.hobokenchicken.com -# Get from https://plex.tv/claim or Plex settings +# JWT Secret (generate: openssl rand -hex 32) +JWT_SECRET=your-random-secret + +# Plex OAuth PLEX_CLIENT_ID=your-plex-client-id PLEX_CLIENT_SECRET=your-plex-client-secret +PLEX_REDIRECT_URI=https://coop.hobokenchicken.com/auth/callback + +# Tautulli +TAUTULLI_URL=http://172.20.1.255:8181 +TAUTULLI_API_KEY=your-tautulli-api-key + +# Overseer +OVERSEER_URL=http://172.20.1.225:5055 +OVERSEER_API_KEY=your-overseer-api-key + +# Ko-fi (optional — for credit purchases) +KOFI_VERIFICATION_TOKEN=your-kofi-verification-token + +# Security +ENCRYPTION_KEY=$(openssl rand -base64 32) ``` -### 4. Setup Solana +### 3. Deploy ```bash -npm run setup:solana +docker-compose up -d --build + +# Initialize database (first time only) +docker exec coop-backend npx prisma db push --accept-data-loss ``` -This will: -- Install Solana CLI -- Create a devnet wallet -- Request airdrop -- Output the private key for your `.env` file +### 4. Configure Caddy -Copy the `SOLANA_MINT_AUTHORITY_KEYPAIR` into your `.env` file. +See `docs/CADDY-CONFIG.md`. Key points: +- Backend runs on **port 3002** +- Use `handle` (not `handle_path`) for `/api/*` +- Proxy `/webhooks/*` to backend -### 5. Deploy Solana Program - -```bash -cd anchor-program -anchor build -anchor deploy -``` - -Update `SOLANA_PROGRAM_ID` in `.env` with the deployed program ID. - -### 6. Deploy - -```bash -./deployment/deploy-production.sh -``` - -### 8. Configure Tautulli Webhook +### 5. Configure Tautulli Webhook 1. Open Tautulli: http://172.20.1.255:8181 2. Go to **Settings > Notification Agents** @@ -134,7 +127,7 @@ Update `SOLANA_PROGRAM_ID` in `.env` with the deployed program ID. **Triggers:** Enable **Watched** -### 9. Configure Overseer Webhook +### 6. Configure Overseer Webhook 1. Open Overseer: http://172.20.1.225:5055 2. Go to **Settings > Notifications** @@ -142,7 +135,7 @@ Update `SOLANA_PROGRAM_ID` in `.env` with the deployed program ID. **Configuration:** - Webhook URL: `https://coop.hobokenchicken.com/webhooks/overseer` -- Authorization Header: `Bearer your-webhook-secret-from-env` +- Content Type: `application/json` **JSON Payload:** ```json @@ -156,24 +149,33 @@ Update `SOLANA_PROGRAM_ID` in `.env` with the deployed program ID. **Events:** Enable **Request Approved** and **Request Declined** +### 7. Configure Ko-fi Webhook (Optional) + +1. Go to ko-fi.com → **Settings > Webhooks** +2. Set **Webhook URL** to: `https://coop.hobokenchicken.com/webhooks/kofi` +3. Save + +Create store items: +- $5 item → link: `https://ko-fi.com/s/YOUR_ITEM_ID` +- $10 item → link: `https://ko-fi.com/s/YOUR_ITEM_ID` + +Update frontend preset links in `frontend/src/app/dashboard/components/BuyCreditsModal.tsx` + +### 8. Set Admin User + +```bash +docker exec coop-postgres psql -U coop -d coop_credits -c "UPDATE users SET is_admin = true WHERE plex_username = 'YOUR_USERNAME';" +``` + ## Verification ### Test Connectivity ```bash -./deployment/health-check.sh -``` +# Backend health +curl https://coop.hobokenchicken.com/health -### Watch Mode - -```bash -./deployment/health-check.sh --watch -``` - -### Manual Tests - -**Test Tautulli webhook:** -```bash +# Test Tautulli webhook manually curl -X POST https://coop.hobokenchicken.com/webhooks/tautulli \ -H "Content-Type: application/json" \ -d '{ @@ -190,88 +192,6 @@ curl -X POST https://coop.hobokenchicken.com/webhooks/tautulli \ }' ``` -**Test API:** -```bash -curl https://coop.hobokenchicken.com/api/health -``` - -## Firewall Configuration - -If using UFW: - -```bash -# Allow web traffic -sudo ufw allow 80/tcp -sudo ufw allow 443/tcp - -# Allow backend to reach local network -sudo ufw allow from 172.20.2.0/16 to 172.20.1.0/24 - -# Deny direct access to internal services -sudo ufw deny 3000/tcp -sudo ufw deny 3001/tcp -sudo ufw deny 5432/tcp -sudo ufw deny 6379/tcp - -# Enable firewall -sudo ufw enable -``` - -## Troubleshooting - -### Cannot reach local services - -1. Check Docker network: - ```bash - docker network inspect coop-credits_coop-external - ``` - -2. Test connectivity from container: - ```bash - docker exec coop-backend ping 172.20.1.255 - ``` - -3. Verify firewall rules: - ```bash - sudo ufw status verbose - ``` - -### Webhooks not working - -1. Check Nginx logs: - ```bash - tail -f docker/nginx/logs/access.log - ``` - -2. Check backend logs: - ```bash - docker-compose -f docker-compose.prod.yml logs -f backend - ``` - -3. Test webhook manually: - ```bash - curl -X POST https://coop.hobokenchicken.com/webhooks/tautulli \ - -H "Content-Type: application/json" \ - -d '{"test": true}' - ``` - -### Database connection issues - -1. Check database status: - ```bash - docker-compose -f docker-compose.prod.yml ps postgres - ``` - -2. View database logs: - ```bash - docker-compose -f docker-compose.prod.yml logs postgres - ``` - -3. Test connection: - ```bash - docker-compose -f docker-compose.prod.yml exec postgres pg_isready -U coop - ``` - ## Maintenance ### Update Application @@ -279,48 +199,45 @@ sudo ufw enable ```bash cd /opt/coop-credits git pull -./deployment/deploy-production.sh +docker-compose up -d --build ``` ### Backup Database ```bash -# Automated backup -docker-compose -f docker-compose.prod.yml exec -T postgres pg_dump -U coop coop_credits > backup_$(date +%Y%m%d).sql +docker exec -T coop-postgres pg_dump -U coop coop_credits > backup_$(date +%Y%m%d).sql ``` ### View Logs ```bash # All services -docker-compose -f docker-compose.prod.yml logs -f +docker-compose logs -f # Specific service -docker-compose -f docker-compose.prod.yml logs -f backend +docker-compose logs -f backend ``` ### Restart Services ```bash -docker-compose -f docker-compose.prod.yml restart backend +docker-compose restart backend +docker-compose restart frontend ``` ## Security Checklist -- [ ] Changed all default passwords in `.env` +- [ ] Changed default PostgreSQL password - [ ] External reverse proxy (Caddy) configured for SSL - [ ] Firewall rules configured -- [ ] Tautulli webhook secret set -- [ ] Overseer webhook secret set - [ ] JWT secret is random and secure - [ ] Database not exposed externally -- [ ] Redis password set - [ ] Encryption key is random and backed up -- [ ] Solana mint authority key backed up securely +- [ ] Ko-fi verification token set (if using purchases) ## Support For issues: -1. Check health: `./deployment/health-check.sh` -2. Review logs: `docker-compose -f docker-compose.prod.yml logs` -3. Check documentation in `docs/INFRASTRUCTURE.md` +1. Check logs: `docker-compose logs -f` +2. Review environment variables +3. Verify all services are running: `docker ps` diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index 53a5c2c..1fa9c43 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -1,35 +1,5 @@ # CoopCredits Troubleshooting Guide -## Installation Warnings - -### NPM Deprecation Warnings - -When running `npm install`, you may see warnings like: - -``` -npm WARN deprecated inflight@1.0.6: This module is not supported... -npm WARN deprecated glob@7.2.0: Old versions of glob are not supported... -npm WARN deprecated @walletconnect/sign-client@2.19.0: ... -``` - -**These are safe to ignore.** These warnings come from: -- Transitive dependencies (dependencies of dependencies) -- Solana wallet adapter packages that haven't updated yet -- Legacy packages still used by the Anchor framework - -**Critical fix applied:** -- ✅ Next.js updated from 14.0.4 to 14.2.15 (security vulnerability patched) - -**What you can do:** -```bash -# The warnings don't affect functionality -# To suppress them during install: -npm install --silent - -# Or use --legacy-peer-deps if needed: -npm install --legacy-peer-deps -``` - ## Common Issues ### Database Connection Failed @@ -48,21 +18,6 @@ sleep 5 docker-compose exec postgres pg_isready -U coop ``` -### Solana Program Build Fails - -**Error:** `anchor build` fails with Rust errors - -**Solution:** -```bash -# Update Rust -cd anchor-program -rustup update - -# Rebuild -anchor clean -anchor build -``` - ### Webhooks Not Receiving **Error:** Tautulli/Overseer webhooks not triggering @@ -75,16 +30,18 @@ anchor build 2. Check webhook URL in Tautulli/Overseer settings -3. Check Nginx logs: +3. Check Caddy routes `/webhooks/*` to backend port 3002 + +4. Check backend logs: ```bash - tail -f docker/nginx/logs/access.log + docker logs --tail 50 coop-backend ``` -4. Test webhook manually: +5. Test webhook manually: ```bash curl -X POST https://coop.hobokenchicken.com/webhooks/tautulli \ -H "Content-Type: application/json" \ - -d '{"test": true}' + -d '{"action":"watched","user_id":"1","rating_key":"1","session_key":"1","media_type":"movie","title":"Test","started":1,"stopped":3601,"percent_complete":90}' ``` ### Cannot Reach Local Services (172.20.1.x) @@ -93,85 +50,72 @@ anchor build **Solution:** ```bash -# Test connectivity from backend container -docker exec coop-backend ping 172.20.1.255 +# Verify backend is in host network mode +docker inspect coop-backend | grep NetworkMode +# Should show: "host" -# Check Docker network -docker network ls -docker network inspect coop-credits_coop-external +# Test connectivity from host +ping 172.20.1.255 -# Verify firewall rules -sudo ufw status +# Check backend logs +docker logs --tail 50 coop-backend + +# Verify env vars +docker exec coop-backend env | grep TAUTULLI +docker exec coop-backend env | grep OVERSEER ``` -### Wallet Creation Fails +> Backend must use `network_mode: host` to reach `172.20.1.x` subnet. Docker bridge networking cannot route to this subnet. -**Error:** `Failed to create wallet` or Solana errors +### Ko-fi Webhook Not Working + +**Error:** Payments not crediting users **Solution:** -```bash -# Check Solana configuration -solana config get +1. Verify `KOFI_VERIFICATION_TOKEN` env var is set in backend container: + ```bash + docker exec coop-backend env | grep KOFI + ``` -# Ensure you're on devnet -solana config set --url devnet +2. Check Ko-fi webhook settings — URL should be: + ``` + https://coop.hobokenchicken.com/webhooks/kofi + ``` -# Check balance -solana balance +3. Check backend logs for verification token mismatch -# Request airdrop if needed -solana airdrop 2 +4. Check admin **Donations** tab to see if payments arrive as UNCLAIMED (email mismatch) + +### BigInt JSON Serialization Error + +**Error:** `Do not know how to serialize a BigInt` + +**Solution:** This is fixed in `backend/src/index.ts` with: +```typescript +(BigInt.prototype as any).toJSON = function () { + return this.toString(); +}; ``` +If you still see it, ensure the patch is loaded before any route handlers. + ### CORS Errors in Browser **Error:** `Access-Control-Allow-Origin` errors **Solution:** 1. Check `FRONTEND_URL` in backend `.env` matches your actual URL -2. Verify CORS_ORIGINS includes your domain +2. Verify CORS origins include your domain in `backend/src/index.ts` 3. Restart backend after changes -### High Memory Usage +### Rate Limiting (429 Too Many Requests) -**Symptom:** Server slows down or OOM errors +**Error:** Dashboard shows "Slow down, chicken!" or fails to load **Solution:** -```bash -# Check what's using memory -docker stats - -# Restart services -docker-compose -f docker-compose.prod.yml restart - -# Increase swap if needed -sudo fallocate -l 2G /swapfile -sudo chmod 600 /swapfile -sudo mkswap /swapfile -sudo swapon /swapfile -``` - -## Docker Build Issues - -### Backend Build Fails: Cannot redeclare block-scoped variable - -**Error:** `error TS2451: Cannot redeclare block-scoped variable 'signature'` - -**Solution:** Fixed in source. If you see this, pull the latest code. - -### Backend: Prisma Client could not locate Query Engine - -**Error:** `PrismaClientInitializationError: Prisma Client could not locate the Query Engine` - -**Solution:** -```bash -# Regenerate Prisma client with correct binary target -cd backend -npx prisma generate - -# Rebuild backend container -docker-compose up -d --build backend -``` +- Rate limit is 1000 requests per 15 minutes per IP +- If you hit it, wait a few minutes +- The dashboard makes several parallel calls on load — this is normal ### Frontend Build Fails: Module not found @@ -184,23 +128,7 @@ docker-compose up -d --build backend } ``` -### Frontend: Prerender Error on /login - -**Error:** `Error occurred prerendering page "/login"` - -**Solution:** This was caused by zustand persist middleware accessing localStorage during SSR. Fixed by wrapping providers with dynamic import `ssr: false`. - -### Frontend: Cannot find module 'next-themes' - -**Error:** `Module not found: Can't resolve 'next-themes'` - -**Solution:** -```bash -cd frontend -npm install next-themes -``` - -### Docker Compose: Public Directory Not Found +### Docker Build: Public Directory Not Found **Error:** `failed to calculate checksum: "/app/frontend/public": not found` @@ -210,25 +138,45 @@ mkdir -p frontend/public touch frontend/public/.gitkeep ``` +### Request Stuck on PENDING + +**Error:** Request never updates from PENDING status + +**Solution:** +1. Check Overseer webhook is configured correctly +2. In admin dashboard, go to **The Feed Queue** tab and click **SYNC** +3. Or manually sync from dashboard's Requests tab +4. Check backend logs for Overseer API errors + +### Disk Space Not Updating + +**Error:** Silo bar shows old values after downloading large files + +**Solution:** +- Disk space is cached for 30 seconds +- Wait 30 seconds and refresh +- The bar updates automatically on dashboard load + ## Getting Help -1. Check health status: +1. View all logs: ```bash - ./deployment/health-check.sh + docker-compose logs -f ``` -2. View all logs: +2. Check specific service: ```bash - docker-compose -f docker-compose.prod.yml logs -f + docker-compose logs -f backend + docker-compose logs -f frontend ``` -3. Check specific service: - ```bash - docker-compose -f docker-compose.prod.yml logs -f backend - ``` - -4. Verify environment: +3. Verify environment: ```bash # Check all required env vars are set grep -E '^\w+=' .env | wc -l ``` + +4. Check database: + ```bash + docker exec coop-postgres psql -U coop -d coop_credits -c "SELECT COUNT(*) FROM users;" + ```