docs: scalar

This commit is contained in:
grtsinry43 2025-10-24 06:21:56 +00:00
parent 5e290945fb
commit 5904ade3db

View File

@ -46,15 +46,54 @@ async function bootstrap() {
openapi: {
info: {
title: 'Project Dash API',
description: 'Backend API documentation for Project Dash application.',
description: `
# GitHub Dashboard Backend API
Backend API for the Project Dash application - A modern GitHub repository dashboard with real-time statistics and analytics.
## Features
- 🔐 GitHub OAuth Authentication
- 📊 Repository Statistics & Analytics
- 🔔 GitHub Webhook Integration
- Redis Caching for Performance
## Authentication
Most endpoints require authentication via JWT token.
**How to authenticate:**
1. Login via \`POST /auth/login\` with GitHub OAuth code
2. Use the returned JWT token in the \`Authorization\` header: \`Bearer <token>\`
## Rate Limiting
GitHub API rate limits apply. Authenticated requests: 5,000/hour.
`,
version: '1.0.0',
contact: {
name: 'API Support',
url: 'https://github.com/yourusername/proj-dash-backend',
},
license: {
name: 'Apache 2.0',
url: 'https://www.apache.org/licenses/LICENSE-2.0.html',
},
},
servers: [
{
url: 'http://localhost:3333',
description: 'Local development server',
},
{
url: 'https://api.projectdash.example.com',
description: 'Production server',
},
],
components: {
securitySchemes: {
bearerAuth: {
type: 'http',
scheme: 'bearer',
bearerFormat: 'JWT',
description: 'Enter your JWT token obtained from `/auth/login`',
},
},
},
@ -63,11 +102,28 @@ async function bootstrap() {
bearerAuth: [],
},
],
tags: [
{
name: 'auth',
description: 'Authentication endpoints',
},
{
name: 'repos',
description: 'Repository management and statistics',
},
{
name: 'stats',
description: 'User statistics and analytics',
},
{
name: 'webhooks',
description: 'GitHub webhook handlers',
},
],
},
})
// --- Docs portal ---
// Top-level docs landing page that links to multiple renderers (ReDoc, Stoplight Elements, RapiDoc)
app.get('/docs', (_, reply) => {
reply.type('text/html').send(`
<!doctype html>
@ -75,153 +131,33 @@ async function bootstrap() {
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>API Documentation</title>
<title>API Docs Project Dashboard</title>
<style>
body { font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial; margin:0; padding:40px; background:#0f172a; color:#e6eef8 }
.container { max-width:900px; margin:0 auto; }
h1 { margin:0 0 8px; font-size:28px; }
p { color:#cbd5e1 }
.cards { display:flex; gap:16px; margin-top:24px; }
.card { background:linear-gradient(180deg,#0b1220,#061226); padding:18px; border-radius:8px; flex:1; box-shadow:0 6px 18px rgba(2,6,23,0.6); }
.card a { color:#7dd3fc; text-decoration:none; font-weight:600 }
.logo { height:36px; margin-bottom:12px }
body { margin: 0; }
</style>
</head>
<body>
<div class="container">
<img class="logo" src="https://raw.githubusercontent.com/readmeio/presskit/master/logos/readme-logo-white.svg" alt="Docs" />
<h1>API Documentation</h1>
<p>Choose a renderer. <strong>ReDoc</strong> is visually polished; <strong>Stoplight</strong> gives a modern interactive portal similar to Knife4j.</p>
<div class="cards">
<div class="card">
<h3>ReDoc Readable & clean</h3>
<p style="color:#9fb4c9">Great for well-structured API reference. Non-interactive but very pretty.</p>
<p><a href="/docs/redoc">Open ReDoc </a></p>
</div>
<div class="card">
<h3>Stoplight Elements Interactive</h3>
<p style="color:#9fb4c9">Modern UI with playground/try-it-out support and a Knife4j-like feel.</p>
<p><a href="/docs/stoplight">Open Stoplight </a></p>
</div>
<div class="card">
<h3>RapiDoc Lightweight</h3>
<p style="color:#9fb4c9">Existing lightweight renderer included for quick inspection.</p>
<p><a href="/docs/rapidoc">Open RapiDoc </a></p>
</div>
</div>
</div>
</body>
</html>
`)
})
// --- RapiDoc route (moved from /docs to /docs/rapidoc) ---
app.get('/docs/rapidoc', (_, reply) => {
reply.type('text/html').send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
</head>
<body>
<rapi-doc
spec-url="/docs/json"
theme="dark"
render-style="view"
show-header="false"
allow-server-selection="false"
allow-authentication="true"
> </rapi-doc>
</body>
</html>
`)
})
// --- API Documentation with ReDoc (alternative nicer UI) ---
// ReDoc gives a clean, modern single-page OpenAPI rendering similar to Knife4j's look.
app.get('/docs/redoc', (_, reply) => {
reply.type('text/html').send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>API Docs ReDoc</title>
<!-- ReDoc standalone bundle from CDN -->
<script src="https://cdn.redoc.ly/redoc/latest/bundles/redoc.standalone.js"></script>
<style>
body { margin: 0; padding: 0; }
#redoc-container { height: 100vh; }
</style>
</head>
<body>
<div id="redoc-container"></div>
<!-- Scalar API Reference -->
<script id="api-reference" data-url="/docs/json"></script>
<script>
// Render ReDoc into the container and point it at the generated OpenAPI JSON
Redoc.init('/docs/json', {
scrollYOffset: 50,
theme: {
colors: { primary: { main: '#2b6cb0' } },
typography: { fontSize: '14px' }
var configuration = {
theme: 'purple',
layout: 'modern',
darkMode: true,
hideModels: false,
hideDownloadButton: false,
searchHotKey: 'k',
customCss: \`
.scalar-app {
--scalar-font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial;
}
\`
}
}, document.getElementById('redoc-container'));
</script>
</body>
</html>
`)
})
// --- Stoplight Elements (interactive, Knife4j-like feel) ---
// Uses Stoplight Elements Web Components from CDN to render a modern interactive portal.
app.get('/docs/stoplight', (_, reply) => {
reply.type('text/html').send(`
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>API Docs Stoplight</title>
<link rel="stylesheet" href="https://unpkg.com/@stoplight/elements/styles.min.css" />
<!-- Load Stoplight Elements as an ES module; add ?module for ESM entry on unpkg -->
<script type="module" src="https://unpkg.com/@stoplight/elements/web-components.min.js?module"></script>
<style>
body { margin:0; font-family: Inter, system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial; }
header { padding:12px 20px; background:#0b1220; color:#e6eef8; display:flex; align-items:center; gap:12px }
header h1 { margin:0; font-size:16px }
main { height: calc(100vh - 56px); }
</style>
</head>
<body>
<header>
<img src="https://raw.githubusercontent.com/stoplightio/elements/main/docs/images/logo.svg" alt="stoplight" style="height:28px"/>
<h1>Interactive API Docs</h1>
</header>
<main>
<!-- elements-api is the web component that renders an API description URL -->
<elements-api api-description-url="/docs/json" router="hash"></elements-api>
<noscript>
<div style="padding:20px;color:#374151;background:#f8fafc;border-radius:6px;margin:16px">JavaScript is required to render interactive docs. You can view the OpenAPI JSON directly: <a href="/docs/json">/docs/json</a></div>
</noscript>
<script>
// If the web component failed to register (CDN blocked), show a fallback notice after a short timeout
setTimeout(() => {
if (!customElements.get('elements-api')) {
const fallback = document.createElement('div');
fallback.style.padding = '16px';
fallback.style.background = '#fff3cd';
fallback.style.color = '#6b4226';
fallback.style.borderRadius = '6px';
fallback.style.margin = '16px';
fallback.innerHTML = 'Interactive docs failed to load from CDN. You can view the OpenAPI JSON directly: <a href="/docs/json">/docs/json</a>';
document.querySelector('main').appendChild(fallback);
}
}, 1200);
var apiReference = document.getElementById('api-reference')
apiReference.dataset.configuration = JSON.stringify(configuration)
</script>
</main>
<script src="https://cdn.jsdelivr.net/npm/@scalar/api-reference"></script>
</body>
</html>
`)
@ -242,7 +178,6 @@ async function bootstrap() {
app.log.error(error)
reply.status(500).send({ error: 'Internal Server Error' })
})
return app
}