This commit is contained in:
Stan
2026-04-19 21:14:16 +02:00
parent 0c74a75126
commit 28d167f11f
42 changed files with 5681 additions and 55 deletions
+99 -4
View File
@@ -2,37 +2,100 @@ import { Router } from 'express';
import {
getActiveTemplate,
getAllActiveTemplates,
getTemplateVersion,
listTemplates
listTemplates,
listTemplateVersions,
publishTemplateVersion
} from '../services/templateService.js';
import { logAuditEvent } from '../services/auditService.js';
import { asyncHandler } from '../utils/asyncHandler.js';
import { validateParam, validateNumericParam } from '../middleware/validateParams.js';
import { templateCache } from '../services/cacheService.js';
const router = Router();
router.get(
'/',
asyncHandler(async (_req, res) => {
asyncHandler(async (req, res) => {
/*
* When ?include=definitions is set the response embeds the full JSON
* definition for every active template. This eliminates the N+1 round-trip
* the old client performed (list → fetch each) and makes initial sync a
* single request. Without the flag the response stays lightweight.
*/
const includeDefinitions = req.query.include === 'definitions';
const cacheKey = `templates-list-${includeDefinitions}`;
const cached = templateCache.get(cacheKey);
if (cached) {
return res.json(cached);
}
if (includeDefinitions) {
const templates = await getAllActiveTemplates();
const payload = { items: templates };
templateCache.set(cacheKey, payload);
return res.json(payload);
}
const templates = await listTemplates();
res.json({ items: templates });
const payload = { items: templates };
templateCache.set(cacheKey, payload);
return res.json(payload);
})
);
router.get(
'/:templateCode',
validateParam('templateCode'),
asyncHandler(async (req, res) => {
/*
* New reports always use the latest active template, so the primary route is
* optimized for that case. Older versions remain accessible through the
* versioned route so existing drafts can stay bound to the original schema.
*/
const cacheKey = `template-active-${req.params.templateCode}`;
const cached = templateCache.get(cacheKey);
if (cached) {
return res.json(cached);
}
const template = await getActiveTemplate(req.params.templateCode);
if (!template) {
return res.status(404).json({ message: 'Template not found.' });
}
templateCache.set(cacheKey, template);
return res.json(template);
})
);
router.get(
'/:templateCode/versions/:versionNumber',
'/:templateCode/versions',
validateParam('templateCode'),
asyncHandler(async (req, res) => {
/*
* Version listing lets the admin workspace display a template's publication
* history and choose which version to activate or review.
*/
const versions = await listTemplateVersions(req.params.templateCode);
return res.json({ items: versions });
})
);
router.get(
'/:templateCode/versions/:versionNumber',
validateParam('templateCode'),
validateNumericParam('versionNumber'),
asyncHandler(async (req, res) => {
/*
* Version-specific access is what allows the frontend to reopen old drafts
* safely even after templates evolve. Without this route, cached reports
* would eventually drift away from the structure they were created against.
*/
const template = await getTemplateVersion(
req.params.templateCode,
req.params.versionNumber
@@ -46,4 +109,36 @@ router.get(
})
);
router.put(
'/:templateCode/versions/:versionNumber/publish',
validateParam('templateCode'),
validateNumericParam('versionNumber'),
asyncHandler(async (req, res) => {
/*
* Publishing a version marks it active and retires the previously active
* version for the same template. This lets the admin promote a draft version
* to production. Existing reports keep their bound version unchanged.
*/
const result = await publishTemplateVersion(
req.params.templateCode,
Number(req.params.versionNumber)
);
if (!result) {
return res.status(404).json({ message: 'Template version not found.' });
}
templateCache.clear();
await logAuditEvent({
entityType: 'template_version',
entityCode: `${req.params.templateCode}::v${req.params.versionNumber}`,
action: 'publish',
newValue: { templateCode: req.params.templateCode, version: Number(req.params.versionNumber) }
});
return res.json(result);
})
);
export default router;