- Remove unused `generate_example_call` and `generate_example_response` functions - Rename `example_call` to `example_request` in `DocMethod` - Update schema example extraction to use `schema.example` directly - Introduce `generate_request_example` and `generate_response_example` for dynamic example generation - Change type of `id` from string to number in schema examples PS: The work is still in progress
482 lines
19 KiB
HTML
482 lines
19 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>${spec.info.title} - API Documentation</title>
|
|
|
|
<!-- Bootstrap CSS -->
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
|
|
<style>
|
|
.method-card {
|
|
margin-bottom: 1.5rem;
|
|
}
|
|
|
|
.param-table {
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.code-block {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #e9ecef;
|
|
border-radius: 0.375rem;
|
|
padding: 1rem;
|
|
overflow-x: auto;
|
|
font-family: 'Courier New', monospace;
|
|
}
|
|
|
|
.object-section {
|
|
margin-bottom: 3rem;
|
|
}
|
|
|
|
.method-endpoint {
|
|
background-color: #e3f2fd;
|
|
padding: 0.5rem;
|
|
border-radius: 0.25rem;
|
|
font-family: monospace;
|
|
font-size: 0.9rem;
|
|
}
|
|
|
|
.toc {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.375rem;
|
|
padding: 1rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
|
|
.toc ul {
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.toc a {
|
|
text-decoration: none;
|
|
color: #495057;
|
|
}
|
|
|
|
.toc a:hover {
|
|
color: #007bff;
|
|
}
|
|
|
|
pre {
|
|
margin: 0;
|
|
white-space: pre-wrap;
|
|
word-wrap: break-word;
|
|
}
|
|
|
|
.curl-section {
|
|
background-color: #f8f9fa;
|
|
border: 1px solid #dee2e6;
|
|
border-radius: 0.375rem;
|
|
padding: 1rem;
|
|
margin-top: 1rem;
|
|
position: relative;
|
|
}
|
|
|
|
.copy-button {
|
|
position: absolute;
|
|
top: 0.5rem;
|
|
right: 0.5rem;
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 0.25rem;
|
|
padding: 0.25rem 0.5rem;
|
|
font-size: 0.75rem;
|
|
cursor: pointer;
|
|
transition: background-color 0.2s;
|
|
}
|
|
|
|
.copy-button:hover {
|
|
background: #0056b3;
|
|
}
|
|
|
|
.copy-button.copied {
|
|
background: #28a745;
|
|
}
|
|
|
|
.curl-command {
|
|
font-family: 'Courier New', monospace;
|
|
font-size: 0.85rem;
|
|
margin: 0;
|
|
padding-right: 4rem;
|
|
}
|
|
</style>
|
|
</head>
|
|
|
|
<body>
|
|
<div class="container mt-4">
|
|
<!-- Header -->
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h1 class="display-4">${spec.info.title}</h1>
|
|
@if spec.info.description.len > 0
|
|
<p class="lead">${spec.info.description}</p>
|
|
@end
|
|
|
|
<!-- Service Information -->
|
|
<div class="row mb-3">
|
|
<div class="col-md-6">
|
|
<p><strong>Version:</strong> <span class="badge bg-primary">${spec.info.version}</span></p>
|
|
@if spec.info.terms_of_service.len > 0
|
|
<p><strong>Terms of Service:</strong> <a href="${spec.info.terms_of_service}"
|
|
target="_blank">View Terms</a></p>
|
|
@end
|
|
</div>
|
|
<div class="col-md-6">
|
|
@if spec.info.contact.name.len > 0
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<h6 class="card-title">Contact Information</h6>
|
|
@if spec.info.contact.name.len > 0
|
|
<p class="card-text"><strong>Name:</strong> ${spec.info.contact.name}</p>
|
|
@end
|
|
@if spec.info.contact.email.len > 0
|
|
<p class="card-text"><strong>Email:</strong> <a
|
|
href="mailto:${spec.info.contact.email}">${spec.info.contact.email}</a></p>
|
|
@end
|
|
@if spec.info.contact.url.len > 0
|
|
<p class="card-text"><strong>Website:</strong> <a href="${spec.info.contact.url}"
|
|
target="_blank">${spec.info.contact.url}</a></p>
|
|
@end
|
|
</div>
|
|
</div>
|
|
@end
|
|
|
|
@if spec.info.license.name.len > 0
|
|
<div class="card mt-2">
|
|
<div class="card-body">
|
|
<h6 class="card-title">License</h6>
|
|
@if spec.info.license.name.len > 0
|
|
<p class="card-text"><strong>License:</strong> ${spec.info.license.name}</p>
|
|
@end
|
|
@if spec.info.license.url.len > 0
|
|
<p class="card-text"><a href="${spec.info.license.url}" target="_blank">View License</a>
|
|
</p>
|
|
@end
|
|
</div>
|
|
</div>
|
|
@end
|
|
</div>
|
|
</div>
|
|
<hr>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Authentication Info -->
|
|
<!-- <div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="alert alert-info">
|
|
<h5>Authentication Required</h5>
|
|
<p>All API endpoints require authentication using a session key obtained through the authentication
|
|
flow:</p>
|
|
<ol>
|
|
@for step in spec.auth_info.steps
|
|
<li>
|
|
<strong>${step.title}</strong>:
|
|
<code>${step.method} ${step.endpoint}</code>
|
|
- ${step.description}
|
|
<div class="code-block mt-2">${step.example}</div>
|
|
</li>
|
|
@end
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div> -->
|
|
|
|
<!-- Table of Contents -->
|
|
@if spec.methods.len > 0 || spec.objects.len > 0
|
|
<div class="row mb-4">
|
|
<div class="col-12">
|
|
<div class="toc">
|
|
<h5>Table of Contents</h5>
|
|
@if spec.methods.len > 0
|
|
<h6>API Methods</h6>
|
|
<ul>
|
|
@for method in spec.methods
|
|
<li><a href="#method-${method.name}">${method.name}</a> - ${method.summary}</li>
|
|
@end
|
|
</ul>
|
|
@end
|
|
|
|
@if spec.objects.len > 0
|
|
<h6>API Objects</h6>
|
|
<ul>
|
|
@for object in spec.objects
|
|
<li><a href="#object-${object.name}">${object.name.title()}</a> - ${object.description}</li>
|
|
@end
|
|
</ul>
|
|
@end
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@end
|
|
|
|
<!-- API Methods -->
|
|
@if spec.methods.len > 0
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h2>API Methods</h2>
|
|
<p class="text-muted">Available methods for this service</p>
|
|
|
|
@for method in spec.methods
|
|
<div class="card method-card" id="method-${method.name}">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">${method.name}</h5>
|
|
<div class="method-endpoint mt-2 mb-2">POST ${method.endpoint_url}</div>
|
|
</div>
|
|
<div class="card-body">
|
|
@if method.summary.len > 0
|
|
<p class="card-text"><strong>${method.summary}</strong></p>
|
|
@end
|
|
@if method.description.len > 0
|
|
<p class="card-text">${method.description}</p>
|
|
@end
|
|
|
|
<!-- Parameters -->
|
|
@if method.params.len > 0
|
|
<h6>Parameters:</h6>
|
|
<table class="table table-sm param-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Type</th>
|
|
<th>Required</th>
|
|
<th>Description</th>
|
|
<th>Example</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for param in method.params
|
|
<tr>
|
|
<td><code>${param.name}</code></td>
|
|
<td><span class="badge bg-secondary">${param.type_info}</span></td>
|
|
@if param.required
|
|
<td><span class="badge bg-danger">Required</span></td>
|
|
@else
|
|
<td><span class="badge bg-success">Optional</span></td>
|
|
@end
|
|
<td>${param.description}</td>
|
|
<td><code class="text-muted">${param.example}</code></td>
|
|
</tr>
|
|
@end
|
|
</tbody>
|
|
</table>
|
|
@end
|
|
|
|
<!-- Return Value -->
|
|
@if method.result.name.len > 0
|
|
<h6>Returns:</h6>
|
|
<div class="alert alert-light">
|
|
<strong>Type:</strong> <span class="badge bg-info">${method.result.type_info}</span><br>
|
|
<strong>Description:</strong> ${method.result.description}<br>
|
|
@if method.result.example.len > 0
|
|
<strong>Example:</strong> <code class="text-muted">${method.result.example}</code>
|
|
@end
|
|
</div>
|
|
@end
|
|
|
|
<!-- Examples -->
|
|
<div class="row mt-3">
|
|
<div class="col-md-6">
|
|
<h6>Example Request:</h6>
|
|
<div class="code-block">
|
|
<pre>${method.example_request}</pre>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Example Response:</h6>
|
|
<div class="code-block">
|
|
<pre>${method.example_response}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Curl Example -->
|
|
<div class="curl-section">
|
|
<h6>Curl Command:</h6>
|
|
<button class="copy-button" onclick="copyToClipboard('curl-${method.name}', this)">
|
|
Copy
|
|
</button>
|
|
<pre class="curl-command" id="curl-${method.name}">${method.curl_example}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@end
|
|
</div>
|
|
</div>
|
|
@end
|
|
|
|
<!-- API Objects (if any) -->
|
|
@if spec.objects.len > 0
|
|
@for object in spec.objects
|
|
<div class="object-section" id="object-${object.name}">
|
|
<div class="row">
|
|
<div class="col-12">
|
|
<h2>${object.name.title()}</h2>
|
|
@if object.description.len > 0
|
|
<p>${object.description}</p>
|
|
@end
|
|
|
|
<!-- Methods for this object -->
|
|
@for method in object.methods
|
|
<div class="card method-card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">${method.name}</h5>
|
|
<div class="method-endpoint">POST ${method.endpoint_url}</div>
|
|
</div>
|
|
<div class="card-body">
|
|
@if method.summary.len > 0
|
|
<p class="card-text"><strong>${method.summary}</strong></p>
|
|
@end
|
|
@if method.description.len > 0
|
|
<p class="card-text">${method.description}</p>
|
|
@end
|
|
|
|
<!-- Parameters -->
|
|
@if method.params.len > 0
|
|
<h6>Parameters:</h6>
|
|
<table class="table table-sm param-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Type</th>
|
|
<th>Required</th>
|
|
<th>Description</th>
|
|
<th>Example</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@for param in method.params
|
|
<tr>
|
|
<td><code>${param.name}</code></td>
|
|
<td><span class="badge bg-secondary">${param.type_info}</span></td>
|
|
@if param.required
|
|
<td><span class="badge bg-danger">Required</span></td>
|
|
@else
|
|
<td><span class="badge bg-success">Optional</span></td>
|
|
@end
|
|
<td>${param.description}</td>
|
|
<td><code class="text-muted">${param.example}</code></td>
|
|
</tr>
|
|
@end
|
|
</tbody>
|
|
</table>
|
|
@end
|
|
|
|
<!-- Return Value -->
|
|
@if method.result.name.len > 0
|
|
<h6>Returns:</h6>
|
|
<div class="alert alert-light">
|
|
<strong>Type:</strong> <span class="badge bg-info">${method.result.type_info}</span><br>
|
|
<strong>Description:</strong> ${method.result.description}<br>
|
|
@if method.result.example.len > 0
|
|
<strong>Example:</strong> <code class="text-muted">${method.result.example}</code>
|
|
@end
|
|
</div>
|
|
@end
|
|
|
|
<!-- Examples -->
|
|
<div class="row mt-3">
|
|
<div class="col-md-6">
|
|
<h6>Example Request:</h6>
|
|
<div class="code-block">
|
|
<pre>${method.example_request}</pre>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-6">
|
|
<h6>Example Response:</h6>
|
|
<div class="code-block">
|
|
<pre>${method.example_response}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Curl Example -->
|
|
<div class="curl-section">
|
|
<h6>Curl Command:</h6>
|
|
<button class="copy-button" onclick="copyToClipboard('curl-${method.name}', this)">
|
|
📋 Copy
|
|
</button>
|
|
<pre class="curl-command" id="curl-${method.name}">${method.curl_example}</pre>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@end
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@end
|
|
@end
|
|
|
|
<!-- Footer -->
|
|
<footer class="mt-5 py-4 bg-light">
|
|
<div class="container text-center">
|
|
<p class="mb-0">Generated from OpenRPC specification • ${spec.info.title} v${spec.info.version}</p>
|
|
</div>
|
|
</footer>
|
|
</div>
|
|
|
|
<!-- Bootstrap JS -->
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.3/dist/js/bootstrap.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
|
|
|
<!-- Copy to Clipboard Functionality -->
|
|
<script>
|
|
function copyToClipboard(elementId, button) {
|
|
const element = document.getElementById(elementId);
|
|
const text = element.textContent;
|
|
|
|
// Use the modern Clipboard API
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
showCopySuccess(button);
|
|
}).catch(err => {
|
|
console.error('Failed to copy: ', err);
|
|
fallbackCopyTextToClipboard(text, button);
|
|
});
|
|
} else {
|
|
// Fallback for older browsers
|
|
fallbackCopyTextToClipboard(text, button);
|
|
}
|
|
}
|
|
|
|
function fallbackCopyTextToClipboard(text, button) {
|
|
const textArea = document.createElement("textarea");
|
|
textArea.value = text;
|
|
textArea.style.top = "0";
|
|
textArea.style.left = "0";
|
|
textArea.style.position = "fixed";
|
|
|
|
document.body.appendChild(textArea);
|
|
textArea.focus();
|
|
textArea.select();
|
|
|
|
try {
|
|
const successful = document.execCommand('copy');
|
|
if (successful) {
|
|
showCopySuccess(button);
|
|
}
|
|
} catch (err) {
|
|
console.error('Fallback: Oops, unable to copy', err);
|
|
}
|
|
|
|
document.body.removeChild(textArea);
|
|
}
|
|
|
|
function showCopySuccess(button) {
|
|
const originalText = button.textContent;
|
|
button.textContent = 'Copied!';
|
|
button.classList.add('copied');
|
|
|
|
setTimeout(() => {
|
|
button.textContent = originalText;
|
|
button.classList.remove('copied');
|
|
}, 2000);
|
|
}
|
|
</script>
|
|
</body>
|
|
|
|
</html> |