"use client"; import { useState, useEffect, useCallback, useRef } from "react"; import Card from "./Card"; import Button from "./Button"; import DistributeProxiesButton from "./DistributeProxiesButton"; import NoAuthProviderToggle from "./NoAuthProviderToggle"; interface NoAuthAccountCardProps { providerId: string; providerName: string; generateAccountId: () => string; dataKey?: string; description?: string; addLabel?: string; enabled?: boolean; savingEnabled?: boolean; onEnabledChange?: (enabled: boolean) => void; } interface Connection { id: string; provider: string; apiKey?: string; providerSpecificData?: Record; isActive?: boolean; } interface AccountProxyConfig { fingerprint: string; proxy: { type: string; host: string; port: number; username?: string; password?: string } | null; } const PROXY_TYPES = [ { value: "http", label: "HTTP" }, { value: "https", label: "HTTPS" }, { value: "socks5", label: "SOCKS5" }, ]; function getAccountProxies(conn: Connection | undefined): AccountProxyConfig[] { return (conn?.providerSpecificData?.accountProxies as AccountProxyConfig[]) || []; } function getProxyForFingerprint(proxies: AccountProxyConfig[], fp: string) { return proxies.find((p) => p.fingerprint === fp)?.proxy ?? null; } export default function NoAuthAccountCard({ providerId, providerName, generateAccountId, dataKey = "fingerprints", description = "Ready to use — no signup needed. Add accounts for rate-limit rotation.", addLabel = "Add Account", enabled = true, savingEnabled = false, onEnabledChange, }: NoAuthAccountCardProps) { const [connections, setConnections] = useState([]); const [loading, setLoading] = useState(true); const [adding, setAdding] = useState(false); const [proxyAccountId, setProxyAccountId] = useState(null); const [proxyType, setProxyType] = useState("socks5"); const [proxyHost, setProxyHost] = useState(""); const [proxyPort, setProxyPort] = useState("1080"); const [proxyUsername, setProxyUsername] = useState(""); const [proxyPassword, setProxyPassword] = useState(""); const [savingProxy, setSavingProxy] = useState(false); const popoverRef = useRef(null); const fetchConnections = useCallback(async () => { try { const res = await fetch("/api/providers"); if (res.ok) { const data = await res.json(); const filtered = (data.connections || []).filter( (c: Connection) => c.provider === providerId ); setConnections(filtered); } } catch (err) { console.error("Failed to fetch connections:", err); } finally { setLoading(false); } }, [providerId]); useEffect(() => { void fetchConnections(); }, [fetchConnections]); useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (popoverRef.current && !popoverRef.current.contains(e.target as Node)) { setProxyAccountId(null); } }; if (proxyAccountId) { document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); } }, [proxyAccountId]); const allAccountIds = connections.flatMap((c) => c.providerSpecificData?.[dataKey] || []); const conn = connections[0]; const accountProxies = getAccountProxies(conn); const handleAddAccount = async () => { setAdding(true); try { const accountId = generateAccountId(); if (connections.length === 0) { const res = await fetch("/api/providers", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ provider: providerId, name: `${providerName} Account 1`, providerSpecificData: { [dataKey]: [accountId] }, }), }); if (!res.ok) throw new Error("Failed to create connection"); } else { const updated = [...allAccountIds, accountId]; const res = await fetch(`/api/providers/${conn.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ providerSpecificData: { [dataKey]: updated }, }), }); if (!res.ok) throw new Error("Failed to update connection"); } await fetchConnections(); } catch (err) { console.error("Failed to add account:", err); } finally { setAdding(false); } }; const handleRemoveAccount = async (accountId: string) => { if (!conn) return; const updated = allAccountIds.filter((id) => id !== accountId); const updatedProxies = accountProxies.filter((p) => p.fingerprint !== accountId); try { const res = await fetch(`/api/providers/${conn.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ providerSpecificData: { [dataKey]: updated, accountProxies: updatedProxies, }, }), }); if (res.ok) await fetchConnections(); } catch (err) { console.error("Failed to remove account:", err); } }; const openProxyConfig = (accountId: string) => { const existing = getProxyForFingerprint(accountProxies, accountId); if (existing) { setProxyType(existing.type); setProxyHost(existing.host); setProxyPort(String(existing.port)); setProxyUsername(existing.username || ""); setProxyPassword(existing.password || ""); } else { setProxyType("socks5"); setProxyHost(""); setProxyPort("1080"); setProxyUsername(""); setProxyPassword(""); } setProxyAccountId(accountId); }; const handleSaveProxy = async () => { if (!conn || !proxyAccountId) return; setSavingProxy(true); try { const trimmedHost = proxyHost.trim(); const newProxy: AccountProxyConfig["proxy"] = trimmedHost ? { type: proxyType, host: trimmedHost, port: Number(proxyPort) || 1080, ...(proxyUsername.trim() ? { username: proxyUsername.trim() } : {}), ...(proxyPassword.trim() ? { password: proxyPassword.trim() } : {}), } : null; const existing = accountProxies.filter((p) => p.fingerprint !== proxyAccountId); const updatedProxies = newProxy ? [...existing, { fingerprint: proxyAccountId, proxy: newProxy }] : existing; const res = await fetch(`/api/providers/${conn.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ providerSpecificData: { accountProxies: updatedProxies }, }), }); if (res.ok) { await fetchConnections(); setProxyAccountId(null); } } catch (err) { console.error("Failed to save proxy:", err); } finally { setSavingProxy(false); } }; const handleDistributeProxies = async () => { if (!conn || allAccountIds.length === 0) return; const proxiesRes = await fetch("/api/settings/proxies"); if (!proxiesRes.ok) throw new Error("Failed to fetch proxies"); const proxiesData = await proxiesRes.json(); const savedProxies = (proxiesData?.items || []).filter((p: any) => p.status === "active"); if (savedProxies.length === 0) { throw new Error("No saved proxies found. Add proxies in Settings → Proxy first."); } const updatedProxies: AccountProxyConfig[] = allAccountIds.map((fp, i) => { const proxy = savedProxies[i % savedProxies.length]; return { fingerprint: fp, proxy: { type: proxy.type || "socks5", host: proxy.host, port: proxy.port, ...(proxy.username ? { username: proxy.username } : {}), ...(proxy.password ? { password: proxy.password } : {}), }, }; }); const res = await fetch(`/api/providers/${conn.id}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ providerSpecificData: { accountProxies: updatedProxies }, }), }); if (!res.ok) throw new Error("Failed to update connection"); await fetchConnections(); }; return (
lock_open

No authentication required

{description}

Accounts ({loading ? "..." : allAccountIds.length})
{!loading && allAccountIds.length > 0 && ( )}
{!loading && allAccountIds.length === 0 && (

Using auto-generated account. Click "{addLabel}" for rate-limit rotation.

)} {!loading && allAccountIds.length > 0 && (
{allAccountIds.map((id, i) => { const proxy = getProxyForFingerprint(accountProxies, id); return (
{i + 1} {id.slice(0, 10)}…
); })}
)} {proxyAccountId && (

Proxy for Account {allAccountIds.indexOf(proxyAccountId) + 1}

setProxyHost(e.target.value)} placeholder="Host" className="flex-1 rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10" /> setProxyPort(e.target.value)} placeholder="Port" className="w-16 rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10" />
setProxyUsername(e.target.value)} placeholder="Username (optional)" className="w-full rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10" /> setProxyPassword(e.target.value)} placeholder="Password (optional)" className="w-full rounded-md border border-black/10 bg-bg px-2.5 py-1.5 text-xs dark:border-white/10" />
)}
); }