API.jsx
1 import { useState, useEffect } from "react"; 2 import { 3 Box, Card, Chip, IconButton, Tab, Tabs, Tooltip, Typography, styled, 4 } from "@mui/material"; 5 import { ContentCopy, Check } from "@mui/icons-material"; 6 import Breadcrumb from "app/components/Breadcrumb"; 7 import { useParams } from "react-router-dom"; 8 import useAuth from "app/hooks/useAuth"; 9 import api from "app/utils/api"; 10 11 const Container = styled("div")(({ theme }) => ({ 12 margin: "24px 48px", 13 [theme.breakpoints.down("md")]: { margin: "24px 32px" }, 14 [theme.breakpoints.down("sm")]: { margin: 16 }, 15 "& .breadcrumb": { marginBottom: 24 }, 16 })); 17 18 const CodeBlock = styled(Box)(() => ({ 19 background: "#1e1e2e", 20 color: "#cdd6f4", 21 padding: "20px 24px", 22 borderRadius: "0 0 10px 10px", 23 fontFamily: "'JetBrains Mono', 'SF Mono', Monaco, Consolas, monospace", 24 fontSize: "0.82rem", 25 lineHeight: 1.7, 26 overflowX: "auto", 27 whiteSpace: "pre", 28 position: "relative", 29 tabSize: 4, 30 })); 31 32 const LangTab = styled(Tab)(() => ({ 33 textTransform: "none", 34 fontWeight: 500, 35 fontSize: "0.85rem", 36 minHeight: 40, 37 minWidth: 80, 38 padding: "6px 16px", 39 })); 40 41 const LANGUAGES = [ 42 { id: "curl", label: "cURL" }, 43 { id: "python", label: "Python" }, 44 { id: "javascript", label: "JavaScript" }, 45 { id: "php", label: "PHP" }, 46 { id: "go", label: "Go" }, 47 { id: "ruby", label: "Ruby" }, 48 ]; 49 50 function replaceVars(code, project) { 51 const url = window.location.protocol + "//" + window.location.host; 52 const question = project.default_prompt || "What can you help me with?"; 53 return code 54 .replaceAll("<URL>", url) 55 .replaceAll("<PROJECT>", project.name || "my-project") 56 .replaceAll("<QUESTION>", question); 57 } 58 59 // ── Code templates ────────────────────────────────────────────────────── 60 61 const snippets = { 62 curl: { 63 question: `curl -X POST '<URL>/projects/<PROJECT>/question' \\ 64 -H 'Content-Type: application/json' \\ 65 -H 'Authorization: Bearer YOUR_API_KEY' \\ 66 -d '{ 67 "question": "<QUESTION>" 68 }'`, 69 chat: `# First message — omit "id" to start a new conversation 70 curl -X POST '<URL>/projects/<PROJECT>/chat' \\ 71 -H 'Content-Type: application/json' \\ 72 -H 'Authorization: Bearer YOUR_API_KEY' \\ 73 -d '{ 74 "question": "<QUESTION>" 75 }' 76 77 # Follow-up — include the "id" from the first response 78 curl -X POST '<URL>/projects/<PROJECT>/chat' \\ 79 -H 'Content-Type: application/json' \\ 80 -H 'Authorization: Bearer YOUR_API_KEY' \\ 81 -d '{ 82 "question": "Tell me more", 83 "id": "CHAT_ID_FROM_FIRST_RESPONSE" 84 }'`, 85 }, 86 python: { 87 question: `import requests 88 89 API_KEY = "YOUR_API_KEY" 90 91 response = requests.post( 92 "<URL>/projects/<PROJECT>/question", 93 headers={ 94 "Content-Type": "application/json", 95 "Authorization": f"Bearer {API_KEY}", 96 }, 97 json={"question": "<QUESTION>"}, 98 ) 99 100 data = response.json() 101 print(data["answer"])`, 102 chat: `import requests 103 104 API_KEY = "YOUR_API_KEY" 105 106 def chat(question, chat_id=None): 107 body = {"question": question} 108 if chat_id: 109 body["id"] = chat_id 110 111 response = requests.post( 112 "<URL>/projects/<PROJECT>/chat", 113 headers={ 114 "Content-Type": "application/json", 115 "Authorization": f"Bearer {API_KEY}", 116 }, 117 json=body, 118 ) 119 return response.json() 120 121 # Start a new conversation 122 result = chat("<QUESTION>") 123 print(result["answer"]) 124 125 # Continue the conversation using the returned id 126 result = chat("Tell me more", chat_id=result["id"]) 127 print(result["answer"])`, 128 }, 129 javascript: { 130 question: `const API_KEY = "YOUR_API_KEY"; 131 132 const response = await fetch("<URL>/projects/<PROJECT>/question", { 133 method: "POST", 134 headers: { 135 "Content-Type": "application/json", 136 "Authorization": \`Bearer \${API_KEY}\`, 137 }, 138 body: JSON.stringify({ question: "<QUESTION>" }), 139 }); 140 141 const data = await response.json(); 142 console.log(data.answer);`, 143 chat: `const API_KEY = "YOUR_API_KEY"; 144 145 async function chat(question, chatId) { 146 const body = { question }; 147 if (chatId) body.id = chatId; 148 149 const response = await fetch("<URL>/projects/<PROJECT>/chat", { 150 method: "POST", 151 headers: { 152 "Content-Type": "application/json", 153 "Authorization": \`Bearer \${API_KEY}\`, 154 }, 155 body: JSON.stringify(body), 156 }); 157 158 return response.json(); 159 } 160 161 // Start a new conversation 162 const result = await chat("<QUESTION>"); 163 console.log(result.answer); 164 165 // Continue the conversation using the returned id 166 const followUp = await chat("Tell me more", result.id); 167 console.log(followUp.answer);`, 168 }, 169 php: { 170 question: `<?php 171 172 $apiKey = "YOUR_API_KEY"; 173 174 $ch = curl_init("<URL>/projects/<PROJECT>/question"); 175 curl_setopt_array($ch, [ 176 CURLOPT_RETURNTRANSFER => true, 177 CURLOPT_POST => true, 178 CURLOPT_POSTFIELDS => json_encode([ 179 "question" => "<QUESTION>", 180 ]), 181 CURLOPT_HTTPHEADER => [ 182 "Content-Type: application/json", 183 "Authorization: Bearer " . $apiKey, 184 ], 185 ]); 186 187 $response = curl_exec($ch); 188 curl_close($ch); 189 190 $data = json_decode($response, true); 191 echo $data["answer"];`, 192 chat: `<?php 193 194 $apiKey = "YOUR_API_KEY"; 195 196 function chat($question, $chatId = null) { 197 global $apiKey; 198 199 $body = ["question" => $question]; 200 if ($chatId) { 201 $body["id"] = $chatId; 202 } 203 204 $ch = curl_init("<URL>/projects/<PROJECT>/chat"); 205 curl_setopt_array($ch, [ 206 CURLOPT_RETURNTRANSFER => true, 207 CURLOPT_POST => true, 208 CURLOPT_POSTFIELDS => json_encode($body), 209 CURLOPT_HTTPHEADER => [ 210 "Content-Type: application/json", 211 "Authorization: Bearer " . $apiKey, 212 ], 213 ]); 214 215 $response = curl_exec($ch); 216 curl_close($ch); 217 218 return json_decode($response, true); 219 } 220 221 // Start a new conversation 222 $result = chat("<QUESTION>"); 223 echo $result["answer"]; 224 225 // Continue the conversation using the returned id 226 $result = chat("Tell me more", $result["id"]); 227 echo $result["answer"];`, 228 }, 229 go: { 230 question: `package main 231 232 import ( 233 "bytes" 234 "encoding/json" 235 "fmt" 236 "io" 237 "net/http" 238 ) 239 240 func main() { 241 apiKey := "YOUR_API_KEY" 242 243 body, _ := json.Marshal(map[string]string{ 244 "question": "<QUESTION>", 245 }) 246 247 req, _ := http.NewRequest("POST", 248 "<URL>/projects/<PROJECT>/question", 249 bytes.NewBuffer(body)) 250 req.Header.Set("Content-Type", "application/json") 251 req.Header.Set("Authorization", "Bearer "+apiKey) 252 253 resp, err := http.DefaultClient.Do(req) 254 if err != nil { 255 panic(err) 256 } 257 defer resp.Body.Close() 258 259 respBody, _ := io.ReadAll(resp.Body) 260 261 var data map[string]interface{} 262 json.Unmarshal(respBody, &data) 263 fmt.Println(data["answer"]) 264 }`, 265 chat: `package main 266 267 import ( 268 "bytes" 269 "encoding/json" 270 "fmt" 271 "io" 272 "net/http" 273 ) 274 275 func chat(apiKey, question string, chatID *string) map[string]interface{} { 276 body := map[string]string{"question": question} 277 if chatID != nil { 278 body["id"] = *chatID 279 } 280 281 payload, _ := json.Marshal(body) 282 req, _ := http.NewRequest("POST", 283 "<URL>/projects/<PROJECT>/chat", 284 bytes.NewBuffer(payload)) 285 req.Header.Set("Content-Type", "application/json") 286 req.Header.Set("Authorization", "Bearer "+apiKey) 287 288 resp, _ := http.DefaultClient.Do(req) 289 defer resp.Body.Close() 290 291 respBody, _ := io.ReadAll(resp.Body) 292 293 var data map[string]interface{} 294 json.Unmarshal(respBody, &data) 295 return data 296 } 297 298 func main() { 299 apiKey := "YOUR_API_KEY" 300 301 // Start a new conversation 302 result := chat(apiKey, "<QUESTION>", nil) 303 fmt.Println(result["answer"]) 304 305 // Continue using the returned id 306 id := result["id"].(string) 307 result = chat(apiKey, "Tell me more", &id) 308 fmt.Println(result["answer"]) 309 }`, 310 }, 311 ruby: { 312 question: `require "net/http" 313 require "json" 314 require "uri" 315 316 api_key = "YOUR_API_KEY" 317 318 uri = URI("<URL>/projects/<PROJECT>/question") 319 http = Net::HTTP.new(uri.host, uri.port) 320 http.use_ssl = uri.scheme == "https" 321 322 request = Net::HTTP::Post.new(uri) 323 request["Content-Type"] = "application/json" 324 request["Authorization"] = "Bearer #{api_key}" 325 request.body = { question: "<QUESTION>" }.to_json 326 327 response = http.request(request) 328 data = JSON.parse(response.body) 329 puts data["answer"]`, 330 chat: `require "net/http" 331 require "json" 332 require "uri" 333 334 api_key = "YOUR_API_KEY" 335 336 def chat(api_key, question, chat_id = nil) 337 uri = URI("<URL>/projects/<PROJECT>/chat") 338 http = Net::HTTP.new(uri.host, uri.port) 339 http.use_ssl = uri.scheme == "https" 340 341 body = { question: question } 342 body[:id] = chat_id if chat_id 343 344 request = Net::HTTP::Post.new(uri) 345 request["Content-Type"] = "application/json" 346 request["Authorization"] = "Bearer #{api_key}" 347 request.body = body.to_json 348 349 response = http.request(request) 350 JSON.parse(response.body) 351 end 352 353 # Start a new conversation 354 result = chat(api_key, "<QUESTION>") 355 puts result["answer"] 356 357 # Continue using the returned id 358 result = chat(api_key, "Tell me more", result["id"]) 359 puts result["answer"]`, 360 }, 361 }; 362 363 // ── Copy button ───────────────────────────────────────────────────────── 364 365 function CopyButton({ text }) { 366 const [copied, setCopied] = useState(false); 367 const handleCopy = () => { 368 navigator.clipboard.writeText(text); 369 setCopied(true); 370 setTimeout(() => setCopied(false), 2000); 371 }; 372 return ( 373 <Tooltip title={copied ? "Copied!" : "Copy code"}> 374 <IconButton 375 size="small" 376 onClick={handleCopy} 377 sx={{ 378 position: "absolute", top: 10, right: 10, 379 color: "#6c7086", 380 "&:hover": { color: "#cdd6f4", background: "rgba(255,255,255,0.08)" }, 381 }} 382 > 383 {copied ? <Check fontSize="small" /> : <ContentCopy fontSize="small" />} 384 </IconButton> 385 </Tooltip> 386 ); 387 } 388 389 // ── Example card ──────────────────────────────────────────────────────── 390 391 function ExampleCard({ title, description, endpoint, method, code }) { 392 return ( 393 <Card 394 variant="outlined" 395 sx={{ 396 borderRadius: "10px", 397 overflow: "hidden", 398 border: "1px solid", 399 borderColor: "divider", 400 }} 401 > 402 <Box sx={{ p: 2.5, pb: 1.5 }}> 403 <Box sx={{ display: "flex", alignItems: "center", gap: 1, mb: 0.5 }}> 404 <Typography variant="subtitle1" fontWeight={600}>{title}</Typography> 405 <Chip label={method} size="small" variant="outlined" 406 sx={{ fontSize: "0.7rem", height: 22, fontFamily: "monospace", fontWeight: 600 }} /> 407 </Box> 408 <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}> 409 {description} 410 </Typography> 411 <Typography 412 variant="caption" 413 sx={{ 414 fontFamily: "monospace", 415 color: "primary.main", 416 background: (t) => t.palette.mode === "dark" ? "rgba(99,102,241,0.1)" : "rgba(99,102,241,0.08)", 417 px: 1, py: 0.3, borderRadius: 1, 418 }} 419 > 420 {endpoint} 421 </Typography> 422 </Box> 423 <CodeBlock> 424 <CopyButton text={code} /> 425 {code} 426 </CodeBlock> 427 </Card> 428 ); 429 } 430 431 // ── Main component ────────────────────────────────────────────────────── 432 433 export default function ProjectAPI() { 434 const { id } = useParams(); 435 const [project, setProject] = useState({}); 436 const [lang, setLang] = useState("curl"); 437 const auth = useAuth(); 438 439 useEffect(() => { 440 document.title = (process.env.REACT_APP_RESTAI_NAME || "RESTai") + " - API - " + id; 441 api.get("/projects/" + id, auth.user.token) 442 .then((d) => setProject(d)) 443 .catch(() => {}); 444 }, [id]); 445 446 const projectName = project.name || "my-project"; 447 const langSnippets = snippets[lang] || snippets.curl; 448 const questionCode = replaceVars(langSnippets.question, project); 449 const chatCode = replaceVars(langSnippets.chat, project); 450 451 return ( 452 <Container> 453 <Box className="breadcrumb"> 454 <Breadcrumb 455 routeSegments={[ 456 { name: "Projects", path: "/projects" }, 457 { name: projectName, path: "/project/" + id }, 458 { name: "API" }, 459 ]} 460 /> 461 </Box> 462 463 <Box> 464 <Typography variant="h5" fontWeight={700} sx={{ mb: 0.5 }}> 465 API Reference 466 </Typography> 467 <Typography variant="body2" color="text.secondary" sx={{ mb: 3 }}> 468 Use your API key to interact with the <strong>{projectName}</strong> project programmatically. 469 </Typography> 470 471 {/* Language selector */} 472 <Tabs 473 value={lang} 474 onChange={(_, v) => setLang(v)} 475 variant="scrollable" 476 scrollButtons="auto" 477 sx={{ 478 mb: 3, 479 minHeight: 40, 480 "& .MuiTabs-indicator": { height: 2.5, borderRadius: 2 }, 481 }} 482 > 483 {LANGUAGES.map((l) => ( 484 <LangTab key={l.id} value={l.id} label={l.label} /> 485 ))} 486 </Tabs> 487 488 {/* Examples */} 489 <Box sx={{ display: "flex", flexDirection: "column", gap: 3 }}> 490 <ExampleCard 491 title="Question" 492 description="Send a one-shot question. Each request is independent with no conversation memory." 493 endpoint={`POST /projects/${projectName}/question`} 494 method="POST" 495 code={questionCode} 496 /> 497 <ExampleCard 498 title="Chat" 499 description="Start or continue a conversation. Omit the id on the first request; include the returned id for follow-ups." 500 endpoint={`POST /projects/${projectName}/chat`} 501 method="POST" 502 code={chatCode} 503 /> 504 </Box> 505 </Box> 506 </Container> 507 ); 508 }