Camwhores.v May 2026
-- Streams (live or recorded) CREATE TABLE streams ( id BIGSERIAL PRIMARY KEY, model_id BIGINT REFERENCES users(id), title VARCHAR(255), is_live BOOLEAN DEFAULT FALSE, start_time TIMESTAMP, end_time TIMESTAMP, is_premium BOOLEAN DEFAULT FALSE, -- true = requires purchase/subscription price_cents INTEGER, -- optional PPV price thumbnail_url VARCHAR(255) ); | Method | URL | Description | Auth | |--------|-----|-------------|------| | GET /api/plans | List all subscription plans | Public | | POST /api/subscriptions | Create a Stripe Checkout session for a plan | Viewer (JWT) | | POST /api/webhooks/stripe | Handle subscription events ( invoice.payment_succeeded , customer.subscription.deleted ) | Stripe secret | | GET /api/streams/:id | Retrieve stream metadata; includes access flag | Viewer (JWT) | | POST /api/purchases/:streamId | Create a PPV checkout session for a specific stream | Viewer (JWT) | | GET /api/user/me | Current user profile + subscription status | Viewer/Model (JWT) |
useEffect(() => async function load() const data = await axios.get(`/api/streams/$id`); setStream(data); setHasAccess(data.access); setLoading(false); load(); , [id]); camwhores.v
function StreamPage() const id = useParams(); const [stream, setStream] = useState(null); const [hasAccess, setHasAccess] = useState(false); const [loading, setLoading] = useState(true); const location = useLocation(); -- Streams (live or recorded) CREATE TABLE streams
// Render the video player (e.g., HLS.js, Vimeo, or your streaming CDN) return ( <div> <h2>stream.title</h2> <video controls src=stream.video_url style= width: '100%', maxWidth: '800px' /> </div> ); model_id BIGINT REFERENCES users(id)
if (loading) return <p>Loading…</p>;