Added Alpine.global() registration to enable template access to functions: - admin.ts: Added Alpine for scan, stats, and settings functions - api-explorer.ts: Already had Alpine (kept as is) - bookshelf.ts: Added Alpine for library/bookshelf interactions - collections.ts: Added Alpine for collection management - conflicts.ts: Added Alpine for conflict resolution - device-management.ts: Added Alpine with event delegation for dynamic content - header.ts: Added Alpine for theme dropdown and user menu - library.ts: Added Alpine registrations - linking.ts: Added Alpine registrations - queue.ts: Added Alpine for queue operations - search.ts: Added Alpine registrations - themeDropdown.ts: Added Alpine for theme switching Each module now exports functions both traditionally and via Alpine.global() for template access.
187 lines
5.2 KiB
TypeScript
187 lines
5.2 KiB
TypeScript
import { showToast } from "./toast";
|
|
|
|
interface ApiExplorerRequest {
|
|
method: string;
|
|
endpoint: string;
|
|
headers: Record<string, string>;
|
|
body?: string;
|
|
}
|
|
|
|
const requestHistory: ApiExplorerRequest[] = [];
|
|
|
|
function sendApiRequest(): void {
|
|
const method =
|
|
(document.getElementById("api-method") as HTMLSelectElement)?.value ||
|
|
"GET";
|
|
const endpoint =
|
|
(document.getElementById("api-endpoint") as HTMLInputElement)?.value || "";
|
|
const bodyText =
|
|
(document.getElementById("api-body") as HTMLTextAreaElement)?.value || "";
|
|
|
|
const token = localStorage.getItem("token");
|
|
|
|
const headers: Record<string, string> = {
|
|
"Content-Type": "application/json",
|
|
};
|
|
|
|
if (token) {
|
|
headers["Authorization"] = `Bearer ${token}`;
|
|
}
|
|
|
|
const request: ApiExplorerRequest = {
|
|
method,
|
|
endpoint,
|
|
headers,
|
|
body: bodyText || undefined,
|
|
};
|
|
|
|
addToHistory(request);
|
|
|
|
const startTime = performance.now();
|
|
|
|
fetch(endpoint, {
|
|
method,
|
|
headers,
|
|
body: bodyText || undefined,
|
|
})
|
|
.then(async (response) => {
|
|
const endTime = performance.now();
|
|
const duration = Math.round(endTime - startTime);
|
|
|
|
const responseText = await response.text();
|
|
let responseData: unknown;
|
|
try {
|
|
responseData = JSON.parse(responseText);
|
|
} catch {
|
|
responseData = responseText;
|
|
}
|
|
|
|
displayResponse(response, responseData, duration);
|
|
generateCurl(request);
|
|
})
|
|
.catch((error) => {
|
|
displayError(error);
|
|
});
|
|
}
|
|
|
|
function displayResponse(
|
|
response: Response,
|
|
data: unknown,
|
|
duration: number,
|
|
): void {
|
|
const container = document.getElementById("api-response");
|
|
if (!container) return;
|
|
|
|
const statusColor = response.ok ? "var(--accent)" : "var(--error)";
|
|
|
|
container.innerHTML = `
|
|
<div class="mb-4">
|
|
<span class="px-2 py-1 rounded text-sm" style="background-color: ${statusColor}; color: var(--bg-primary)">
|
|
${response.status} ${response.statusText}
|
|
</span>
|
|
<span class="text-sm ml-2" style="color: var(--text-secondary)">${duration}ms</span>
|
|
</div>
|
|
<pre class="p-4 rounded overflow-auto max-h-96" style="background-color: var(--bg-primary); color: var(--text-primary)">${JSON.stringify(data, null, 2)}</pre>
|
|
`;
|
|
}
|
|
|
|
function displayError(error: Error): void {
|
|
const container = document.getElementById("api-response");
|
|
if (!container) return;
|
|
|
|
container.innerHTML = `
|
|
<div class="p-4 rounded" style="background-color: var(--bg-primary)">
|
|
<p style="color: var(--error)">Error: ${error.message}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function generateCurl(request: ApiExplorerRequest): void {
|
|
const container = document.getElementById("curl-command");
|
|
if (!container) return;
|
|
|
|
let curl = `curl -X ${request.method} '${request.endpoint}'`;
|
|
|
|
Object.entries(request.headers).forEach(([key, value]) => {
|
|
curl += ` \\\n -H '${key}: ${value}'`;
|
|
});
|
|
|
|
if (request.body) {
|
|
curl += ` \\\n -d '${request.body}'`;
|
|
}
|
|
|
|
container.textContent = curl;
|
|
}
|
|
|
|
function addToHistory(request: ApiExplorerRequest): void {
|
|
requestHistory.unshift(request);
|
|
if (requestHistory.length > 20) {
|
|
requestHistory.pop();
|
|
}
|
|
renderHistory();
|
|
}
|
|
|
|
function renderHistory(): void {
|
|
const container = document.getElementById("request-history");
|
|
if (!container) return;
|
|
|
|
if (requestHistory.length === 0) {
|
|
container.innerHTML =
|
|
'<p class="text-sm p-2" style="color: var(--text-secondary)">No requests yet</p>';
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = requestHistory
|
|
.slice(0, 10)
|
|
.map(
|
|
(req, i) => `
|
|
<div class="p-2 rounded cursor-pointer hover:bg-opacity-50 transition-colors"
|
|
style="background-color: var(--bg-secondary)"
|
|
onclick="window.loadFromHistory(${i})">
|
|
<span class="text-xs font-mono" style="color: ${req.method === "GET" ? "var(--accent)" : req.method === "POST" ? "var(--success)" : req.method === "DELETE" ? "var(--error)" : "var(--text-primary)"}">${req.method}</span>
|
|
<span class="text-xs ml-2" style="color: var(--text-secondary)">${req.endpoint}</span>
|
|
</div>
|
|
`,
|
|
)
|
|
.join("");
|
|
}
|
|
|
|
function loadFromHistory(index: number): void {
|
|
const request = requestHistory[index];
|
|
if (!request) return;
|
|
|
|
const methodSelect = document.getElementById(
|
|
"api-method",
|
|
) as HTMLSelectElement;
|
|
const endpointInput = document.getElementById(
|
|
"api-endpoint",
|
|
) as HTMLInputElement;
|
|
const bodyInput = document.getElementById("api-body") as HTMLTextAreaElement;
|
|
|
|
if (methodSelect) methodSelect.value = request.method;
|
|
if (endpointInput) endpointInput.value = request.endpoint;
|
|
if (bodyInput) bodyInput.value = request.body || "";
|
|
}
|
|
|
|
function copyCurl(): void {
|
|
const curl = document.getElementById("curl-command")?.textContent;
|
|
if (curl) {
|
|
navigator.clipboard.writeText(curl);
|
|
showToast("cURL copied to clipboard", "success");
|
|
}
|
|
}
|
|
|
|
function formatJson(): void {
|
|
const bodyInput = document.getElementById("api-body") as HTMLTextAreaElement;
|
|
if (!bodyInput) return;
|
|
|
|
try {
|
|
const parsed = JSON.parse(bodyInput.value);
|
|
bodyInput.value = JSON.stringify(parsed, null, 2);
|
|
} catch {
|
|
showToast("Invalid JSON", "error");
|
|
}
|
|
}
|
|
|
|
export { sendApiRequest, loadFromHistory, copyCurl, formatJson };
|