Initial commit: Full Crawl API implementation
This commit is contained in:
159
frontend/app/playground/page.tsx
Normal file
159
frontend/app/playground/page.tsx
Normal file
@@ -0,0 +1,159 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
export default function Playground() {
|
||||
const [apiKey, setApiKey] = useState('')
|
||||
const [url, setUrl] = useState('https://example.com')
|
||||
const [endpoint, setEndpoint] = useState('screenshot')
|
||||
const [options, setOptions] = useState('{}')
|
||||
const [result, setResult] = useState('')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [codeLang, setCodeLang] = useState('curl')
|
||||
|
||||
const endpoints = [
|
||||
{ value: 'screenshot', label: 'Screenshot' },
|
||||
{ value: 'pdf', label: 'PDF' },
|
||||
{ value: 'crawl', label: 'Crawl' },
|
||||
{ value: 'content', label: 'Content' },
|
||||
{ value: 'markdown', label: 'Markdown' },
|
||||
{ value: 'json', label: 'JSON' },
|
||||
{ value: 'links', label: 'Links' },
|
||||
{ value: 'scrape', label: 'Scrape' },
|
||||
{ value: 'snapshot', label: 'Snapshot' },
|
||||
{ value: 'extract', label: 'AI Extract' },
|
||||
]
|
||||
|
||||
async function sendRequest() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const body: any = { url }
|
||||
if (options && options !== '{}') {
|
||||
body.options = JSON.parse(options)
|
||||
}
|
||||
const res = await fetch(`http://localhost:3000/api/${endpoint}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': apiKey,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
})
|
||||
const data = await res.json()
|
||||
setResult(JSON.stringify(data, null, 2))
|
||||
} catch (e) {
|
||||
setResult(String(e))
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
function getCodeSnippet() {
|
||||
const body = JSON.stringify({ url, options: JSON.parse(options || '{}') }, null, 2)
|
||||
switch (codeLang) {
|
||||
case 'curl':
|
||||
return `curl -X POST http://localhost:3000/api/${endpoint} \\
|
||||
-H "Content-Type: application/json" \\
|
||||
-H "x-api-key: ${apiKey || 'YOUR_API_KEY'}" \\
|
||||
-d '${body}'`
|
||||
case 'python':
|
||||
return `import requests
|
||||
|
||||
response = requests.post(
|
||||
"http://localhost:3000/api/${endpoint}",
|
||||
headers={
|
||||
"Content-Type": "application/json",
|
||||
"x-api-key": "${apiKey || 'YOUR_API_KEY'}"
|
||||
},
|
||||
json=${body.replace(/true/g, 'True').replace(/false/g, 'False').replace(/null/g, 'None')}
|
||||
)
|
||||
print(response.json())`
|
||||
case 'javascript':
|
||||
return `const response = await fetch('http://localhost:3000/api/${endpoint}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'x-api-key': '${apiKey || 'YOUR_API_KEY'}'
|
||||
},
|
||||
body: JSON.stringify(${body})
|
||||
});
|
||||
const data = await response.json();
|
||||
console.log(data);`
|
||||
default:
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<main style={{ maxWidth: 1200, margin: '0 auto', padding: '40px 20px' }}>
|
||||
<nav style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 40 }}>
|
||||
<Link href="/" style={{ fontSize: 24, fontWeight: 700, color: '#fff', textDecoration: 'none' }}>Crawl API</Link>
|
||||
<Link href="/" style={{ color: '#888', textDecoration: 'none' }}>← Back</Link>
|
||||
</nav>
|
||||
|
||||
<h1 style={{ fontSize: 36, marginBottom: 8 }}>API Playground</h1>
|
||||
<p style={{ color: '#888', marginBottom: 32 }}>Test any endpoint directly from the browser.</p>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 24 }}>
|
||||
<div>
|
||||
<div style={{ background: '#111', borderRadius: 12, padding: 24, marginBottom: 24 }}>
|
||||
<h3 style={{ marginTop: 0 }}>Request</h3>
|
||||
<div style={{ display: 'grid', gap: 12 }}>
|
||||
<div>
|
||||
<label style={{ display: 'block', color: '#888', marginBottom: 4, fontSize: 13 }}>API Key</label>
|
||||
<input type="text" value={apiKey} onChange={e => setApiKey(e.target.value)} placeholder="your-api-key"
|
||||
style={{ width: '100%', padding: 10, background: '#1a1a1a', border: '1px solid #333', borderRadius: 6, color: '#fff', fontSize: 13, boxSizing: 'border-box' }} />
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', color: '#888', marginBottom: 4, fontSize: 13 }}>Endpoint</label>
|
||||
<select value={endpoint} onChange={e => setEndpoint(e.target.value)}
|
||||
style={{ width: '100%', padding: 10, background: '#1a1a1a', border: '1px solid #333', borderRadius: 6, color: '#fff', fontSize: 13 }}>
|
||||
{endpoints.map(ep => <option key={ep.value} value={ep.value}>{ep.label}</option>)}
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', color: '#888', marginBottom: 4, fontSize: 13 }}>URL</label>
|
||||
<input type="text" value={url} onChange={e => setUrl(e.target.value)}
|
||||
style={{ width: '100%', padding: 10, background: '#1a1a1a', border: '1px solid #333', borderRadius: 6, color: '#fff', fontSize: 13, boxSizing: 'border-box' }} />
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ display: 'block', color: '#888', marginBottom: 4, fontSize: 13 }}>Options (JSON)</label>
|
||||
<textarea value={options} onChange={e => setOptions(e.target.value)} rows={4}
|
||||
style={{ width: '100%', padding: 10, background: '#1a1a1a', border: '1px solid #333', borderRadius: 6, color: '#fff', fontSize: 13, boxSizing: 'border-box', fontFamily: 'monospace' }} />
|
||||
</div>
|
||||
<button onClick={sendRequest} disabled={loading}
|
||||
style={{ background: loading ? '#333' : '#fff', color: '#000', padding: '12px', borderRadius: 6, border: 'none', fontWeight: 600, cursor: loading ? 'not-allowed' : 'pointer' }}>
|
||||
{loading ? 'Sending...' : 'Send Request'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ background: '#111', borderRadius: 12, padding: 24 }}>
|
||||
<h3 style={{ marginTop: 0 }}>Code Snippet</h3>
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 12 }}>
|
||||
{['curl', 'python', 'javascript'].map(l => (
|
||||
<button key={l} onClick={() => setCodeLang(l)}
|
||||
style={{ background: codeLang === l ? '#fff' : '#1a1a1a', color: codeLang === l ? '#000' : '#888', padding: '6px 12px', borderRadius: 4, border: 'none', fontSize: 12, cursor: 'pointer', textTransform: 'uppercase' }}>
|
||||
{l}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<pre style={{ margin: 0, fontSize: 12, overflow: 'auto', background: '#0a0a0a', padding: 12, borderRadius: 6 }}>{getCodeSnippet()}</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style={{ background: '#111', borderRadius: 12, padding: 24, height: '100%' }}>
|
||||
<h3 style={{ marginTop: 0 }}>Response</h3>
|
||||
{result ? (
|
||||
<pre style={{ margin: 0, fontSize: 13, overflow: 'auto', background: '#0a0a0a', padding: 12, borderRadius: 6, height: 'calc(100% - 40px)' }}>{result}</pre>
|
||||
) : (
|
||||
<div style={{ color: '#555', textAlign: 'center', padding: '40px 0' }}>Send a request to see the response</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user