
Fullstack Debugger
- 175 installs
- 178 repo stars
- Updated July 14, 2026
- erichowens/some_claude_skills
Diagnose production or staging issues spanning browser UI, API layers, and databases when users report failures or monitors fire error alerts.
About
Provides systematic fullstack debugging: reproduce issues from UI through API to database, interpret logs and traces, isolate root causes, and propose minimal fixes with regression checks. Suited for SaaS, API, and mobile stacks when production errors span multiple layers and need fast, evidence-based resolution.
- Cross-tier reproduction workflows
- Log and trace correlation
- Network and state inspection
- Database query and migration checks
- Minimal fix and regression test guidance
Fullstack Debugger by the numbers
- 175 all-time installs (skills.sh)
- Ranked #198 of 596 Debugging skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/erichowens/some_claude_skills --skill fullstack-debuggerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 175 |
|---|---|
| repo stars | ★ 178 |
| Last updated | July 14, 2026 |
| Repository | erichowens/some_claude_skills ↗ |
What it does
Diagnose production or staging issues spanning browser UI, API layers, and databases when users report failures or monitors fire error alerts.
Files
Fullstack Debugger
Expert debugger for modern web stacks: Next.js 15, Cloudflare Workers, Supabase, and edge deployments. Systematic, evidence-based troubleshooting.
Activation Triggers
Activate on: "debug", "not working", "broken", "error", "500 error", "401", "403", "cache issue", "CORS error", "RLS policy", "auth not working", "blank page", "hydration error", "build failed", "worker not responding"
NOT for: Feature development → language skills | Architecture → system-architect | Performance optimization → performance-engineer
Debug Philosophy
1. REPRODUCE → Can you make it fail consistently?
2. ISOLATE → Which layer is broken?
3. EVIDENCE → What do logs/network/state show?
4. HYPOTHESIZE → What could cause this?
5. TEST → Validate one hypothesis at a time
6. FIX → Minimal change that resolves issue
7. VERIFY → Confirm fix doesn't break other thingsArchitecture Layers
┌─────────────────────────────────────────────────────────────┐
│ DEBUGGING LAYERS │
├─────────────────────────────────────────────────────────────┤
│ │
│ Layer 1: Browser/Client │
│ ├── Console errors, network tab, React DevTools │
│ ├── localStorage/sessionStorage state │
│ └── React Query cache state │
│ │
│ Layer 2: Next.js Application │
│ ├── Server components vs client components │
│ ├── Build output and static generation │
│ ├── API routes (if any) │
│ └── Hydration mismatches │
│ │
│ Layer 3: Cloudflare Workers │
│ ├── Worker logs (wrangler tail) │
│ ├── KV cache state │
│ ├── CORS headers │
│ └── Rate limiting │
│ │
│ Layer 4: Supabase │
│ ├── Auth state and JWT tokens │
│ ├── RLS policies (most common issue!) │
│ ├── Database queries and indexes │
│ └── Realtime subscriptions │
│ │
│ Layer 5: External APIs │
│ ├── Third-party service availability │
│ ├── API rate limits │
│ └── Response format changes │
│ │
└─────────────────────────────────────────────────────────────┘Quick Diagnosis Commands
Check Everything At Once
# Run from next-app/ directory
echo "=== Build Check ===" && npm run build 2>&1 | tail -20
echo "=== TypeScript ===" && npx tsc --noEmit 2>&1 | head -20
echo "=== Lint ===" && npm run lint 2>&1 | head -20
echo "=== Git Status ===" && git status --shortSupabase RLS Diagnosis
# Check if RLS is blocking queries (most common issue!)
node -e "
const { createClient } = require('@supabase/supabase-js');
const supabase = createClient(
'YOUR_SUPABASE_URL',
'YOUR_ANON_KEY'
);
async function diagnose() {
// Test as anonymous user
const { data, error, count } = await supabase
.from('YOUR_TABLE')
.select('*', { count: 'exact' })
.limit(5);
console.log('Error:', error);
console.log('Count:', count);
console.log('Sample:', data);
}
diagnose();
"Worker Health Check
# Check if workers are responding
curl -s -o /dev/null -w "%{http_code}" https://YOUR-WORKER.workers.dev/health
# Check CORS headers
curl -s -D - -o /dev/null -H "Origin: https://yoursite.com" \
https://YOUR-WORKER.workers.dev/api/endpoint | grep -iE "(access-control|x-)"
# Stream worker logs
cd workers/your-worker && npx wrangler tailCache Inspection
# Check Cloudflare KV cache
npx wrangler kv:key list --namespace-id=YOUR_NAMESPACE_ID | head -20
# Get specific cached value
npx wrangler kv:key get --namespace-id=YOUR_NAMESPACE_ID "cache:key"
# Clear a cached item
npx wrangler kv:key delete --namespace-id=YOUR_NAMESPACE_ID "cache:key"Common Issues & Solutions
1. RLS Policy Blocking Data (Most Common!)
Symptoms:
- Query returns empty array but no error
- Works in Supabase dashboard but not in app
- Works for some users but not others
Diagnosis:
-- In Supabase SQL Editor
-- Check what policies exist
SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual
FROM pg_policies
WHERE tablename = 'your_table';
-- Test as anonymous user
SET ROLE anon;
SELECT * FROM your_table LIMIT 5;
RESET ROLE;
-- Test as authenticated user
SET ROLE authenticated;
SET request.jwt.claims = '{"sub": "user-uuid-here"}';
SELECT * FROM your_table LIMIT 5;
RESET ROLE;Common Fixes:
-- Allow public read access
CREATE POLICY "Allow public read" ON your_table
FOR SELECT USING (true);
-- Allow authenticated users to read
CREATE POLICY "Allow authenticated read" ON your_table
FOR SELECT TO authenticated USING (true);
-- Allow users to read their own data
CREATE POLICY "Users read own data" ON your_table
FOR SELECT USING (auth.uid() = user_id);2. CORS Errors
Symptoms:
- "Access to fetch blocked by CORS policy"
- Works in Postman but not in browser
- Preflight request fails
Diagnosis:
# Check what CORS headers are returned
curl -s -D - -o /dev/null \
-H "Origin: https://yoursite.com" \
-H "Access-Control-Request-Method: POST" \
-X OPTIONS \
https://your-worker.workers.dev/api/endpointFix in Cloudflare Worker:
// In your worker
const corsHeaders = {
'Access-Control-Allow-Origin': '*', // Or specific domain
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, { headers: corsHeaders });
}
// Add to all responses
return new Response(data, {
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});3. Auth State Not Persisting
Symptoms:
- User logged in but shows as logged out on refresh
- Auth works locally but not in production
- Session disappears randomly
Diagnosis:
// In browser console
console.log('Session:', await supabase.auth.getSession());
console.log('User:', await supabase.auth.getUser());
console.log('LocalStorage:', Object.keys(localStorage).filter(k => k.includes('supabase')));Common Fixes:
- Check Supabase URL matches (http vs https, trailing slash)
- Verify site URL in Supabase Auth settings
- Check for cookie blocking (Safari, incognito)
- Ensure AuthContext wraps all components needing auth
4. Hydration Mismatch
Symptoms:
- "Hydration failed because the initial UI does not match"
- Content flashes on page load
- Different content on server vs client
Diagnosis:
// Temporarily add to suspect component
useEffect(() => {
console.log('Client render:', document.body.innerHTML.slice(0, 500));
}, []);Common Fixes:
// Use client-only rendering for dynamic content
'use client';
import { useState, useEffect } from 'react';
function DynamicContent() {
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return null; // or skeleton
return <div>{/* dynamic content */}</div>;
}5. Worker Not Deploying
Symptoms:
- Deploy command succeeds but changes not reflected
- Old code still running
- Intermittent old/new behavior
Diagnosis:
# Check deployment status
npx wrangler deployments list
# View current worker code
npx wrangler deployments view
# Check for multiple environments
npx wrangler whoamiFixes:
# Force redeploy
npx wrangler deploy --force
# Clear Cloudflare cache
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE_ID/purge_cache" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
--data '{"purge_everything":true}'6. TypeScript Cache Haunting
Symptoms:
- Errors reference deleted/changed code
- Types don't match current code
- "Cannot find module" for existing files
Fix:
# Nuclear option - clear all caches
rm -rf .next node_modules/.cache tsconfig.tsbuildinfo
npm run build
# Or just TypeScript cache
rm -rf node_modules/.cache/typescript
npx tsc --build --clean7. Static Export Issues
Symptoms:
- "Error: Page X couldn't be rendered statically"
- Dynamic routes fail in static export
- API routes don't work after deploy
Diagnosis:
# Check next.config for output mode
grep -A5 "output:" next.config.ts
# Find dynamic components
grep -r "useSearchParams\|usePathname\|cookies()\|headers()" src/Fixes:
// For components using dynamic APIs
export const dynamic = 'force-dynamic';
// or wrap in Suspense with fallback
// For generateStaticParams
export async function generateStaticParams() {
return [{ slug: 'page1' }, { slug: 'page2' }];
}8. Rate Limiting Issues
Symptoms:
- 429 errors after several requests
- Works initially then stops
- Different behavior per IP
Diagnosis:
# Check rate limit headers
curl -i https://your-worker.workers.dev/api/endpoint 2>&1 | grep -i ratelimit
# Check KV for rate limit keys
npx wrangler kv:key list --namespace-id=RATE_LIMIT_KV_ID | grep rateFixes:
# Clear rate limit for an IP
npx wrangler kv:key delete --namespace-id=RATE_LIMIT_KV_ID "rate:192.168.1.1"
# Adjust limits in wrangler.toml
RATE_LIMIT_REQUESTS = "100"
RATE_LIMIT_WINDOW = "3600"9. Meeting/Location Data Issues
Symptoms:
- No meetings found in certain areas
- Stale meeting data
- Cache showing wrong data
Diagnosis:
# Check cache status for a location
curl -s -D - -o /dev/null \
-H "Origin: https://yoursite.com" \
"https://your-proxy.workers.dev/api/all?lat=45.52&lng=-122.68&radius=25" \
| grep -iE "(x-cache|x-geohash|x-source)"
# Force cache refresh
curl -H "Origin: https://yoursite.com" \
"https://your-proxy.workers.dev/warm"
# Check Supabase for meeting count
node -e "
const { createClient } = require('@supabase/supabase-js');
const supabase = createClient('URL', 'KEY');
supabase.from('meetings').select('*', { count: 'exact', head: true })
.then(({count}) => console.log('Total meetings:', count));
"10. Build Fails on Cloudflare Pages
Symptoms:
- Works locally but fails on deploy
- "Module not found" errors
- Memory exceeded
Diagnosis:
# Check build output locally
NODE_ENV=production npm run build 2>&1 | tee build.log
# Check for conditional imports
grep -r "require(" src/ --include="*.ts" --include="*.tsx"
# Check bundle size
npx next-bundle-analyzerFixes:
// next.config.ts - increase memory
module.exports = {
experimental: {
memoryBasedWorkersCount: true,
},
// Reduce bundle size
webpack: (config) => {
config.externals = [...(config.externals || []), 'sharp'];
return config;
}
};Debug Scripts
scripts/diagnose.sh
#!/bin/bash
# Run all diagnostics
echo "=== Environment ==="
node -v && npm -v
echo "=== Dependencies ==="
npm ls --depth=0 2>&1 | grep -E "(UNMET|missing)"
echo "=== TypeScript ==="
npx tsc --noEmit 2>&1 | head -30
echo "=== Build ==="
npm run build 2>&1 | tail -30
echo "=== Workers ==="
for worker in workers/*/; do
echo "Worker: $worker"
(cd "$worker" && npx wrangler whoami 2>/dev/null)
done
echo "=== Supabase ==="
npx supabase status 2>/dev/null || echo "Supabase CLI not configured"scripts/check-rls.js
// Check RLS policies are working correctly
const { createClient } = require('@supabase/supabase-js');
const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY
);
async function checkTable(table) {
console.log(`\n=== Checking ${table} ===`);
const { data, error, count } = await supabase
.from(table)
.select('*', { count: 'exact' })
.limit(1);
if (error) {
console.log(`ERROR: ${error.message}`);
} else {
console.log(`OK: ${count} rows accessible`);
}
}
// Check critical tables
['profiles', 'meetings', 'forum_posts', 'journal_entries'].forEach(checkTable);Validation Checklist
[ ] Can reproduce the issue consistently
[ ] Identified which layer is failing (client/Next/Worker/Supabase/API)
[ ] Checked browser console for errors
[ ] Checked network tab for failed requests
[ ] Checked worker logs (wrangler tail)
[ ] Verified RLS policies allow access
[ ] Tested with fresh browser/incognito
[ ] Cleared all caches (browser, React Query, KV, TS)
[ ] Checked environment variables match production
[ ] Verified CORS headers are correct
[ ] Tested on production URL (not just localhost)
[ ] Created minimal reproduction caseOutput
When debugging, always provide: 1. Root cause - What exactly was wrong 2. Evidence - Logs, errors, or queries that proved it 3. Fix - Minimal code change to resolve 4. Verification - How to confirm it's fixed 5. Prevention - How to avoid this in future
Tools Available
Read,Write,Edit- File operationsBash- Run commands, curl, wranglerGrep,Glob- Search codebaseWebFetch- Test endpointsmcp__supabase__*- Direct Supabase operationsmcp__playwright__*- Browser automation for UI testing
Debug Decision Tree
Use this flowchart to systematically identify the source of issues.
Starting Point
Is there an error message?
├── YES → Go to "Error Analysis"
└── NO → Go to "Unexpected Behavior"---
Error Analysis
Where does the error appear?
│
├── Browser Console (Red text)
│ ├── "Failed to fetch" / CORS
│ │ └── → Check Worker CORS headers
│ │ → Test: curl -H "Origin: https://site.com" <endpoint>
│ │
│ ├── "Hydration failed"
│ │ └── → Client/server mismatch
│ │ → Add 'use client' or useEffect wrapper
│ │
│ ├── "TypeError: Cannot read properties of undefined"
│ │ └── → Data not loaded yet
│ │ → Add optional chaining (?.) or loading state
│ │
│ └── Other JS error
│ └── → Check stack trace, find component
│ → Add error boundary for graceful handling
│
├── Network Tab (Red request)
│ ├── Status 401/403
│ │ └── → Authentication issue
│ │ → Check JWT, Supabase session, RLS policies
│ │
│ ├── Status 404
│ │ └── → Wrong URL or resource deleted
│ │ → Verify endpoint exists, check typos
│ │
│ ├── Status 500
│ │ └── → Server/Worker error
│ │ → Check wrangler tail or Supabase logs
│ │
│ └── Status 0 / Canceled
│ └── → Request never completed
│ → Check CORS, network, or component unmounted
│
├── Build/Terminal
│ ├── TypeScript error
│ │ └── → Type mismatch or cache issue
│ │ → Fix types or clear TS cache
│ │
│ ├── "Module not found"
│ │ └── → Missing dependency or wrong path
│ │ → npm install or fix import
│ │
│ └── "Page couldn't be rendered statically"
│ └── → Dynamic API in static context
│ → Add 'force-dynamic' or generateStaticParams
│
└── Supabase Dashboard
├── Auth error
│ └── → Check redirect URLs, email templates
│
└── Query error
└── → Check RLS policies, table exists---
Unexpected Behavior
What's happening vs expected?
│
├── Data is empty (but should have results)
│ ├── API returning empty?
│ │ └── Check Network tab response body
│ │ ├── Empty array [] → RLS blocking
│ │ ├── Has data → Frontend not displaying
│ │ └── Error in response → API issue
│ │
│ └── Data loads then disappears?
│ └── → Component remounting or state reset
│ → Check parent key props, auth redirects
│
├── Data is stale (showing old values)
│ ├── Changed in Supabase but not showing?
│ │ └── → Cache issue (browser, React Query, or KV)
│ │ → Clear all caches, check cache headers
│ │
│ └── Changed in code but not deployed?
│ └── → Check deployment succeeded
│ → Purge CDN cache
│
├── UI looks wrong
│ ├── Layout broken?
│ │ └── → CSS issue, check Tailwind classes
│ │ → Inspect element, check applied styles
│ │
│ ├── Flash of unstyled content?
│ │ └── → CSS not loading fast enough
│ │ → Check font loading, critical CSS
│ │
│ └── Different on mobile vs desktop?
│ └── → Responsive breakpoints
│ → Check Tailwind sm:/md:/lg: classes
│
├── Auth not working
│ ├── Can't log in?
│ │ └── → Check Supabase Auth settings
│ │ → Verify redirect URLs match
│ │
│ ├── Logged in but shows as logged out?
│ │ └── → Session not persisting
│ │ → Check cookies, localStorage, AuthContext
│ │
│ └── Works locally, fails in production?
│ └── → Environment variables missing
│ → Check Cloudflare Pages env vars
│
└── Slow performance
├── Initial load slow?
│ └── → Bundle too large
│ → Analyze with next/bundle-analyzer
│
├── Interactions slow?
│ └── → Re-renders or heavy computation
│ → Profile with React DevTools
│
└── API calls slow?
└── → Backend or network issue
→ Check wrangler tail for timing---
Quick Diagnostic Commands
# 1. Is the site even reachable?
curl -s -o /dev/null -w "%{http_code}" https://yoursite.com
# 2. Is the API working?
curl -s https://your-worker.workers.dev/health
# 3. Are CORS headers present?
curl -s -D - -o /dev/null -H "Origin: https://yoursite.com" https://your-api.com/endpoint
# 4. What's in the worker logs?
cd workers/your-worker && npx wrangler tail
# 5. Is Supabase accessible?
node -e "require('@supabase/supabase-js').createClient('URL','KEY').from('table').select('*').limit(1).then(console.log)"
# 6. Is the build clean?
npm run build 2>&1 | grep -E "(error|Error|failed)"
# 7. TypeScript happy?
npx tsc --noEmit 2>&1 | head -20---
Layer Isolation Test
When you can't tell which layer is broken:
1. Test Supabase directly (dashboard or curl)
└── Works? → Problem is NOT Supabase
2. Test Worker directly (curl, no browser)
└── Works? → Problem is NOT Worker
3. Test in browser devtools (Network tab)
└── Request succeeds? → Problem is frontend code
4. Test locally (npm run dev)
└── Works locally? → Problem is deployment/config
5. Test in incognito (fresh state)
└── Works in incognito? → Problem is cached state---
The "Nothing Is Working" Checklist
When everything seems broken:
[ ] Is the internet working? (ping google.com)
[ ] Is Node running correctly? (node -v)
[ ] Are dependencies installed? (npm ls --depth=0)
[ ] Is the dev server running? (npm run dev)
[ ] Are environment variables set? (echo $NEXT_PUBLIC_...)
[ ] Is git clean or are there conflicts? (git status)
[ ] Did you save all files? (check unsaved indicators)
[ ] Is the TypeScript cache haunting you? (clear it!)
[ ] Are you on the right branch? (git branch --show-current)
[ ] Did something change recently? (git log -5)Common Error Patterns & Solutions
Browser Console Errors
"Failed to fetch" / "TypeError: Failed to fetch"
Cause: Network request failed - usually CORS, network issue, or server down Debug:
# Test endpoint directly
curl -v https://your-endpoint.workers.dev/api/test
# Check CORS
curl -H "Origin: https://yoursite.com" -v https://your-endpoint.workers.dev/api/testFix: Add CORS headers to worker response
"Hydration failed because the initial UI does not match"
Cause: Server HTML differs from client render (dates, random values, browser APIs) Debug:
// Add to suspect component
useEffect(() => {
console.log('Mounted - client only');
}, []);Fix: Wrap dynamic content in client-only component:
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []);
if (!mounted) return <Skeleton />;"Cannot read properties of undefined"
Cause: Accessing nested property before data loads Fix:
// Before
data.user.name
// After
data?.user?.name ?? 'Unknown'"Invalid hook call"
Cause: Hooks called conditionally, in loops, or outside components Debug: Check for early returns before hooks Fix: Move hooks to top of component, before any conditionals
---
Supabase Errors
Empty results (no error, but data = [])
Cause: RLS policy blocking access (99% of cases) Debug:
-- Check policies
SELECT * FROM pg_policies WHERE tablename = 'your_table';
-- Test as anon
SET ROLE anon;
SELECT * FROM your_table LIMIT 1;Fix: Add SELECT policy: CREATE POLICY "name" ON table FOR SELECT USING (true);
"JWT expired"
Cause: Session token expired Fix:
// Refresh session
const { data, error } = await supabase.auth.refreshSession();"relation does not exist"
Cause: Table doesn't exist, wrong schema, or typo Debug:
SELECT table_name FROM information_schema.tables WHERE table_schema = 'public';"new row violates row-level security policy"
Cause: INSERT/UPDATE blocked by RLS Debug: Check INSERT/UPDATE policies, verify auth.uid() matches
---
Cloudflare Worker Errors
1101 Worker threw exception
Cause: Unhandled exception in worker code Debug:
cd workers/your-worker
npx wrangler tail
# Then trigger the errorFix: Add try/catch, check for undefined values
1102 Worker exceeded CPU time limit
Cause: Worker took too long (50ms free, 30s paid) Fix: Optimize code, use streaming, cache results
524 A timeout occurred
Cause: Worker took longer than 100 seconds Fix: Break into smaller operations, use Durable Objects
KV "key not found"
Cause: Key doesn't exist or expired Debug:
npx wrangler kv:key get --namespace-id=YOUR_NS "your:key"---
Next.js Build Errors
"Page X couldn't be rendered statically"
Cause: Using dynamic APIs (cookies, headers, searchParams) without proper handling Fix:
// Add to page
export const dynamic = 'force-dynamic';
// or wrap in Suspense"Module not found: Can't resolve 'X'"
Cause: Missing dependency, wrong import path, or server/client mismatch Debug:
npm ls X # Check if installed
grep -r "from 'X'" src/ # Find importsFix: npm install X or fix import path
"You're importing a component that needs useState"
Cause: Using client hook in server component Fix: Add 'use client' directive at top of file
Out of memory during build
Cause: Large pages, too many images, complex dependencies Fix:
# Increase Node memory
NODE_OPTIONS="--max-old-space-size=4096" npm run build---
TypeScript Errors
"Cannot find module" (but it exists)
Cause: Stale TypeScript cache Fix:
rm -rf node_modules/.cache/typescript tsconfig.tsbuildinfo
npx tsc --build --clean"Type X is not assignable to type Y"
Debug: Look at both types, find the mismatch Fix: Adjust type, add assertion, or fix data
"Object is possibly 'undefined'"
Fix: Add null check or optional chaining:
// Before
user.name
// After
user?.name ?? 'default'---
Authentication Issues
User stuck in logged-out state
Debug:
// In browser console
console.log(await supabase.auth.getSession());
localStorage.getItem('sb-xxx-auth-token');Common causes:
- Wrong Supabase URL (http vs https)
- Site URL mismatch in Supabase dashboard
- Cookie blocked (Safari, incognito)
Redirect loop on auth
Cause: Auth callback not handling state correctly Fix: Check redirect URLs in Supabase dashboard match your app
---
Performance Issues
Slow initial load
Debug:
# Check bundle size
npx @next/bundle-analyzerFix:
- Dynamic imports for large components
- Image optimization
- Remove unused dependencies
"Stale closure" / state not updating
Cause: Callback using old state value Fix: Use functional update or add to dependency array:
setCount(prev => prev + 1); // Not setCount(count + 1)---
Deployment Issues
Works locally, fails in production
Debug checklist: 1. Environment variables set in Cloudflare Pages? 2. Build command correct? 3. Output directory correct (out for static)? 4. Node version matches?
Changes not reflecting after deploy
Cause: CDN/browser caching Fix:
# Purge Cloudflare cache
curl -X POST "https://api.cloudflare.com/client/v4/zones/ZONE/purge_cache" \
-H "Authorization: Bearer TOKEN" \
-d '{"purge_everything":true}'
# Or add cache busting
?v=2 to resource URLs#!/bin/bash
# CORS Diagnostic Script
# Tests if CORS headers are correctly configured on worker endpoints
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# Default values
ORIGIN="${1:-https://jbuds4life.pages.dev}"
ENDPOINT="${2:-https://jb4l-meeting-proxy.erich-owens.workers.dev/health}"
echo "╔════════════════════════════════════════════╗"
echo "║ CORS DIAGNOSTIC TOOL ║"
echo "╚════════════════════════════════════════════╝"
echo ""
echo "Testing: $ENDPOINT"
echo "Origin: $ORIGIN"
echo ""
# Test 1: Simple GET request
echo "=== Test 1: Simple GET Request ==="
RESPONSE=$(curl -s -D - -o /dev/null -H "Origin: $ORIGIN" "$ENDPOINT")
echo "$RESPONSE" | grep -iE "(HTTP|access-control|x-)" || true
# Check for Access-Control-Allow-Origin
if echo "$RESPONSE" | grep -qi "access-control-allow-origin"; then
ACAO=$(echo "$RESPONSE" | grep -i "access-control-allow-origin" | tr -d '\r')
echo -e "${GREEN}✅ CORS header present: $ACAO${NC}"
else
echo -e "${RED}❌ No Access-Control-Allow-Origin header!${NC}"
fi
# Test 2: Preflight OPTIONS request
echo ""
echo "=== Test 2: Preflight OPTIONS Request ==="
PREFLIGHT=$(curl -s -D - -o /dev/null \
-X OPTIONS \
-H "Origin: $ORIGIN" \
-H "Access-Control-Request-Method: POST" \
-H "Access-Control-Request-Headers: Content-Type, Authorization" \
"$ENDPOINT")
echo "$PREFLIGHT" | grep -iE "(HTTP|access-control)" || true
# Check preflight response
HTTP_STATUS=$(echo "$PREFLIGHT" | head -1 | awk '{print $2}')
if [ "$HTTP_STATUS" = "200" ] || [ "$HTTP_STATUS" = "204" ]; then
echo -e "${GREEN}✅ Preflight returns $HTTP_STATUS${NC}"
else
echo -e "${RED}❌ Preflight returns $HTTP_STATUS (expected 200 or 204)${NC}"
fi
# Check required headers
echo ""
echo "=== Required CORS Headers ==="
for HEADER in "Access-Control-Allow-Origin" "Access-Control-Allow-Methods" "Access-Control-Allow-Headers"; do
if echo "$PREFLIGHT" | grep -qi "$HEADER"; then
VALUE=$(echo "$PREFLIGHT" | grep -i "$HEADER" | cut -d: -f2- | tr -d '\r')
echo -e "${GREEN}✅ $HEADER:$VALUE${NC}"
else
echo -e "${RED}❌ Missing: $HEADER${NC}"
fi
done
# Test 3: Actual POST (if endpoint supports it)
echo ""
echo "=== Test 3: POST Request with JSON ==="
POST_RESPONSE=$(curl -s -D - -o /dev/null \
-X POST \
-H "Origin: $ORIGIN" \
-H "Content-Type: application/json" \
-d '{"test": true}' \
"$ENDPOINT" 2>&1 || true)
POST_STATUS=$(echo "$POST_RESPONSE" | head -1 | awk '{print $2}')
echo "POST Status: $POST_STATUS"
echo "$POST_RESPONSE" | grep -iE "access-control" || echo "(no CORS headers in response)"
# Summary
echo ""
echo "════════════════════════════════════════════"
echo "CORS CHECKLIST"
echo "════════════════════════════════════════════"
cat << 'EOF'
Worker should include in ALL responses (including OPTIONS):
const corsHeaders = {
'Access-Control-Allow-Origin': '*', // or specific origin
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400',
};
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: corsHeaders });
}
// Add to all other responses
return new Response(body, {
status: 200,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
EOF
#!/usr/bin/env node
/**
* RLS Policy Diagnostic Script
*
* Checks if Row Level Security policies are allowing/blocking access correctly.
* The #1 cause of "empty results but no error" issues in Supabase.
*
* Usage:
* node check-rls.js # Uses env vars
* node check-rls.js <url> <anon_key> # Explicit credentials
*/
const TABLES_TO_CHECK = [
'profiles',
'meetings',
'forum_posts',
'forum_comments',
'journal_entries',
'daily_checkins',
'saved_meetings',
'recovery_plans',
'safety_plans'
];
async function main() {
// Dynamic import for ESM compatibility
const { createClient } = await import('@supabase/supabase-js');
const url = process.argv[2] || process.env.NEXT_PUBLIC_SUPABASE_URL;
const key = process.argv[3] || process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
if (!url || !key) {
console.error('Usage: node check-rls.js <SUPABASE_URL> <ANON_KEY>');
console.error('Or set NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY');
process.exit(1);
}
const supabase = createClient(url, key);
console.log('╔════════════════════════════════════════════╗');
console.log('║ RLS POLICY DIAGNOSTIC ║');
console.log('╚════════════════════════════════════════════╝\n');
console.log(`URL: ${url.substring(0, 40)}...`);
console.log(`Testing as: anonymous (anon key)\n`);
const results = {
accessible: [],
blocked: [],
errors: []
};
for (const table of TABLES_TO_CHECK) {
process.stdout.write(`Checking ${table.padEnd(20)}`);
try {
const { data, error, count } = await supabase
.from(table)
.select('*', { count: 'exact' })
.limit(1);
if (error) {
if (error.code === 'PGRST204') {
// Table doesn't exist
console.log(`⚠️ TABLE NOT FOUND`);
} else if (error.code === '42501') {
// Permission denied
console.log(`🔒 BLOCKED BY RLS`);
results.blocked.push({ table, error: error.message });
} else {
console.log(`❌ ERROR: ${error.message}`);
results.errors.push({ table, error: error.message });
}
} else if (count === 0) {
console.log(`✅ OK (0 rows - empty or filtered)`);
results.accessible.push({ table, count: 0 });
} else {
console.log(`✅ OK (${count} rows accessible)`);
results.accessible.push({ table, count });
}
} catch (err) {
console.log(`❌ EXCEPTION: ${err.message}`);
results.errors.push({ table, error: err.message });
}
}
// Summary
console.log('\n════════════════════════════════════════════');
console.log('SUMMARY');
console.log('════════════════════════════════════════════');
console.log(`✅ Accessible: ${results.accessible.length} tables`);
console.log(`🔒 Blocked: ${results.blocked.length} tables`);
console.log(`❌ Errors: ${results.errors.length} tables`);
if (results.blocked.length > 0) {
console.log('\n🔒 BLOCKED TABLES (need RLS policy review):');
results.blocked.forEach(({ table, error }) => {
console.log(` - ${table}: ${error}`);
});
console.log('\nTo fix, add a SELECT policy in Supabase:');
console.log(' CREATE POLICY "Allow public read" ON table_name');
console.log(' FOR SELECT USING (true);');
}
if (results.errors.length > 0) {
console.log('\n❌ TABLES WITH ERRORS:');
results.errors.forEach(({ table, error }) => {
console.log(` - ${table}: ${error}`);
});
}
// Exit with error if any issues
if (results.blocked.length > 0 || results.errors.length > 0) {
process.exit(1);
}
}
main().catch(err => {
console.error('Fatal error:', err);
process.exit(1);
});
#!/bin/bash
# Comprehensive diagnostic script for Next.js + Cloudflare + Supabase apps
# Run from the project root (next-app/ directory)
set -e
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
echo -e "${BLUE}╔════════════════════════════════════════════╗${NC}"
echo -e "${BLUE}║ FULLSTACK DIAGNOSTIC TOOL ║${NC}"
echo -e "${BLUE}╚════════════════════════════════════════════╝${NC}"
# 1. Environment Check
echo -e "\n${YELLOW}=== Environment ===${NC}"
echo -n "Node: " && node -v 2>/dev/null || echo -e "${RED}NOT INSTALLED${NC}"
echo -n "npm: " && npm -v 2>/dev/null || echo -e "${RED}NOT INSTALLED${NC}"
echo -n "Wrangler: " && npx wrangler --version 2>/dev/null || echo -e "${YELLOW}Not installed (needed for Workers)${NC}"
# 2. Git Status
echo -e "\n${YELLOW}=== Git Status ===${NC}"
if [ -d .git ]; then
git status --short
echo "Branch: $(git branch --show-current)"
echo "Last commit: $(git log -1 --oneline)"
else
echo -e "${YELLOW}Not a git repository${NC}"
fi
# 3. Dependencies Check
echo -e "\n${YELLOW}=== Dependencies ===${NC}"
if [ -f package.json ]; then
MISSING=$(npm ls --depth=0 2>&1 | grep -E "(UNMET|missing|ERR)" | head -10)
if [ -z "$MISSING" ]; then
echo -e "${GREEN}All dependencies OK${NC}"
else
echo -e "${RED}Missing dependencies:${NC}"
echo "$MISSING"
fi
else
echo -e "${RED}No package.json found${NC}"
fi
# 4. TypeScript Check
echo -e "\n${YELLOW}=== TypeScript ===${NC}"
if [ -f tsconfig.json ]; then
TS_ERRORS=$(npx tsc --noEmit 2>&1 | head -20)
if [ -z "$TS_ERRORS" ]; then
echo -e "${GREEN}No TypeScript errors${NC}"
else
echo -e "${RED}TypeScript errors:${NC}"
echo "$TS_ERRORS"
fi
else
echo -e "${YELLOW}No tsconfig.json found${NC}"
fi
# 5. ESLint Check
echo -e "\n${YELLOW}=== ESLint ===${NC}"
LINT_ERRORS=$(npm run lint 2>&1 | tail -20)
if echo "$LINT_ERRORS" | grep -q "error"; then
echo -e "${RED}Lint errors found:${NC}"
echo "$LINT_ERRORS" | grep -E "(error|Error)" | head -10
else
echo -e "${GREEN}No lint errors${NC}"
fi
# 6. Build Check
echo -e "\n${YELLOW}=== Build Test ===${NC}"
echo "Running build (this may take a moment)..."
BUILD_OUTPUT=$(npm run build 2>&1)
if echo "$BUILD_OUTPUT" | grep -q "Export successful\|Compiled successfully\|Build completed"; then
echo -e "${GREEN}Build successful${NC}"
else
echo -e "${RED}Build failed:${NC}"
echo "$BUILD_OUTPUT" | tail -30
fi
# 7. Environment Variables
echo -e "\n${YELLOW}=== Environment Variables ===${NC}"
if [ -f .env.local ]; then
echo "Found .env.local with $(wc -l < .env.local | tr -d ' ') lines"
grep -E "^NEXT_PUBLIC_" .env.local 2>/dev/null | sed 's/=.*/=***/' || true
else
echo -e "${YELLOW}No .env.local file (using system env)${NC}"
fi
# Check required vars
for VAR in NEXT_PUBLIC_SUPABASE_URL NEXT_PUBLIC_SUPABASE_ANON_KEY; do
if grep -q "$VAR" .env.local 2>/dev/null || [ -n "${!VAR}" ]; then
echo -e "${GREEN}$VAR: Set${NC}"
else
echo -e "${RED}$VAR: Missing!${NC}"
fi
done
# 8. Workers Check
echo -e "\n${YELLOW}=== Cloudflare Workers ===${NC}"
if [ -d workers ]; then
for worker in workers/*/; do
WORKER_NAME=$(basename "$worker")
if [ -f "$worker/wrangler.toml" ]; then
echo -n "$WORKER_NAME: "
if (cd "$worker" && npx wrangler whoami >/dev/null 2>&1); then
echo -e "${GREEN}configured${NC}"
else
echo -e "${YELLOW}not authenticated${NC}"
fi
fi
done
else
echo "No workers directory found"
fi
# 9. Supabase Check
echo -e "\n${YELLOW}=== Supabase ===${NC}"
if command -v supabase &> /dev/null; then
supabase status 2>/dev/null || echo "Supabase not linked to this project"
else
echo "Supabase CLI not installed"
fi
# 10. Port Check
echo -e "\n${YELLOW}=== Port Status ===${NC}"
for PORT in 3000 3001 5432 54321; do
if lsof -i :$PORT >/dev/null 2>&1; then
PROC=$(lsof -i :$PORT | tail -1 | awk '{print $1}')
echo -e "Port $PORT: ${YELLOW}In use by $PROC${NC}"
else
echo -e "Port $PORT: ${GREEN}Available${NC}"
fi
done
# Summary
echo -e "\n${BLUE}════════════════════════════════════════════${NC}"
echo -e "${BLUE}Diagnostic complete. Check any RED items above.${NC}"
echo -e "${BLUE}════════════════════════════════════════════${NC}"
#!/bin/bash
# Cache Inspection Script
# Inspects Cloudflare KV cache for meeting-proxy
set -e
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
# KV Namespace IDs (update these for your project)
MEETING_CACHE_NS="${MEETING_CACHE_NS:-104a373dda314cfc8d267b4b2ffa92b9}"
RATE_LIMIT_NS="${RATE_LIMIT_NS:-7ed18d6c32334aaeb3450ad6e140c985}"
echo "╔════════════════════════════════════════════╗"
echo "║ CACHE INSPECTION TOOL ║"
echo "╚════════════════════════════════════════════╝"
echo ""
# Check wrangler auth
if ! npx wrangler whoami > /dev/null 2>&1; then
echo -e "${YELLOW}Not authenticated with Cloudflare. Run: npx wrangler login${NC}"
exit 1
fi
# Function to list KV keys
list_kv_keys() {
local NS_ID=$1
local NS_NAME=$2
echo -e "\n${BLUE}=== $NS_NAME ===${NC}"
echo "Namespace ID: $NS_ID"
echo ""
KEYS=$(npx wrangler kv:key list --namespace-id="$NS_ID" 2>/dev/null || echo "[]")
if [ "$KEYS" = "[]" ]; then
echo " (empty)"
else
echo "$KEYS" | jq -r '.[].name' 2>/dev/null | head -20 || echo "$KEYS" | head -20
COUNT=$(echo "$KEYS" | jq 'length' 2>/dev/null || echo "?")
echo ""
echo "Total keys: $COUNT"
if [ "$COUNT" -gt 20 ]; then
echo "(showing first 20)"
fi
fi
}
# Function to get a specific key
get_kv_value() {
local NS_ID=$1
local KEY=$2
echo -e "\n${BLUE}=== Value for '$KEY' ===${NC}"
VALUE=$(npx wrangler kv:key get --namespace-id="$NS_ID" "$KEY" 2>/dev/null)
if [ -z "$VALUE" ]; then
echo "(not found or empty)"
else
# Pretty print if JSON
if echo "$VALUE" | jq . > /dev/null 2>&1; then
echo "$VALUE" | jq '.' | head -50
LEN=$(echo "$VALUE" | wc -c)
if [ "$LEN" -gt 5000 ]; then
echo "... (truncated, total $LEN bytes)"
fi
else
echo "$VALUE" | head -20
fi
fi
}
# Function to check cache hit rate
check_cache_status() {
local LAT=$1
local LNG=$2
local RADIUS=${3:-25}
echo -e "\n${BLUE}=== Cache Status Check ===${NC}"
echo "Location: ($LAT, $LNG), Radius: ${RADIUS}mi"
RESPONSE=$(curl -s -D - -o /dev/null \
-H "Origin: https://jbuds4life.pages.dev" \
"https://jb4l-meeting-proxy.erich-owens.workers.dev/api/all?lat=$LAT&lng=$LNG&radius=$RADIUS" 2>&1)
CACHE=$(echo "$RESPONSE" | grep -i "x-cache:" | tr -d '\r' || echo "x-cache: unknown")
GEOHASH=$(echo "$RESPONSE" | grep -i "x-geohash:" | tr -d '\r' || echo "x-geohash: unknown")
SOURCE=$(echo "$RESPONSE" | grep -i "x-source:" | tr -d '\r' || echo "")
echo "$CACHE"
echo "$GEOHASH"
[ -n "$SOURCE" ] && echo "$SOURCE"
}
# Main menu
case "${1:-list}" in
list)
list_kv_keys "$MEETING_CACHE_NS" "Meeting Cache"
;;
rate)
list_kv_keys "$RATE_LIMIT_NS" "Rate Limits"
;;
get)
if [ -z "$2" ]; then
echo "Usage: $0 get <key>"
exit 1
fi
get_kv_value "$MEETING_CACHE_NS" "$2"
;;
status)
LAT="${2:-45.5152}"
LNG="${3:--122.6784}"
check_cache_status "$LAT" "$LNG"
;;
warm)
echo "Warming cache for top metros..."
curl -s -H "Origin: https://jbuds4life.pages.dev" \
"https://jb4l-meeting-proxy.erich-owens.workers.dev/warm"
echo ""
;;
clear)
if [ -z "$2" ]; then
echo "Usage: $0 clear <key>"
echo " $0 clear meetings:c20 # Clear Portland cache"
exit 1
fi
echo "Deleting key: $2"
npx wrangler kv:key delete --namespace-id="$MEETING_CACHE_NS" "$2"
echo -e "${GREEN}Deleted${NC}"
;;
*)
echo "Usage: $0 <command> [args]"
echo ""
echo "Commands:"
echo " list List meeting cache keys"
echo " rate List rate limit keys"
echo " get <key> Get value for a key"
echo " status [lat lng] Check cache status for location"
echo " warm Warm cache for top metros"
echo " clear <key> Delete a cache key"
echo ""
echo "Examples:"
echo " $0 list"
echo " $0 get meetings:c20:25"
echo " $0 status 45.52 -122.68"
echo " $0 clear meetings:c20:25"
;;
esac