docfast/scripts/generate-openapi.mjs
OpenClaw Subagent f0cb83a901
Some checks failed
Build & Deploy to Staging / Build & Deploy to Staging (push) Failing after 2m11s
Add rate limit headers to OpenAPI generation script
- Update generate-openapi.mjs to include header components
- Ensure public/openapi.json has rate limit headers
- Update rate limits description in generation script
2026-03-18 11:08:05 +01:00

155 lines
5.2 KiB
JavaScript

#!/usr/bin/env node
/**
* Generates openapi.json from JSDoc annotations in route files.
* Run: node scripts/generate-openapi.mjs
* Output: public/openapi.json
*/
import swaggerJsdoc from 'swagger-jsdoc';
import { writeFileSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
const __dirname = dirname(fileURLToPath(import.meta.url));
const options = {
definition: {
openapi: '3.0.3',
info: {
title: 'DocFast API',
version: '1.0.0',
description: `Convert HTML, Markdown, and URLs to pixel-perfect PDFs. Built-in invoice & receipt templates.
## Authentication
All conversion and template endpoints require an API key via \`Authorization: Bearer <key>\` or \`X-API-Key: <key>\` header.
## Demo Endpoints
Try the API without signing up! Demo endpoints are public (no API key needed) but rate-limited to 5 requests/hour per IP and produce watermarked PDFs.
## Rate Limits
- Demo: 5 PDFs/hour per IP (watermarked)
- Pro tier: 5,000 PDFs/month, 30 req/min
All 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.
## Getting Started
1. Try the demo at \`POST /v1/demo/html\` — no signup needed
2. Subscribe to Pro at [docfast.dev](https://docfast.dev/#pricing) for clean PDFs
3. 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 signing up — watermarked PDFs, rate-limited' },
{ name: 'Conversion', description: 'Convert HTML, Markdown, or URLs to PDF (requires API key)' },
{ name: 'Templates', description: 'Built-in document templates' },
{ name: 'Account', description: 'Key recovery and email management' },
{ 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: ['A4', 'Letter', 'Legal', 'A3', 'A5', 'Tabloid'],
default: 'A4',
description: 'Page size'
},
landscape: {
type: 'boolean',
default: false,
description: 'Landscape orientation'
},
margin: {
type: 'object',
properties: {
top: { type: 'string', description: 'Top margin (e.g. "10mm", "1in")', default: '0' },
right: { type: 'string', description: 'Right margin', default: '0' },
bottom: { type: 'string', description: 'Bottom margin', default: '0' },
left: { type: 'string', description: 'Left margin', default: '0' }
},
description: 'Page margins'
},
printBackground: {
type: 'boolean',
default: true,
description: 'Print background colors and images'
},
filename: {
type: 'string',
description: 'Custom filename for Content-Disposition header',
default: 'document.pdf'
}
}
},
Error: {
type: 'object',
properties: {
error: { type: 'string', description: 'Error message' }
},
required: ['error']
}
}
}
},
apis: [
join(__dirname, '../src/routes/*.ts'),
join(__dirname, '../src/openapi-extra.yaml')
]
};
const spec = swaggerJsdoc(options);
const outPath = join(__dirname, '../public/openapi.json');
writeFileSync(outPath, JSON.stringify(spec, null, 2));
console.log(`✅ Generated ${outPath} (${Object.keys(spec.paths || {}).length} paths)`);