123 lines
3.7 KiB
TypeScript
123 lines
3.7 KiB
TypeScript
import { promises as fs } from 'fs';
|
|
import * as path from 'path';
|
|
|
|
function getScalarBundleCandidates(): string[] {
|
|
const processWithResources = process as NodeJS.Process & { resourcesPath?: string };
|
|
const candidates: string[] = [];
|
|
|
|
if (processWithResources.resourcesPath) {
|
|
candidates.push(path.join(processWithResources.resourcesPath, 'scalar', 'api-reference.js'));
|
|
}
|
|
|
|
candidates.push(path.join(process.cwd(), 'node_modules', '@scalar', 'api-reference', 'dist', 'browser', 'standalone.js'));
|
|
|
|
try {
|
|
candidates.push(path.join(path.dirname(require.resolve('@scalar/api-reference')), 'browser', 'standalone.js'));
|
|
} catch {
|
|
// ignore; the packaged app path above is the production path
|
|
}
|
|
|
|
return candidates;
|
|
}
|
|
|
|
export async function getScalarApiReferenceBundlePath(): Promise<string | null> {
|
|
for (const candidate of getScalarBundleCandidates()) {
|
|
try {
|
|
await fs.access(candidate);
|
|
return candidate;
|
|
} catch {
|
|
// try the next candidate
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
export function getDocsHtml(specUrl: string): string {
|
|
const scalarConfig = {
|
|
url: specUrl,
|
|
theme: 'default',
|
|
layout: 'modern',
|
|
proxyUrl: '',
|
|
telemetry: false,
|
|
persistAuth: false,
|
|
showDeveloperTools: 'never',
|
|
hideDownloadButton: false,
|
|
hideTestRequestButton: false,
|
|
hideClientButton: false,
|
|
externalUrls: {
|
|
dashboardUrl: '',
|
|
registryUrl: '',
|
|
proxyUrl: '',
|
|
apiBaseUrl: ''
|
|
},
|
|
agent: {
|
|
disabled: true,
|
|
hideAddApi: true
|
|
},
|
|
mcp: {
|
|
disabled: true
|
|
}
|
|
};
|
|
|
|
return `<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<meta
|
|
http-equiv="Content-Security-Policy"
|
|
content="default-src 'none'; script-src 'self' 'nonce-metoyou-local-api-docs'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"
|
|
/>
|
|
<title>MetoYou Local API</title>
|
|
<style>
|
|
:root { color-scheme: light dark; }
|
|
body {
|
|
margin: 0;
|
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
|
background: #0b0d11;
|
|
color: #e6e9ee;
|
|
}
|
|
.placeholder {
|
|
max-width: 720px;
|
|
margin: 8vh auto;
|
|
padding: 2rem;
|
|
background: #14181f;
|
|
border: 1px solid #232a35;
|
|
border-radius: 12px;
|
|
}
|
|
h1 { margin-top: 0; }
|
|
a { color: #7aa2f7; }
|
|
code { background: #1f262f; padding: 0.1rem 0.4rem; border-radius: 4px; }
|
|
#api-reference { min-height: 100vh; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div id="api-reference"></div>
|
|
<noscript>
|
|
<div class="placeholder">
|
|
<h1>API Documentation</h1>
|
|
<p>JavaScript is required to render Scalar. The OpenAPI specification is available directly:</p>
|
|
<p><a href="${specUrl}">${specUrl}</a></p>
|
|
</div>
|
|
</noscript>
|
|
<script nonce="metoyou-local-api-docs" src="/scalar/api-reference.js"></script>
|
|
<script nonce="metoyou-local-api-docs">
|
|
(function () {
|
|
var config = ${JSON.stringify(scalarConfig)};
|
|
|
|
if (!window.Scalar || typeof window.Scalar.createApiReference !== 'function') {
|
|
var root = document.getElementById('api-reference');
|
|
root.innerHTML = '<div class="placeholder"><h1>API Documentation</h1>'
|
|
+ '<p>The bundled Scalar UI could not be loaded.</p>'
|
|
+ '<p>Spec: <a href="' + config.url + '">' + config.url + '</a></p></div>';
|
|
return;
|
|
}
|
|
|
|
window.Scalar.createApiReference('#api-reference', config);
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>`;
|
|
}
|