docfast/src/swagger.ts
OpenClaw Subagent 70eb6908e3
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Has been cancelled
Document rate limit headers in OpenAPI spec
- Add reusable header components (X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, Retry-After)
- Reference headers in 200 responses on all conversion and demo endpoints
- Add Retry-After header to 429 responses
- Update Rate Limits section in API description to mention response headers
- Add comprehensive tests for header documentation (21 new tests)
- All 809 tests passing
2026-03-18 11:06:22 +01:00

169 lines
6.8 KiB
TypeScript

import swaggerJsdoc from "swagger-jsdoc";
import { createRequire } from "module";
const _require = createRequire(import.meta.url);
const { version } = _require("../package.json");
const options: swaggerJsdoc.Options = {
definition: {
openapi: "3.0.3",
info: {
title: "DocFast API",
version,
description:
"Convert HTML, Markdown, and URLs to pixel-perfect PDFs. Built-in invoice & receipt templates.\n\n## Authentication\nAll conversion endpoints require an API key via `Authorization: Bearer <key>` or `X-API-Key: <key>` header. Get your key at [docfast.dev](https://docfast.dev).\n\n## Rate Limits\n- Demo: 5 conversions/hour, watermarked output\n- Pro tier: 5,000 PDFs/month, 30 req/min\n\nAll rate-limited endpoints return `X-RateLimit-Limit`, `X-RateLimit-Remaining`, and `X-RateLimit-Reset` headers. On `429`, a `Retry-After` header indicates seconds until the next allowed request.\n\n## Getting Started\n1. Try the demo endpoints (no auth required, 5/hour limit)\n2. Upgrade to Pro at [docfast.dev](https://docfast.dev) for clean output and higher limits\n3. Use your API key to convert documents",
contact: {
name: "DocFast",
url: "https://docfast.dev",
email: "support@docfast.dev",
},
},
servers: [{ url: "https://docfast.dev", description: "Production" }],
tags: [
{ name: "Demo", description: "Try the API without authentication (watermarked, 5/hour)" },
{ name: "Conversion", description: "Convert HTML, Markdown, or URLs to PDF" },
{ name: "Templates", description: "Built-in document templates" },
{ name: "Account", description: "Signup and key recovery" },
{ name: "Billing", description: "Stripe-powered subscription management" },
{ name: "System", description: "Health checks and usage stats" },
],
components: {
securitySchemes: {
BearerAuth: {
type: "http",
scheme: "bearer",
description: "API key as Bearer token",
},
ApiKeyHeader: {
type: "apiKey",
in: "header",
name: "X-API-Key",
description: "API key via X-API-Key header",
},
},
headers: {
"X-RateLimit-Limit": {
description: "The maximum number of requests allowed in the current time window",
schema: {
type: "integer",
example: 30,
},
},
"X-RateLimit-Remaining": {
description: "The number of requests remaining in the current time window",
schema: {
type: "integer",
example: 29,
},
},
"X-RateLimit-Reset": {
description: "Unix timestamp (seconds since epoch) when the rate limit window resets",
schema: {
type: "integer",
example: 1679875200,
},
},
"Retry-After": {
description: "Number of seconds to wait before retrying the request (returned on 429 responses)",
schema: {
type: "integer",
example: 60,
},
},
},
schemas: {
PdfOptions: {
type: "object",
properties: {
format: {
type: "string",
enum: ["Letter", "Legal", "Tabloid", "Ledger", "A0", "A1", "A2", "A3", "A4", "A5", "A6"],
default: "A4",
description: "Page size. Ignored if width/height are set.",
},
landscape: {
type: "boolean",
default: false,
description: "Landscape orientation",
},
margin: {
type: "object",
description: "Page margins. Accepts CSS units (e.g. '20mm', '1in', '72px').",
properties: {
top: { type: "string", example: "20mm" },
bottom: { type: "string", example: "20mm" },
left: { type: "string", example: "15mm" },
right: { type: "string", example: "15mm" },
},
},
printBackground: {
type: "boolean",
default: true,
description: "Print background graphics and colors",
},
filename: {
type: "string",
default: "document.pdf",
description: "Suggested filename for the PDF download",
},
waitUntil: {
type: "string",
enum: ["load", "domcontentloaded", "networkidle0", "networkidle2"],
description: "Wait condition for page rendering before PDF generation.",
},
headerTemplate: {
type: "string",
description: "HTML template for the page header. Requires displayHeaderFooter: true. Use these CSS classes for dynamic values: date, title, url, pageNumber, totalPages. Maximum size: 100KB. Example: '<span class=\"pageNumber\"></span> / <span class=\"totalPages\"></span>'",
},
footerTemplate: {
type: "string",
description: "HTML template for the page footer. Requires displayHeaderFooter: true. Supports the same CSS classes as headerTemplate. Maximum size: 100KB.",
},
displayHeaderFooter: {
type: "boolean",
default: false,
description: "Whether to show header and footer templates. Must be true for headerTemplate/footerTemplate to render.",
},
scale: {
type: "number",
minimum: 0.1,
maximum: 2,
default: 1,
description: "Scale of the webpage rendering. 1 = 100%, 0.5 = 50%, 2 = 200%.",
example: 1,
},
pageRanges: {
type: "string",
description: "Paper ranges to print, e.g. '1-5', '1,3,5', '2-4,6'. Empty string means all pages.",
example: "1-3",
},
preferCSSPageSize: {
type: "boolean",
default: false,
description: "Give any CSS @page size declared in the page priority over the format option.",
},
width: {
type: "string",
description: "Paper width with units. Overrides format. Accepts CSS units (e.g. '10in', '210mm', '8.5in').",
example: "8.5in",
},
height: {
type: "string",
description: "Paper height with units. Overrides format. Accepts CSS units (e.g. '11in', '297mm').",
example: "11in",
},
},
},
Error: {
type: "object",
properties: {
error: { type: "string", description: "Error message" },
},
},
},
},
},
apis: ["./dist/routes/*.js", "./dist/index.js"],
};
export const swaggerSpec = swaggerJsdoc(options);