"use client"; import { useState, useEffect, type ReactNode } from "react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; /** * PasswordGate — a simple client-side password protection layer. * * How it works: * 1. On mount, asks the server (GET /api/verify-access) whether protection is enabled. * 2. If not enabled, renders children immediately (no gate). * 3. If enabled, checks sessionStorage for a stored access key. * 4. If found, validates it against the server via POST /api/verify-access. * 5. If not found or invalid, shows a password prompt. * 6. On correct password, stores it in sessionStorage and renders children. * * Environment: * - ACCESS_KEY env variable must be set on the server for protection to activate. * - If ACCESS_KEY is not set, the gate is bypassed entirely. * * This is intentionally simple and modular — remove the wrapper * from layout.tsx to disable it entirely. */ const SESSION_KEY = "spsays_access"; const API_ENDPOINT = "/api/verify-access"; export function PasswordGate({ children }: { children: ReactNode }) { const [status, setStatus] = useState<"loading" | "locked" | "unlocked">( "loading", ); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const [verifying, setVerifying] = useState(false); async function verifyKey(key: string): Promise { try { const res = await fetch(API_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key }), }); if (!res.ok) return false; const data = await res.json(); return data.valid === true; } catch { return false; } } useEffect(() => { async function init() { // Step 1: Ask server if protection is enabled try { const res = await fetch(API_ENDPOINT); const data = await res.json(); if (!data.enabled) { // No ACCESS_KEY set on server — bypass gate setStatus("unlocked"); return; } } catch { // If we can't reach the server, unlock to avoid blocking the site setStatus("unlocked"); return; } // Step 2: Protection is enabled — check sessionStorage const stored = sessionStorage.getItem(SESSION_KEY); if (stored) { const ok = await verifyKey(stored); if (ok) { setStatus("unlocked"); return; } // Stored key is invalid — clear it sessionStorage.removeItem(SESSION_KEY); } setStatus("locked"); } init(); }, []); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); if (!password.trim()) return; setVerifying(true); setError(""); const ok = await verifyKey(password.trim()); if (ok) { sessionStorage.setItem(SESSION_KEY, password.trim()); setStatus("unlocked"); } else { setError("Invalid access key. Please try again."); } setVerifying(false); } // Loading state — brief flash while checking with server if (status === "loading") { return (
Loading…
); } // Unlocked — render the app if (status === "unlocked") { return <>{children}; } // Locked — show password prompt return (
Prabhupāda Says

Enter the access key to continue

{ setPassword(e.target.value); if (error) setError(""); }} autoFocus disabled={verifying} /> {error && (

{error}

)}
); }