fix: code-driven OpenAPI docs — replace static JSON with swagger-jsdoc
Some checks failed
Deploy to Staging / build-and-deploy (push) Failing after 10m13s

BREAKING: OpenAPI spec is now generated from JSDoc annotations on route
handlers at startup, eliminating drift between code and documentation.

What was wrong:
- Static public/openapi.json was manually maintained and could drift
- Missing endpoints: signup, billing (checkout/success/webhook)
- Signup route was imported but never mounted (dead code)

What was fixed:
- Added swagger-jsdoc to generate OpenAPI spec from JSDoc on route files
- Every route handler now has @openapi JSDoc annotation as source of truth
- Spec served dynamically at GET /openapi.json (no static file)
- Deleted public/openapi.json
- Documented all missing endpoints (signup, billing x3)
- Mounted /v1/signup route
- All 9 screenshot params documented with types, ranges, defaults
This commit is contained in:
SnapAPI CEO 2026-02-20 07:32:37 +00:00
parent a70157d0ae
commit 713cc30ac7
10 changed files with 700 additions and 124 deletions

View file

@ -4,6 +4,123 @@ import logger from "../services/logger.js";
export const screenshotRouter = Router();
/**
* @openapi
* /v1/screenshot:
* post:
* tags: [Screenshots]
* summary: Take a screenshot (authenticated)
* description: Capture a pixel-perfect, unwatermarked screenshot. Requires an API key.
* operationId: takeScreenshot
* security:
* - BearerAuth: []
* - ApiKeyAuth: []
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* required: [url]
* properties:
* url:
* type: string
* format: uri
* description: URL to capture
* example: "https://example.com"
* format:
* type: string
* enum: [png, jpeg, webp]
* default: png
* description: Output image format
* width:
* type: integer
* minimum: 320
* maximum: 3840
* default: 1280
* description: Viewport width in pixels
* height:
* type: integer
* minimum: 200
* maximum: 2160
* default: 800
* description: Viewport height in pixels
* fullPage:
* type: boolean
* default: false
* description: Capture full scrollable page instead of viewport only
* quality:
* type: integer
* minimum: 1
* maximum: 100
* default: 80
* description: JPEG/WebP quality (ignored for PNG)
* waitForSelector:
* type: string
* description: CSS selector to wait for before capturing (e.g. "#main-content")
* deviceScale:
* type: number
* minimum: 1
* maximum: 3
* default: 1
* description: Device scale factor (2 = Retina)
* delay:
* type: integer
* minimum: 0
* maximum: 5000
* default: 0
* description: Extra delay in ms after page load before capturing
* examples:
* simple:
* summary: Simple screenshot
* value: { "url": "https://example.com" }
* hd_jpeg:
* summary: HD JPEG
* value: { "url": "https://github.com", "format": "jpeg", "width": 1920, "height": 1080, "quality": 90 }
* mobile:
* summary: Mobile viewport
* value: { "url": "https://example.com", "width": 375, "height": 812, "deviceScale": 2 }
* responses:
* 200:
* description: Screenshot image binary
* content:
* image/png:
* schema: { type: string, format: binary }
* image/jpeg:
* schema: { type: string, format: binary }
* image/webp:
* schema: { type: string, format: binary }
* 400:
* description: Invalid request (bad URL, blocked domain, etc.)
* content:
* application/json:
* schema: { $ref: "#/components/schemas/Error" }
* 401:
* description: Missing API key
* content:
* application/json:
* schema: { $ref: "#/components/schemas/Error" }
* 403:
* description: Invalid API key
* content:
* application/json:
* schema: { $ref: "#/components/schemas/Error" }
* 429:
* description: Rate or usage limit exceeded
* content:
* application/json:
* schema: { $ref: "#/components/schemas/Error" }
* 503:
* description: Service busy (queue full)
* content:
* application/json:
* schema: { $ref: "#/components/schemas/Error" }
* 504:
* description: Screenshot timed out
* content:
* application/json:
* schema: { $ref: "#/components/schemas/Error" }
*/
screenshotRouter.post("/", async (req: any, res) => {
const { url, format, width, height, fullPage, quality, waitForSelector, deviceScale, delay } = req.body;