docfast/dist/routes/templates.js
OpenClaw 8b31d11e74
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 16m15s
docs: add missing OpenAPI annotations for signup/verify, billing/success, billing/webhook
2026-02-27 16:04:55 +00:00

163 lines
5.6 KiB
JavaScript

import { Router } from "express";
import { renderPdf } from "../services/browser.js";
import logger from "../services/logger.js";
import { templates, renderTemplate } from "../services/templates.js";
import { sanitizeFilename } from "../utils/sanitize.js";
export const templatesRouter = Router();
/**
* @openapi
* /v1/templates:
* get:
* tags: [Templates]
* summary: List available templates
* description: Returns a list of all built-in document templates with their required fields.
* security:
* - BearerAuth: []
* - ApiKeyHeader: []
* responses:
* 200:
* description: List of templates
* content:
* application/json:
* schema:
* type: object
* properties:
* templates:
* type: array
* items:
* type: object
* properties:
* id:
* type: string
* example: invoice
* name:
* type: string
* example: Invoice
* description:
* type: string
* fields:
* type: array
* items:
* type: object
* properties:
* name:
* type: string
* required:
* type: boolean
* description:
* type: string
* 401:
* description: Missing API key
* 403:
* description: Invalid API key
*/
templatesRouter.get("/", (_req, res) => {
const list = Object.entries(templates).map(([id, t]) => ({
id,
name: t.name,
description: t.description,
fields: t.fields,
}));
res.json({ templates: list });
});
/**
* @openapi
* /v1/templates/{id}/render:
* post:
* tags: [Templates]
* summary: Render a template to PDF
* description: |
* Renders a built-in template with the provided data and returns a PDF.
* Use GET /v1/templates to see available templates and their required fields.
* Special fields: `_format` (page size), `_margin` (page margins), `_filename` (output filename).
* security:
* - BearerAuth: []
* - ApiKeyHeader: []
* parameters:
* - in: path
* name: id
* required: true
* schema:
* type: string
* description: Template ID (e.g. "invoice", "receipt")
* requestBody:
* required: true
* content:
* application/json:
* schema:
* type: object
* properties:
* data:
* type: object
* description: Template data (fields depend on template). Can also be passed at root level.
* _format:
* type: string
* enum: [A4, Letter, Legal, A3, A5, Tabloid]
* default: A4
* description: Page size override
* _margin:
* type: object
* properties:
* top: { type: string }
* right: { type: string }
* bottom: { type: string }
* left: { type: string }
* description: Page margin override
* _filename:
* type: string
* description: Custom output filename
* responses:
* 200:
* description: PDF document
* content:
* application/pdf:
* schema:
* type: string
* format: binary
* 400:
* description: Missing required template fields
* 401:
* description: Missing API key
* 403:
* description: Invalid API key
* 404:
* description: Template not found
* 500:
* description: Template rendering failed
*/
templatesRouter.post("/:id/render", async (req, res) => {
try {
const id = req.params.id;
const template = templates[id];
if (!template) {
res.status(404).json({ error: `Template '${id}' not found` });
return;
}
const data = req.body.data || req.body;
// Validate required fields
const missingFields = template.fields
.filter((f) => f.required && (data[f.name] === undefined || data[f.name] === null || data[f.name] === ""))
.map((f) => f.name);
if (missingFields.length > 0) {
res.status(400).json({
error: "Missing required fields",
missing: missingFields,
hint: `Required fields for '${id}': ${template.fields.filter((f) => f.required).map((f) => f.name).join(", ")}`,
});
return;
}
const html = renderTemplate(id, data);
const pdf = await renderPdf(html, {
format: data._format || "A4",
margin: data._margin,
});
const filename = sanitizeFilename(data._filename || `${id}.pdf`);
res.setHeader("Content-Type", "application/pdf");
res.setHeader("Content-Disposition", `inline; filename="${filename}"`);
res.send(pdf);
}
catch (err) {
logger.error({ err }, "Template render error");
res.status(500).json({ error: "Template rendering failed", detail: err.message });
}
});