106 lines
3.7 KiB
JavaScript
106 lines
3.7 KiB
JavaScript
/**
|
|
* OpenAPI spec generation via swagger-jsdoc + docs UI (swagger-stripey).
|
|
*
|
|
* swagger-jsdoc scans JSDoc `@openapi` comments on route handlers in server.js
|
|
* (and any file passed in `apis`) to build the spec at startup.
|
|
* Docs UI lives in docs/api.html (swagger-stripey: Stripe-style 3-panel renderer).
|
|
*
|
|
* Usage:
|
|
* import { mountDocs } from './lib/openapi.js';
|
|
* // After all routes are registered:
|
|
* mountDocs(app);
|
|
*/
|
|
|
|
import swaggerJsdoc from 'swagger-jsdoc';
|
|
import express from 'express';
|
|
import { readFileSync } from 'fs';
|
|
import { dirname, join } from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
|
let version = 'unknown';
|
|
try {
|
|
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
version = pkg.version;
|
|
} catch { /* ignore */ }
|
|
|
|
const swaggerDefinition = {
|
|
openapi: '3.0.3',
|
|
info: {
|
|
title: 'camofox-browser',
|
|
version,
|
|
description:
|
|
'Anti-detection browser automation server for AI agents. ' +
|
|
'Accessibility snapshots, element refs, session isolation, cookie import, proxy rotation, and structured logs.',
|
|
license: { name: 'MIT', url: 'https://opensource.org/licenses/MIT' },
|
|
contact: { name: 'Jo Inc', url: 'https://askjo.ai', email: 'oss@askjo.ai' },
|
|
},
|
|
servers: [{ url: 'http://localhost:9377', description: 'Local development' }],
|
|
tags: [
|
|
{ name: 'System', description: 'Server health, metrics, and status.' },
|
|
{ name: 'Tabs', description: 'Create, list, inspect, and destroy browser tabs.' },
|
|
{ name: 'Navigation', description: 'Navigate tabs to URLs or via search macros.' },
|
|
{ name: 'Interaction', description: 'Click, type, scroll, press keys, evaluate JS.' },
|
|
{ name: 'Content', description: 'Accessibility snapshots, screenshots, links, images, downloads.' },
|
|
{ name: 'Sessions', description: 'Per-user session state: cookies, teardown.' },
|
|
{ name: 'Browser', description: 'Global browser lifecycle (start/stop).' },
|
|
{ name: 'Legacy', description: 'OpenClaw-compatible endpoints (deprecated).' },
|
|
],
|
|
components: {
|
|
securitySchemes: {
|
|
BearerAuth: {
|
|
type: 'http',
|
|
scheme: 'bearer',
|
|
description: 'Bearer token matching CAMOFOX_API_KEY (per-route auth for sensitive endpoints like cookie import and traces).',
|
|
},
|
|
AccessKeyAuth: {
|
|
type: 'http',
|
|
scheme: 'bearer',
|
|
description: 'Bearer token matching CAMOFOX_ACCESS_KEY. When set, gates all routes except /health, cookie import, and /stop. Acts as a superkey -- also accepted by endpoints that normally require CAMOFOX_API_KEY.',
|
|
},
|
|
},
|
|
schemas: {
|
|
Error: {
|
|
type: 'object',
|
|
required: ['error'],
|
|
properties: { error: { type: 'string' } },
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
/**
|
|
* Mount GET /openapi.json and GET /docs on the Express app.
|
|
* Call AFTER all routes are registered so swagger-jsdoc can scan them.
|
|
*
|
|
* @param {import('express').Application} app
|
|
* @param {Object} [opts]
|
|
* @param {string[]} [opts.apis] - Glob patterns for files with @openapi JSDoc (default: ['./server.js'])
|
|
*/
|
|
export function mountDocs(app, opts = {}) {
|
|
const apis = opts.apis || ['./server.js'];
|
|
|
|
const spec = swaggerJsdoc({
|
|
definition: swaggerDefinition,
|
|
apis,
|
|
});
|
|
|
|
app.get('/openapi.json', (_req, res) => {
|
|
res.json(spec);
|
|
});
|
|
|
|
// Serve docs static assets (api.html, fox.png, openapi.json)
|
|
const docsDir = join(__dirname, '..', 'docs');
|
|
app.use('/docs', express.static(docsDir, { index: 'api.html' }));
|
|
|
|
// Also serve fox.png at root for backward compat with old Swagger UI HTML
|
|
app.get('/fox.png', (_req, res) => {
|
|
res.sendFile(join(docsDir, 'fox.png'));
|
|
});
|
|
|
|
return spec;
|
|
}
|
|
|
|
export { swaggerDefinition };
|